Compare commits

...

147 Commits

Author SHA1 Message Date
Applevangelist aaf77815ca #SRS
* Added MSRSQUEUE message queue
2022-08-25 10:50:45 +02:00
Applevangelist 3f488cc091 #AIRBASE
* Added 3 new airports to the enumerator SouthAtlantic
2022-08-25 10:38:44 +02:00
Applevangelist bdbbdfe60e #RANGE
* Added Instructor and RangeControl radio info on F10
2022-08-23 09:58:50 +02:00
Applevangelist d15c2be2d0 Range fixes (#1764)
* Fixed some typos in self.PlayerSettings
* Fixed result messages
* Thanks for [JFG] Caponi
2022-08-22 17:03:33 +02:00
Applevangelist 73994914eb Fix for #1763 UTILS.GetOSTime() (#1765)
* Fix GetOSTime fixes #1763
2022-08-22 17:03:21 +02:00
Applevangelist 63b0dae794 Update ATC_Ground.lua (#1766)
* Make ATC_Ground work on any map
2022-08-22 17:03:02 +02:00
Engines 003e865ff7 Late break bug (#1762)
* Quick update to Airboss.lua to inluce Invincible.  Not yet tested.

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

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

* Removing surpurflous Moose.lua for pull request.

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

* Removing the test Moose.lua (again).

* Fixing merge confilict

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

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

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

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

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

Remove duplicate function

* Update PseudoATC.lua

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

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

Scaley

* Create AI_Air.lua

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

* Fix syntax error

C
2022-04-08 07:16:23 +02:00
Frank 7f5be2829c Update Database.lua
- Template statics saved under first statics unit name so they can be found.
2022-04-04 16:50:17 +02:00
Applevangelist fa5afae783 A2A Dispatcher - nil checks to evade dead units 2022-04-04 12:58:32 +02:00
Frank b17507d0fa Update SpawnStatic.lua
- Fixed isCargo flag not honored.
2022-04-04 12:18:45 +02:00
Applevangelist 5ed43a3190 CTLD - build object at the 1st crate location not randomly around helo 2022-04-04 11:31:35 +02:00
Applevangelist d2a5144a23 AIRBASE, added ["Deir_ez-Zor"] = "Deir ez-Zor", 2022-03-30 12:06:25 +02:00
Applevangelist fb2031d7ca docu nicefy 2022-03-29 12:01:28 +02:00
Applevangelist 4a42571925 CTLD - corrected error in setting Hercules min and max drop height, added documentation 2022-03-29 08:52:33 +02:00
Applevangelist 38413625c2 AIRBASE - Add'l AB for the Channels map 2022-03-28 10:12:39 +02:00
Frank 2d544b7a98 Merge pull request #1698 from gavinedwards/airboss.hermes.include
Added Hermes section to Airboss.  Will require tuning.
2022-03-26 23:34:54 +01:00
Lt Cdr Gavin Edwards b1e5e1840e Adding leading lines that I accidentally truncated. 2022-03-26 15:23:38 -07:00
Lt Cdr Gavin Edwards e6f9b4a125 Added Hermes section to Airboss. Will require tuning. 2022-03-26 11:54:53 -07:00
Applevangelist 5260b2b430 And don't forget Fahrenheit 2022-03-26 14:46:52 +01:00
Applevangelist 0213bc7aef Correcting Celcius to Celsius 2022-03-26 14:43:06 +01:00
Applevangelist ca8b0899d0 docu changes 2022-03-23 07:56:52 +01:00
Applevangelist a1f5c0ab9b CSAR/CTLD - added type to script 2022-03-22 10:38:20 +01:00
Applevangelist b0e3f82d27 AIRBASE - added 10 new AB names in Syria 2022-03-18 09:48:50 +01:00
Applevangelist 327ab4766b changed descriptions 2022-03-18 07:59:58 +01:00
Applevangelist 3aee8a49c1 Added CONTROLLABLE SetSpeed() and SetAltitude() 2022-03-18 07:39:48 +01:00
Applevangelist 57de0b7351 docu fixes 2022-03-16 08:45:27 +01:00
Applevangelist 4df1e310a3 CSAR - remove timer check for "open the door" message to make behaviour more realistic 2022-03-14 09:12:24 +01:00
TommyC81 802a77238a Range re-formatting and documentation re-fixing (#1691)
* Update Range.lua

Code formatting.

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

Minor code formatting.

* Update Airbase.lua

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

Code formatting and minor typo/document fixes.

* Update Marker.lua

Code formatting and minor typo/document fixes. Note specifically the correction of "self.tocoaliton" to "self.tocoalition".
2021-12-28 11:01:05 +01:00
Applevangelist f62e3391e1 SCORING - make treason and fratricide switchable 2021-12-28 08:25:31 +01:00
66 changed files with 14993 additions and 12811 deletions
+10
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
View File
Binary file not shown.
+3 -3
View File
@@ -40,8 +40,8 @@
-- --
-- ![Process](..\Presentations\AI_CAP\Dia10.JPG) -- ![Process](..\Presentations\AI_CAP\Dia10.JPG)
-- --
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. -- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land. -- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
-- --
-- ![Process](..\Presentations\AI_CAP\Dia13.JPG) -- ![Process](..\Presentations\AI_CAP\Dia13.JPG)
-- --
@@ -71,7 +71,7 @@
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets. -- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
-- * **@{#AI_A2A_CAP.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}. -- * **@{#AI_A2A_CAP.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}.
-- * **@{#AI_A2A_CAP.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task. -- * **@{#AI_A2A_CAP.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task.
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. -- * **Status** ( Group ): The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
-- --
-- ## 3. Set the Range of Engagement -- ## 3. Set the Range of Engagement
-- --
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -42,8 +42,8 @@
-- --
-- ![Process](..\Presentations\AI_GCI\Dia10.JPG) -- ![Process](..\Presentations\AI_GCI\Dia10.JPG)
-- --
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. -- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land. -- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
-- --
-- ![Process](..\Presentations\AI_GCI\Dia13.JPG) -- ![Process](..\Presentations\AI_GCI\Dia13.JPG)
-- --
@@ -73,7 +73,7 @@
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets. -- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
-- * **@{#AI_A2A_GCI.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}. -- * **@{#AI_A2A_GCI.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}.
-- * **@{#AI_A2A_GCI.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task. -- * **@{#AI_A2A_GCI.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task.
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. -- * **Status** ( Group ): The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
-- --
-- ## 3. Set the Range of Engagement -- ## 3. Set the Range of Engagement
-- --
+6 -6
View File
@@ -39,8 +39,8 @@
-- --
-- ![Process](..\Presentations\AI_PATROL\Dia10.JPG) -- ![Process](..\Presentations\AI_PATROL\Dia10.JPG)
-- --
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. -- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land. -- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
-- --
-- ![Process](..\Presentations\AI_PATROL\Dia11.JPG) -- ![Process](..\Presentations\AI_PATROL\Dia11.JPG)
-- --
@@ -68,7 +68,7 @@
-- * **RTB** ( Group ): Route the AI to the home base. -- * **RTB** ( Group ): Route the AI to the home base.
-- * **Detect** ( Group ): The AI is detecting targets. -- * **Detect** ( Group ): The AI is detecting targets.
-- * **Detected** ( Group ): The AI has detected new targets. -- * **Detected** ( Group ): The AI has detected new targets.
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. -- * **Status** ( Group ): The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
-- --
-- ## 3. Set or Get the AI controllable -- ## 3. Set or Get the AI controllable
-- --
@@ -100,8 +100,8 @@
-- ## 6. Manage the "out of fuel" in the AI_A2A_PATROL -- ## 6. Manage the "out of fuel" in the AI_A2A_PATROL
-- --
-- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base. -- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base.
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel treshold is calculated. -- Therefore, with a parameter and a calculation of the distance to the home base, the fuel threshold is calculated.
-- When the fuel treshold is reached, the AI will continue for a given time its patrol task in orbit, -- When the fuel threshold is reached, the AI will continue for a given time its patrol task in orbit,
-- while a new AI is targetted to the AI_A2A_PATROL. -- while a new AI is targetted to the AI_A2A_PATROL.
-- Once the time is finished, the old AI will return to the base. -- Once the time is finished, the old AI will return to the base.
-- Use the method @{#AI_A2A_PATROL.ManageFuel}() to have this proces in place. -- Use the method @{#AI_A2A_PATROL.ManageFuel}() to have this proces in place.
@@ -109,7 +109,7 @@
-- ## 7. Manage "damage" behaviour of the AI in the AI_A2A_PATROL -- ## 7. Manage "damage" behaviour of the AI in the AI_A2A_PATROL
-- --
-- When the AI is damaged, it is required that a new Patrol is started. However, damage cannon be foreseen early on. -- When the AI is damaged, it is required that a new Patrol is started. However, damage cannon be foreseen early on.
-- Therefore, when the damage treshold is reached, the AI will return immediately to the home base (RTB). -- Therefore, when the damage threshold is reached, the AI will return immediately to the home base (RTB).
-- Use the method @{#AI_A2A_PATROL.ManageDamage}() to have this proces in place. -- Use the method @{#AI_A2A_PATROL.ManageDamage}() to have this proces in place.
-- --
-- === -- ===
@@ -3243,17 +3243,17 @@ do -- AI_A2G_DISPATCHER
return self return self
end end
--- Set the default fuel treshold when defenders will RTB or Refuel in the air. --- Set the default fuel threshold when defenders will RTB or Refuel in the air.
-- The fuel treshold is by default set to 15%, which means that an aircraft will stay in the air until 15% of its fuel is remaining. -- The fuel threshold is by default set to 15%, which means that an aircraft will stay in the air until 15% of its fuel is remaining.
-- @param #AI_A2G_DISPATCHER self -- @param #AI_A2G_DISPATCHER self
-- @param #number FuelThreshold A decimal number between 0 and 1, that expresses the % of the treshold of fuel remaining in the tank when the plane will go RTB or Refuel. -- @param #number FuelThreshold A decimal number between 0 and 1, that expresses the % of the threshold of fuel remaining in the tank when the plane will go RTB or Refuel.
-- @return #AI_A2G_DISPATCHER -- @return #AI_A2G_DISPATCHER
-- @usage -- @usage
-- --
-- -- Now Setup the A2G dispatcher, and initialize it using the Detection object. -- -- Now Setup the A2G dispatcher, and initialize it using the Detection object.
-- A2GDispatcher = AI_A2G_DISPATCHER:New( Detection ) -- A2GDispatcher = AI_A2G_DISPATCHER:New( Detection )
-- --
-- -- Now Setup the default fuel treshold. -- -- Now Setup the default fuel threshold.
-- A2GDispatcher:SetDefaultFuelThreshold( 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank. -- A2GDispatcher:SetDefaultFuelThreshold( 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
-- --
function AI_A2G_DISPATCHER:SetDefaultFuelThreshold( FuelThreshold ) function AI_A2G_DISPATCHER:SetDefaultFuelThreshold( FuelThreshold )
@@ -3264,18 +3264,18 @@ do -- AI_A2G_DISPATCHER
end end
--- Set the fuel treshold for the squadron when defenders will RTB or Refuel in the air. --- Set the fuel threshold for the squadron when defenders will RTB or Refuel in the air.
-- The fuel treshold is by default set to 15%, which means that an aircraft will stay in the air until 15% of its fuel is remaining. -- The fuel threshold is by default set to 15%, which means that an aircraft will stay in the air until 15% of its fuel is remaining.
-- @param #AI_A2G_DISPATCHER self -- @param #AI_A2G_DISPATCHER self
-- @param #string SquadronName The name of the squadron. -- @param #string SquadronName The name of the squadron.
-- @param #number FuelThreshold A decimal number between 0 and 1, that expresses the % of the treshold of fuel remaining in the tank when the plane will go RTB or Refuel. -- @param #number FuelThreshold A decimal number between 0 and 1, that expresses the % of the threshold of fuel remaining in the tank when the plane will go RTB or Refuel.
-- @return #AI_A2G_DISPATCHER -- @return #AI_A2G_DISPATCHER
-- @usage -- @usage
-- --
-- -- Now Setup the A2G dispatcher, and initialize it using the Detection object. -- -- Now Setup the A2G dispatcher, and initialize it using the Detection object.
-- A2GDispatcher = AI_A2G_DISPATCHER:New( Detection ) -- A2GDispatcher = AI_A2G_DISPATCHER:New( Detection )
-- --
-- -- Now Setup the default fuel treshold. -- -- Now Setup the default fuel threshold.
-- A2GDispatcher:SetSquadronRefuelThreshold( "SquadronName", 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank. -- A2GDispatcher:SetSquadronRefuelThreshold( "SquadronName", 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
-- --
function AI_A2G_DISPATCHER:SetSquadronFuelThreshold( SquadronName, FuelThreshold ) function AI_A2G_DISPATCHER:SetSquadronFuelThreshold( SquadronName, FuelThreshold )
@@ -3295,7 +3295,7 @@ do -- AI_A2G_DISPATCHER
-- -- Now Setup the A2G dispatcher, and initialize it using the Detection object. -- -- Now Setup the A2G dispatcher, and initialize it using the Detection object.
-- A2GDispatcher = AI_A2G_DISPATCHER:New( Detection ) -- A2GDispatcher = AI_A2G_DISPATCHER:New( Detection )
-- --
-- -- Now Setup the default fuel treshold. -- -- Now Setup the default fuel threshold.
-- A2GDispatcher:SetDefaultFuelThreshold( 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank. -- A2GDispatcher:SetDefaultFuelThreshold( 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
-- --
-- -- Now Setup the default tanker. -- -- Now Setup the default tanker.
@@ -3318,7 +3318,7 @@ do -- AI_A2G_DISPATCHER
-- -- Now Setup the A2G dispatcher, and initialize it using the Detection object. -- -- Now Setup the A2G dispatcher, and initialize it using the Detection object.
-- A2GDispatcher = AI_A2G_DISPATCHER:New( Detection ) -- A2GDispatcher = AI_A2G_DISPATCHER:New( Detection )
-- --
-- -- Now Setup the squadron fuel treshold. -- -- Now Setup the squadron fuel threshold.
-- A2GDispatcher:SetSquadronRefuelThreshold( "SquadronName", 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank. -- A2GDispatcher:SetSquadronRefuelThreshold( "SquadronName", 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
-- --
-- -- Now Setup the squadron tanker. -- -- Now Setup the squadron tanker.
@@ -3421,7 +3421,7 @@ do -- AI_A2G_DISPATCHER
-- @param #AI_A2G_DISPATCHER self -- @param #AI_A2G_DISPATCHER self
function AI_A2G_DISPATCHER:CountDefendersEngaged( AttackerDetection, AttackerCount ) function AI_A2G_DISPATCHER:CountDefendersEngaged( AttackerDetection, AttackerCount )
-- First, count the active AIGroups Units, targetting the DetectedSet -- First, count the active AIGroups Units, targeting the DetectedSet
local DefendersEngaged = 0 local DefendersEngaged = 0
local DefendersTotal = 0 local DefendersTotal = 0
@@ -4729,5 +4729,29 @@ do
self:Patrol( SquadronName, PatrolTaskType ) self:Patrol( SquadronName, PatrolTaskType )
end end
--- Add resources to a Squadron
-- @param #AI_A2G_DISPATCHER self
-- @param #string Squadron The squadron name.
-- @param #number Amount Number of resources to add.
function AI_A2G_DISPATCHER:AddToSquadron(Squadron,Amount)
local Squadron = self:GetSquadron(Squadron)
if Squadron.ResourceCount then
Squadron.ResourceCount = Squadron.ResourceCount + Amount
end
self:T({Squadron = Squadron.Name,SquadronResourceCount = Squadron.ResourceCount})
end
--- Remove resources from a Squadron
-- @param #AI_A2G_DISPATCHER self
-- @param #string Squadron The squadron name.
-- @param #number Amount Number of resources to remove.
function AI_A2G_DISPATCHER:RemoveFromSquadron(Squadron,Amount)
local Squadron = self:GetSquadron(Squadron)
if Squadron.ResourceCount then
Squadron.ResourceCount = Squadron.ResourceCount - Amount
end
self:T({Squadron = Squadron.Name,SquadronResourceCount = Squadron.ResourceCount})
end
end end
+2 -2
View File
@@ -42,8 +42,8 @@
-- --
-- ![Process](..\Presentations\AI_GCI\Dia10.JPG) -- ![Process](..\Presentations\AI_GCI\Dia10.JPG)
-- --
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. -- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land. -- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
-- --
-- ![Process](..\Presentations\AI_GCI\Dia13.JPG) -- ![Process](..\Presentations\AI_GCI\Dia13.JPG)
-- --
+30 -10
View File
@@ -253,6 +253,9 @@ function AI_AIR:New( AIGroup )
self.IdleCount = 0 self.IdleCount = 0
self.RTBSpeedMaxFactor = 0.6
self.RTBSpeedMinFactor = 0.5
return self return self
end end
@@ -370,11 +373,11 @@ end
--- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base. --- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base.
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel treshold is calculated. -- Therefore, with a parameter and a calculation of the distance to the home base, the fuel threshold is calculated.
-- When the fuel treshold is reached, the AI will continue for a given time its patrol task in orbit, while a new AIControllable is targetted to the AI_AIR. -- When the fuel threshold is reached, the AI will continue for a given time its patrol task in orbit, while a new AIControllable is targetted to the AI_AIR.
-- Once the time is finished, the old AI will return to the base. -- Once the time is finished, the old AI will return to the base.
-- @param #AI_AIR self -- @param #AI_AIR self
-- @param #number FuelThresholdPercentage The treshold in percentage (between 0 and 1) when the AIControllable is considered to get out of fuel. -- @param #number FuelThresholdPercentage The threshold in percentage (between 0 and 1) when the AIControllable is considered to get out of fuel.
-- @param #number OutOfFuelOrbitTime The amount of seconds the out of fuel AIControllable will orbit before returning to the base. -- @param #number OutOfFuelOrbitTime The amount of seconds the out of fuel AIControllable will orbit before returning to the base.
-- @return #AI_AIR self -- @return #AI_AIR self
function AI_AIR:SetFuelThreshold( FuelThresholdPercentage, OutOfFuelOrbitTime ) function AI_AIR:SetFuelThreshold( FuelThresholdPercentage, OutOfFuelOrbitTime )
@@ -387,14 +390,14 @@ function AI_AIR:SetFuelThreshold( FuelThresholdPercentage, OutOfFuelOrbitTime )
return self return self
end end
--- When the AI is damaged beyond a certain treshold, it is required that the AI returns to the home base. --- When the AI is damaged beyond a certain threshold, it is required that the AI returns to the home base.
-- However, damage cannot be foreseen early on. -- However, damage cannot be foreseen early on.
-- Therefore, when the damage treshold is reached, -- Therefore, when the damage threshold is reached,
-- the AI will return immediately to the home base (RTB). -- the AI will return immediately to the home base (RTB).
-- Note that for groups, the average damage of the complete group will be calculated. -- Note that for groups, the average damage of the complete group will be calculated.
-- So, in a group of 4 airplanes, 2 lost and 2 with damage 0.2, the damage treshold will be 0.25. -- So, in a group of 4 airplanes, 2 lost and 2 with damage 0.2, the damage threshold will be 0.25.
-- @param #AI_AIR self -- @param #AI_AIR self
-- @param #number PatrolDamageThreshold The treshold in percentage (between 0 and 1) when the AI is considered to be damaged. -- @param #number PatrolDamageThreshold The threshold in percentage (between 0 and 1) when the AI is considered to be damaged.
-- @return #AI_AIR self -- @return #AI_AIR self
function AI_AIR:SetDamageThreshold( PatrolDamageThreshold ) function AI_AIR:SetDamageThreshold( PatrolDamageThreshold )
@@ -476,7 +479,7 @@ function AI_AIR:onafterStatus()
local Fuel = self.Controllable:GetFuelMin() local Fuel = self.Controllable:GetFuelMin()
-- If the fuel in the controllable is below the treshold percentage, -- If the fuel in the controllable is below the threshold percentage,
-- then send for refuel in case of a tanker, otherwise RTB. -- then send for refuel in case of a tanker, otherwise RTB.
if Fuel < self.FuelThresholdPercentage then if Fuel < self.FuelThresholdPercentage then
@@ -576,6 +579,19 @@ function AI_AIR.RTBHold( AIGroup, Fsm )
end end
--- Set the min and max factors on RTB speed. Use this, if your planes are heading back to base too fast. Default values are 0.5 and 0.6.
-- The RTB speed is calculated as the max speed of the unit multiplied by MinFactor (lower bracket) and multiplied by MaxFactor (upper bracket).
-- A random value in this bracket is then applied in the waypoint routing generation.
-- @param #AI_AIR self
-- @param #number MinFactor Lower bracket factor. Defaults to 0.5.
-- @param #number MaxFactor Upper bracket factor. Defaults to 0.6.
-- @return #AI_AIR self
function AI_AIR:SetRTBSpeedFactors(MinFactor,MaxFactor)
self.RTBSpeedMaxFactor = MaxFactor or 0.6
self.RTBSpeedMinFactor = MinFactor or 0.5
return self
end
--- @param #AI_AIR self --- @param #AI_AIR self
-- @param Wrapper.Group#GROUP AIGroup -- @param Wrapper.Group#GROUP AIGroup
@@ -590,6 +606,8 @@ function AI_AIR:onafterRTB( AIGroup, From, Event, To )
self:ClearTargetDistance() self:ClearTargetDistance()
--AIGroup:ClearTasks() --AIGroup:ClearTasks()
AIGroup:OptionProhibitAfterburner(true)
local EngageRoute = {} local EngageRoute = {}
--- Calculate the target route point. --- Calculate the target route point.
@@ -597,12 +615,14 @@ function AI_AIR:onafterRTB( AIGroup, From, Event, To )
local FromCoord = AIGroup:GetCoordinate() local FromCoord = AIGroup:GetCoordinate()
local ToTargetCoord = self.HomeAirbase:GetCoordinate() -- coordinate is on land height(!) local ToTargetCoord = self.HomeAirbase:GetCoordinate() -- coordinate is on land height(!)
local ToTargetVec3 = ToTargetCoord:GetVec3() local ToTargetVec3 = ToTargetCoord:GetVec3()
ToTargetVec3.y = ToTargetCoord:GetLandHeight()+1000 -- let's set this 1000m/3000 feet above ground ToTargetVec3.y = ToTargetCoord:GetLandHeight()+3000 -- let's set this 1000m/3000 feet above ground
local ToTargetCoord2 = COORDINATE:NewFromVec3( ToTargetVec3 ) local ToTargetCoord2 = COORDINATE:NewFromVec3( ToTargetVec3 )
if not self.RTBMinSpeed or not self.RTBMaxSpeed then if not self.RTBMinSpeed or not self.RTBMaxSpeed then
local RTBSpeedMax = AIGroup:GetSpeedMax() local RTBSpeedMax = AIGroup:GetSpeedMax()
self:SetRTBSpeed( RTBSpeedMax * 0.5, RTBSpeedMax * 0.6 ) local RTBSpeedMaxFactor = self.RTBSpeedMaxFactor or 0.6
local RTBSpeedMinFactor = self.RTBSpeedMinFactor or 0.5
self:SetRTBSpeed( RTBSpeedMax * RTBSpeedMinFactor, RTBSpeedMax * RTBSpeedMaxFactor)
end end
local RTBSpeed = math.random( self.RTBMinSpeed, self.RTBMaxSpeed ) local RTBSpeed = math.random( self.RTBMinSpeed, self.RTBMaxSpeed )
@@ -10,11 +10,11 @@
-- * Setup (CAS) Controlled Air Support squadrons, to attack closeby enemy ground units near friendly installations. -- * Setup (CAS) Controlled Air Support squadrons, to attack closeby enemy ground units near friendly installations.
-- * Setup (BAI) Battleground Air Interdiction squadrons to attack remote enemy ground units and targets. -- * Setup (BAI) Battleground Air Interdiction squadrons to attack remote enemy ground units and targets.
-- * Define and use a detection network controlled by recce. -- * Define and use a detection network controlled by recce.
-- * Define AIR defense squadrons at airbases, farps and carriers. -- * Define AIR defense squadrons at airbases, FARPs and carriers.
-- * Enable airbases for AIR defenses. -- * Enable airbases for AIR defenses.
-- * Add different planes and helicopter templates to squadrons. -- * Add different planes and helicopter templates to squadrons.
-- * Assign squadrons to execute a specific engagement type depending on threat level of the detected ground enemy unit composition. -- * Assign squadrons to execute a specific engagement type depending on threat level of the detected ground enemy unit composition.
-- * Add multiple squadrons to different airbases, farps or carriers. -- * Add multiple squadrons to different airbases, FARPs or carriers.
-- * Define different ranges to engage upon. -- * Define different ranges to engage upon.
-- * Establish an automatic in air refuel process for planes using refuel tankers. -- * Establish an automatic in air refuel process for planes using refuel tankers.
-- * Setup default settings for all squadrons and AIR defenses. -- * Setup default settings for all squadrons and AIR defenses.
@@ -40,7 +40,7 @@
-- --
-- AI_AIR_DISPATCHER is the main AIR defense class that models the AIR defense system. -- AI_AIR_DISPATCHER is the main AIR defense class that models the AIR defense system.
-- --
-- Before you start using the AI_AIR_DISPATCHER, ask youself the following questions. -- Before you start using the AI_AIR_DISPATCHER, ask yourself the following questions.
-- --
-- --
-- ## 1. Which coalition am I modeling an AIR defense system for? blue or red? -- ## 1. Which coalition am I modeling an AIR defense system for? blue or red?
@@ -128,7 +128,7 @@
-- Depending on the defense type, different payloads will be needed. See further points on squadron definition. -- Depending on the defense type, different payloads will be needed. See further points on squadron definition.
-- --
-- --
-- ## 7. Where will the Squadrons be located? On Airbases? On Carrier Ships? On Farps? -- ## 7. Where will the Squadrons be located? On Airbases? On Carrier Ships? On FARPs?
-- --
-- Squadrons are placed at the **home base** on an **airfield**, **carrier** or **farp**. -- Squadrons are placed at the **home base** on an **airfield**, **carrier** or **farp**.
-- Carefully plan where each Squadron will be located as part of the defense system required for mission effective defenses. -- Carefully plan where each Squadron will be located as part of the defense system required for mission effective defenses.
@@ -354,7 +354,7 @@ do -- AI_AIR_DISPATCHER
-- **DetectionSetGroup** is then calling `FilterStart()`, which is starting the dynamic filtering or inclusion of these groups. -- **DetectionSetGroup** is then calling `FilterStart()`, which is starting the dynamic filtering or inclusion of these groups.
-- Note that any destroy or new spawn of a group having a name, starting with the above prefix, will be removed or added to the set. -- Note that any destroy or new spawn of a group having a name, starting with the above prefix, will be removed or added to the set.
-- --
-- Then a new detection object is created from the class `DETECTION_AREAS`. A grouping radius of 1000 meters (1km) is choosen. -- Then a new detection object is created from the class `DETECTION_AREAS`. A grouping radius of 1000 meters (1km) is chosen.
-- --
-- The `Detection` object is then passed to the @{#AI_AIR_DISPATCHER.New}() method to indicate the reconnaissance network -- The `Detection` object is then passed to the @{#AI_AIR_DISPATCHER.New}() method to indicate the reconnaissance network
-- configuration and setup the AIR defense detection mechanism. -- configuration and setup the AIR defense detection mechanism.
@@ -647,7 +647,7 @@ do -- AI_AIR_DISPATCHER
-- * @{#AI_AIR_DISPATCHER.SetSquadronLandingAtRunway}() will despawn the returning aircraft directly after landing at the runway. -- * @{#AI_AIR_DISPATCHER.SetSquadronLandingAtRunway}() will despawn the returning aircraft directly after landing at the runway.
-- * @{#AI_AIR_DISPATCHER.SetSquadronLandingAtEngineShutdown}() will despawn the returning aircraft when the aircraft has returned to its parking spot and has turned off its engines. -- * @{#AI_AIR_DISPATCHER.SetSquadronLandingAtEngineShutdown}() will despawn the returning aircraft when the aircraft has returned to its parking spot and has turned off its engines.
-- --
-- You can use these methods to minimize the airbase coodination overhead and to increase the airbase efficiency. -- You can use these methods to minimize the airbase coordination overhead and to increase the airbase efficiency.
-- When there are lots of aircraft returning for landing, at the same airbase, the takeoff process will be halted, which can cause a complete failure of the -- When there are lots of aircraft returning for landing, at the same airbase, the takeoff process will be halted, which can cause a complete failure of the
-- A2A defense system, as no new CAP or GCI planes can takeoff. -- A2A defense system, as no new CAP or GCI planes can takeoff.
-- Note that the method @{#AI_AIR_DISPATCHER.SetSquadronLandingNearAirbase}() will only work for returning aircraft, not for damaged or out of fuel aircraft. -- Note that the method @{#AI_AIR_DISPATCHER.SetSquadronLandingNearAirbase}() will only work for returning aircraft, not for damaged or out of fuel aircraft.
@@ -724,13 +724,13 @@ do -- AI_AIR_DISPATCHER
-- --
-- Use the method @{#AI_AIR_DISPATCHER.SetSquadronEngageLimit}() to limit the amount of aircraft that will engage with the enemy, per squadron. -- Use the method @{#AI_AIR_DISPATCHER.SetSquadronEngageLimit}() to limit the amount of aircraft that will engage with the enemy, per squadron.
-- --
-- ## 4. Set the **fuel treshold**. -- ## 4. Set the **fuel threshold**.
-- --
-- When aircraft get **out of fuel** to a certain %-tage, which is by default **15% (0.15)**, there are two possible actions that can be taken: -- When aircraft get **out of fuel** to a certain %, which is by default **15% (0.15)**, there are two possible actions that can be taken:
-- - The aircraft will go RTB, and will be replaced with a new aircraft if possible. -- - The aircraft will go RTB, and will be replaced with a new aircraft if possible.
-- - The aircraft will refuel at a tanker, if a tanker has been specified for the squadron. -- - The aircraft will refuel at a tanker, if a tanker has been specified for the squadron.
-- --
-- Use the method @{#AI_AIR_DISPATCHER.SetSquadronFuelThreshold}() to set the **squadron fuel treshold** of the aircraft for all squadrons. -- Use the method @{#AI_AIR_DISPATCHER.SetSquadronFuelThreshold}() to set the **squadron fuel threshold** of the aircraft for all squadrons.
-- --
-- ## 6. Other configuration options -- ## 6. Other configuration options
-- --
@@ -786,17 +786,17 @@ do -- AI_AIR_DISPATCHER
-- --
-- Use the method @{#AI_AIR_DISPATCHER.SetDefaultGrouping}() to set the **default grouping** of spawned airplanes for all squadrons. -- Use the method @{#AI_AIR_DISPATCHER.SetDefaultGrouping}() to set the **default grouping** of spawned airplanes for all squadrons.
-- --
-- ## 10.5. Default RTB fuel treshold. -- ## 10.5. Default RTB fuel threshold.
-- --
-- When an airplane gets **out of fuel** to a certain %-tage, which is **15% (0.15)**, it will go RTB, and will be replaced with a new airplane when applicable. -- When an airplane gets **out of fuel** to a certain %, which is **15% (0.15)**, it will go RTB, and will be replaced with a new airplane when applicable.
-- --
-- Use the method @{#AI_AIR_DISPATCHER.SetDefaultFuelThreshold}() to set the **default fuel treshold** of spawned airplanes for all squadrons. -- Use the method @{#AI_AIR_DISPATCHER.SetDefaultFuelThreshold}() to set the **default fuel threshold** of spawned airplanes for all squadrons.
-- --
-- ## 10.6. Default RTB damage treshold. -- ## 10.6. Default RTB damage threshold.
-- --
-- When an airplane is **damaged** to a certain %-tage, which is **40% (0.40)**, it will go RTB, and will be replaced with a new airplane when applicable. -- When an airplane is **damaged** to a certain %, which is **40% (0.40)**, it will go RTB, and will be replaced with a new airplane when applicable.
-- --
-- Use the method @{#AI_AIR_DISPATCHER.SetDefaultDamageThreshold}() to set the **default damage treshold** of spawned airplanes for all squadrons. -- Use the method @{#AI_AIR_DISPATCHER.SetDefaultDamageThreshold}() to set the **default damage threshold** of spawned airplanes for all squadrons.
-- --
-- ## 10.7. Default settings for **patrol**. -- ## 10.7. Default settings for **patrol**.
-- --
@@ -829,7 +829,7 @@ do -- AI_AIR_DISPATCHER
-- --
-- In the mission editor, setup a group with task Refuelling. A tanker unit of the correct coalition will be automatically selected. -- In the mission editor, setup a group with task Refuelling. A tanker unit of the correct coalition will be automatically selected.
-- Then, use the method @{#AI_AIR_DISPATCHER.SetDefaultTanker}() to set the tanker for the dispatcher. -- Then, use the method @{#AI_AIR_DISPATCHER.SetDefaultTanker}() to set the tanker for the dispatcher.
-- Use the method @{#AI_AIR_DISPATCHER.SetDefaultFuelThreshold}() to set the %-tage left in the defender airplane tanks when a refuel action is needed. -- Use the method @{#AI_AIR_DISPATCHER.SetDefaultFuelThreshold}() to set the % left in the defender airplane tanks when a refuel action is needed.
-- --
-- When the tanker specified is alive and in the air, the tanker will be used for refuelling. -- When the tanker specified is alive and in the air, the tanker will be used for refuelling.
-- --
@@ -843,7 +843,7 @@ do -- AI_AIR_DISPATCHER
-- A2ADispatcher:SetSquadronCapInterval("Sochi", 2, 30, 600, 1 ) -- A2ADispatcher:SetSquadronCapInterval("Sochi", 2, 30, 600, 1 )
-- A2ADispatcher:SetSquadronGci( "Sochi", 900, 1200 ) -- A2ADispatcher:SetSquadronGci( "Sochi", 900, 1200 )
-- --
-- -- Set the default tanker for refuelling to "Tanker", when the default fuel treshold has reached 90% fuel left. -- -- Set the default tanker for refuelling to "Tanker", when the default fuel threshold has reached 90% fuel left.
-- A2ADispatcher:SetDefaultFuelThreshold( 0.9 ) -- A2ADispatcher:SetDefaultFuelThreshold( 0.9 )
-- A2ADispatcher:SetDefaultTanker( "Tanker" ) -- A2ADispatcher:SetDefaultTanker( "Tanker" )
-- --
@@ -883,9 +883,6 @@ do -- AI_AIR_DISPATCHER
-- However, the squadron will still stay alive. Any airplane that is airborne will continue its operations until all airborne airplanes -- However, the squadron will still stay alive. Any airplane that is airborne will continue its operations until all airborne airplanes
-- of the squadron will be destroyed. This to keep consistency of air operations not to confuse the players. -- of the squadron will be destroyed. This to keep consistency of air operations not to confuse the players.
-- --
--
--
--
-- @field #AI_AIR_DISPATCHER -- @field #AI_AIR_DISPATCHER
AI_AIR_DISPATCHER = { AI_AIR_DISPATCHER = {
ClassName = "AI_AIR_DISPATCHER", ClassName = "AI_AIR_DISPATCHER",
@@ -949,7 +946,6 @@ do -- AI_AIR_DISPATCHER
--- @field #AI_AIR_DISPATCHER.DefenseQueue DefenseQueue --- @field #AI_AIR_DISPATCHER.DefenseQueue DefenseQueue
AI_AIR_DISPATCHER.DefenseQueue = {} AI_AIR_DISPATCHER.DefenseQueue = {}
--- Defense approach types --- Defense approach types
-- @type #AI_AIR_DISPATCHER.DefenseApproach -- @type #AI_AIR_DISPATCHER.DefenseApproach
AI_AIR_DISPATCHER.DefenseApproach = { AI_AIR_DISPATCHER.DefenseApproach = {
@@ -958,26 +954,26 @@ do -- AI_AIR_DISPATCHER
} }
--- AI_AIR_DISPATCHER constructor. --- AI_AIR_DISPATCHER constructor.
-- This is defining the AIR DISPATCHER for one coaliton. -- This is defining the AIR DISPATCHER for one coalition.
-- The Dispatcher works with a @{Functional.Detection#DETECTION_BASE} object that is taking of the detection of targets using the EWR units. -- The Dispatcher works with a @{Functional.Detection#DETECTION_BASE} object that is taking of the detection of targets using the EWR units.
-- The Detection object is polymorphic, depending on the type of detection object choosen, the detection will work differently. -- The Detection object is polymorphic, depending on the type of detection object chosen, the detection will work differently.
-- @param #AI_AIR_DISPATCHER self -- @param #AI_AIR_DISPATCHER self
-- @param Functional.Detection#DETECTION_BASE Detection The DETECTION object that will detects targets using the the Early Warning Radar network. -- @param Functional.Detection#DETECTION_BASE Detection The DETECTION object that will detects targets using the the Early Warning Radar network.
-- @return #AI_AIR_DISPATCHER self -- @return #AI_AIR_DISPATCHER self
-- @usage -- @usage
-- --
-- -- Setup the Detection, using DETECTION_AREAS. -- -- Setup the Detection, using DETECTION_AREAS.
-- -- First define the SET of GROUPs that are defining the EWR network. -- -- First define the SET of GROUPs that are defining the EWR network.
-- -- Here with prefixes DF CCCP AWACS, DF CCCP EWR. -- -- Here with prefixes DF CCCP AWACS, DF CCCP EWR.
-- DetectionSetGroup = SET_GROUP:New() -- DetectionSetGroup = SET_GROUP:New()
-- DetectionSetGroup:FilterPrefixes( { "DF CCCP AWACS", "DF CCCP EWR" } ) -- DetectionSetGroup:FilterPrefixes( { "DF CCCP AWACS", "DF CCCP EWR" } )
-- DetectionSetGroup:FilterStart() -- DetectionSetGroup:FilterStart()
-- --
-- -- Define the DETECTION_AREAS, using the DetectionSetGroup, with a 30km grouping radius. -- -- Define the DETECTION_AREAS, using the DetectionSetGroup, with a 30km grouping radius.
-- Detection = DETECTION_AREAS:New( DetectionSetGroup, 30000 ) -- Detection = DETECTION_AREAS:New( DetectionSetGroup, 30000 )
-- --
-- -- Now Setup the AIR dispatcher, and initialize it using the Detection object. -- -- Now Setup the AIR dispatcher, and initialize it using the Detection object.
-- AIRDispatcher = AI_AIR_DISPATCHER:New( Detection ) -- -- AIRDispatcher = AI_AIR_DISPATCHER:New( Detection )
-- --
function AI_AIR_DISPATCHER:New( Detection ) function AI_AIR_DISPATCHER:New( Detection )
@@ -1435,17 +1431,17 @@ do -- AI_AIR_DISPATCHER
end end
--- Set the default damage treshold when defenders will RTB. --- Set the default damage threshold when defenders will RTB.
-- The default damage treshold is by default set to 40%, which means that when the airplane is 40% damaged, it will go RTB. -- The default damage threshold is by default set to 40%, which means that when the airplane is 40% damaged, it will go RTB.
-- @param #AI_AIR_DISPATCHER self -- @param #AI_AIR_DISPATCHER self
-- @param #number DamageThreshold A decimal number between 0 and 1, that expresses the %-tage of the damage treshold before going RTB. -- @param #number DamageThreshold A decimal number between 0 and 1, that expresses the % of the damage threshold before going RTB.
-- @return #AI_AIR_DISPATCHER -- @return #AI_AIR_DISPATCHER
-- @usage -- @usage
-- --
-- -- Now Setup the AIR dispatcher, and initialize it using the Detection object. -- -- Now Setup the AIR dispatcher, and initialize it using the Detection object.
-- AIRDispatcher = AI_AIR_DISPATCHER:New( Detection ) -- AIRDispatcher = AI_AIR_DISPATCHER:New( Detection )
-- --
-- -- Now Setup the default damage treshold. -- -- Now Setup the default damage threshold.
-- AIRDispatcher:SetDefaultDamageThreshold( 0.90 ) -- Go RTB when the airplane 90% damaged. -- AIRDispatcher:SetDefaultDamageThreshold( 0.90 ) -- Go RTB when the airplane 90% damaged.
-- --
function AI_AIR_DISPATCHER:SetDefaultDamageThreshold( DamageThreshold ) function AI_AIR_DISPATCHER:SetDefaultDamageThreshold( DamageThreshold )
@@ -1989,7 +1985,7 @@ do -- AI_AIR_DISPATCHER
--- Defines the default amount of extra planes that will take-off as part of the defense system. --- Defines the default amount of extra planes that will take-off as part of the defense system.
-- @param #AI_AIR_DISPATCHER self -- @param #AI_AIR_DISPATCHER self
-- @param #number Overhead The %-tage of Units that dispatching command will allocate to intercept in surplus of detected amount of units. -- @param #number Overhead The % of Units that dispatching command will allocate to intercept in surplus of detected amount of units.
-- The default overhead is 1, so equal balance. The @{#AI_AIR_DISPATCHER.SetOverhead}() method can be used to tweak the defense strength, -- The default overhead is 1, so equal balance. The @{#AI_AIR_DISPATCHER.SetOverhead}() method can be used to tweak the defense strength,
-- taking into account the plane types of the squadron. For example, a MIG-31 with full long-distance AIR missiles payload, may still be less effective than a F-15C with short missiles... -- taking into account the plane types of the squadron. For example, a MIG-31 with full long-distance AIR missiles payload, may still be less effective than a F-15C with short missiles...
-- So in this case, one may want to use the Overhead method to allocate more defending planes as the amount of detected attacking planes. -- So in this case, one may want to use the Overhead method to allocate more defending planes as the amount of detected attacking planes.
@@ -2028,7 +2024,7 @@ do -- AI_AIR_DISPATCHER
--- Defines the amount of extra planes that will take-off as part of the defense system. --- Defines the amount of extra planes that will take-off as part of the defense system.
-- @param #AI_AIR_DISPATCHER self -- @param #AI_AIR_DISPATCHER self
-- @param #string SquadronName The name of the squadron. -- @param #string SquadronName The name of the squadron.
-- @param #number Overhead The %-tage of Units that dispatching command will allocate to intercept in surplus of detected amount of units. -- @param #number Overhead The % of Units that dispatching command will allocate to intercept in surplus of detected amount of units.
-- The default overhead is 1, so equal balance. The @{#AI_AIR_DISPATCHER.SetOverhead}() method can be used to tweak the defense strength, -- The default overhead is 1, so equal balance. The @{#AI_AIR_DISPATCHER.SetOverhead}() method can be used to tweak the defense strength,
-- taking into account the plane types of the squadron. For example, a MIG-31 with full long-distance AIR missiles payload, may still be less effective than a F-15C with short missiles... -- taking into account the plane types of the squadron. For example, a MIG-31 with full long-distance AIR missiles payload, may still be less effective than a F-15C with short missiles...
-- So in this case, one may want to use the Overhead method to allocate more defending planes as the amount of detected attacking planes. -- So in this case, one may want to use the Overhead method to allocate more defending planes as the amount of detected attacking planes.
@@ -2068,7 +2064,7 @@ do -- AI_AIR_DISPATCHER
--- Gets the overhead of planes as part of the defense system, in comparison with the attackers. --- Gets the overhead of planes as part of the defense system, in comparison with the attackers.
-- @param #AI_AIR_DISPATCHER self -- @param #AI_AIR_DISPATCHER self
-- @param #string SquadronName The name of the squadron. -- @param #string SquadronName The name of the squadron.
-- @return #number The %-tage of Units that dispatching command will allocate to intercept in surplus of detected amount of units. -- @return #number The % of Units that dispatching command will allocate to intercept in surplus of detected amount of units.
-- The default overhead is 1, so equal balance. The @{#AI_AIR_DISPATCHER.SetOverhead}() method can be used to tweak the defense strength, -- The default overhead is 1, so equal balance. The @{#AI_AIR_DISPATCHER.SetOverhead}() method can be used to tweak the defense strength,
-- taking into account the plane types of the squadron. For example, a MIG-31 with full long-distance AIR missiles payload, may still be less effective than a F-15C with short missiles... -- taking into account the plane types of the squadron. For example, a MIG-31 with full long-distance AIR missiles payload, may still be less effective than a F-15C with short missiles...
-- So in this case, one may want to use the Overhead method to allocate more defending planes as the amount of detected attacking planes. -- So in this case, one may want to use the Overhead method to allocate more defending planes as the amount of detected attacking planes.
@@ -2674,17 +2670,17 @@ do -- AI_AIR_DISPATCHER
return self return self
end end
--- Set the default fuel treshold when defenders will RTB or Refuel in the air. --- Set the default fuel threshold when defenders will RTB or Refuel in the air.
-- The fuel treshold is by default set to 15%, which means that an airplane will stay in the air until 15% of its fuel has been consumed. -- The fuel threshold is by default set to 15%, which means that an airplane will stay in the air until 15% of its fuel has been consumed.
-- @param #AI_AIR_DISPATCHER self -- @param #AI_AIR_DISPATCHER self
-- @param #number FuelThreshold A decimal number between 0 and 1, that expresses the %-tage of the treshold of fuel remaining in the tank when the plane will go RTB or Refuel. -- @param #number FuelThreshold A decimal number between 0 and 1, that expresses the % of the threshold of fuel remaining in the tank when the plane will go RTB or Refuel.
-- @return #AI_AIR_DISPATCHER -- @return #AI_AIR_DISPATCHER
-- @usage -- @usage
-- --
-- -- Now Setup the AIR dispatcher, and initialize it using the Detection object. -- -- Now Setup the AIR dispatcher, and initialize it using the Detection object.
-- AIRDispatcher = AI_AIR_DISPATCHER:New( Detection ) -- AIRDispatcher = AI_AIR_DISPATCHER:New( Detection )
-- --
-- -- Now Setup the default fuel treshold. -- -- Now Setup the default fuel threshold.
-- AIRDispatcher:SetDefaultFuelThreshold( 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank. -- AIRDispatcher:SetDefaultFuelThreshold( 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
-- --
function AI_AIR_DISPATCHER:SetDefaultFuelThreshold( FuelThreshold ) function AI_AIR_DISPATCHER:SetDefaultFuelThreshold( FuelThreshold )
@@ -2695,18 +2691,18 @@ do -- AI_AIR_DISPATCHER
end end
--- Set the fuel treshold for the squadron when defenders will RTB or Refuel in the air. --- Set the fuel threshold for the squadron when defenders will RTB or Refuel in the air.
-- The fuel treshold is by default set to 15%, which means that an airplane will stay in the air until 15% of its fuel has been consumed. -- The fuel threshold is by default set to 15%, which means that an airplane will stay in the air until 15% of its fuel has been consumed.
-- @param #AI_AIR_DISPATCHER self -- @param #AI_AIR_DISPATCHER self
-- @param #string SquadronName The name of the squadron. -- @param #string SquadronName The name of the squadron.
-- @param #number FuelThreshold A decimal number between 0 and 1, that expresses the %-tage of the treshold of fuel remaining in the tank when the plane will go RTB or Refuel. -- @param #number FuelThreshold A decimal number between 0 and 1, that expresses the % of the threshold of fuel remaining in the tank when the plane will go RTB or Refuel.
-- @return #AI_AIR_DISPATCHER -- @return #AI_AIR_DISPATCHER
-- @usage -- @usage
-- --
-- -- Now Setup the AIR dispatcher, and initialize it using the Detection object. -- -- Now Setup the AIR dispatcher, and initialize it using the Detection object.
-- AIRDispatcher = AI_AIR_DISPATCHER:New( Detection ) -- AIRDispatcher = AI_AIR_DISPATCHER:New( Detection )
-- --
-- -- Now Setup the default fuel treshold. -- -- Now Setup the default fuel threshold.
-- AIRDispatcher:SetSquadronRefuelThreshold( "SquadronName", 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank. -- AIRDispatcher:SetSquadronRefuelThreshold( "SquadronName", 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
-- --
function AI_AIR_DISPATCHER:SetSquadronFuelThreshold( SquadronName, FuelThreshold ) function AI_AIR_DISPATCHER:SetSquadronFuelThreshold( SquadronName, FuelThreshold )
@@ -2726,7 +2722,7 @@ do -- AI_AIR_DISPATCHER
-- -- Now Setup the AIR dispatcher, and initialize it using the Detection object. -- -- Now Setup the AIR dispatcher, and initialize it using the Detection object.
-- AIRDispatcher = AI_AIR_DISPATCHER:New( Detection ) -- AIRDispatcher = AI_AIR_DISPATCHER:New( Detection )
-- --
-- -- Now Setup the default fuel treshold. -- -- Now Setup the default fuel threshold.
-- AIRDispatcher:SetDefaultFuelThreshold( 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank. -- AIRDispatcher:SetDefaultFuelThreshold( 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
-- --
-- -- Now Setup the default tanker. -- -- Now Setup the default tanker.
@@ -2749,7 +2745,7 @@ do -- AI_AIR_DISPATCHER
-- -- Now Setup the AIR dispatcher, and initialize it using the Detection object. -- -- Now Setup the AIR dispatcher, and initialize it using the Detection object.
-- AIRDispatcher = AI_AIR_DISPATCHER:New( Detection ) -- AIRDispatcher = AI_AIR_DISPATCHER:New( Detection )
-- --
-- -- Now Setup the squadron fuel treshold. -- -- Now Setup the squadron fuel threshold.
-- AIRDispatcher:SetSquadronRefuelThreshold( "SquadronName", 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank. -- AIRDispatcher:SetSquadronRefuelThreshold( "SquadronName", 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
-- --
-- -- Now Setup the squadron tanker. -- -- Now Setup the squadron tanker.
@@ -2847,7 +2843,7 @@ do -- AI_AIR_DISPATCHER
-- @param #AI_AIR_DISPATCHER self -- @param #AI_AIR_DISPATCHER self
function AI_AIR_DISPATCHER:CountDefendersEngaged( AttackerDetection, AttackerCount ) function AI_AIR_DISPATCHER:CountDefendersEngaged( AttackerDetection, AttackerCount )
-- First, count the active AIGroups Units, targetting the DetectedSet -- First, count the active AIGroups Units, targeting the DetectedSet
local DefendersEngaged = 0 local DefendersEngaged = 0
local DefendersTotal = 0 local DefendersTotal = 0
+6 -2
View File
@@ -42,8 +42,8 @@
-- --
-- ![Process](..\Presentations\AI_GCI\Dia10.JPG) -- ![Process](..\Presentations\AI_GCI\Dia10.JPG)
-- --
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. -- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land. -- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
-- --
-- ![Process](..\Presentations\AI_GCI\Dia13.JPG) -- ![Process](..\Presentations\AI_GCI\Dia13.JPG)
-- --
@@ -533,6 +533,10 @@ function AI_AIR_ENGAGE:onafterEngage( DefenderGroup, From, Event, To, AttackSetU
DefenderCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude. DefenderCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude.
local TargetCoord = AttackSetUnit:GetFirst():GetPointVec3() local TargetCoord = AttackSetUnit:GetFirst():GetPointVec3()
if not TargetCoord then
self:Return()
return
end
TargetCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude. TargetCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude.
local TargetDistance = DefenderCoord:Get2DDistance( TargetCoord ) local TargetDistance = DefenderCoord:Get2DDistance( TargetCoord )
+3 -3
View File
@@ -39,8 +39,8 @@
-- --
-- ![Process](..\Presentations\AI_CAP\Dia10.JPG) -- ![Process](..\Presentations\AI_CAP\Dia10.JPG)
-- --
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. -- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land. -- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
-- --
-- ![Process](..\Presentations\AI_CAP\Dia13.JPG) -- ![Process](..\Presentations\AI_CAP\Dia13.JPG)
-- --
@@ -70,7 +70,7 @@
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets. -- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
-- * **@{#AI_AIR_PATROL.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}. -- * **@{#AI_AIR_PATROL.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}.
-- * **@{#AI_AIR_PATROL.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task. -- * **@{#AI_AIR_PATROL.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task.
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. -- * **Status** ( Group ): The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
-- --
-- ## 3. Set the Range of Engagement -- ## 3. Set the Range of Engagement
-- --
+6 -6
View File
@@ -49,7 +49,7 @@
-- Upon started, The AI will **Route** itself towards the random 3D point within a patrol zone, -- Upon started, The AI will **Route** itself towards the random 3D point within a patrol zone,
-- using a random speed within the given altitude and speed limits. -- using a random speed within the given altitude and speed limits.
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits. -- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
-- This cycle will continue until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. -- This cycle will continue until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
-- --
-- ![Route Event](..\Presentations\AI_BAI\Dia5.JPG) -- ![Route Event](..\Presentations\AI_BAI\Dia5.JPG)
-- --
@@ -87,7 +87,7 @@
-- It will keep patrolling there, until it is notified to RTB or move to another BOMB Zone. -- It will keep patrolling there, until it is notified to RTB or move to another BOMB Zone.
-- It can be notified to go RTB through the **RTB** event. -- It can be notified to go RTB through the **RTB** event.
-- --
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land. -- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
-- --
-- ![Engage Event](..\Presentations\AI_BAI\Dia12.JPG) -- ![Engage Event](..\Presentations\AI_BAI\Dia12.JPG)
-- --
@@ -117,7 +117,7 @@
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets. -- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
-- * **@{#AI_BAI_ZONE.Destroy}**: The AI has destroyed a target @{Wrapper.Unit}. -- * **@{#AI_BAI_ZONE.Destroy}**: The AI has destroyed a target @{Wrapper.Unit}.
-- * **@{#AI_BAI_ZONE.Destroyed}**: The AI has destroyed all target @{Wrapper.Unit}s assigned in the BOMB task. -- * **@{#AI_BAI_ZONE.Destroyed}**: The AI has destroyed all target @{Wrapper.Unit}s assigned in the BOMB task.
-- * **Status**: The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. -- * **Status**: The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
-- --
-- ## 3. Modify the Engage Zone behaviour to pinpoint a **map object** or **scenery object** -- ## 3. Modify the Engage Zone behaviour to pinpoint a **map object** or **scenery object**
-- --
@@ -515,8 +515,8 @@ function AI_BAI_ZONE:onafterEngage( Controllable, From, Event, To,
--- Calculate the current route point. --- Calculate the current route point.
local CurrentVec2 = self.Controllable:GetVec2() local CurrentVec2 = self.Controllable:GetVec2()
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). --DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() local CurrentAltitude = self.Controllable:GetAltitude()
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
local ToEngageZoneSpeed = self.PatrolMaxSpeed local ToEngageZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:WaypointAir( local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
@@ -602,7 +602,7 @@ function AI_BAI_ZONE:onafterEngage( Controllable, From, Event, To,
self:SetRefreshTimeInterval( 2 ) self:SetRefreshTimeInterval( 2 )
self:SetDetectionActivated() self:SetDetectionActivated()
self:__Target( -2 ) -- Start Targetting self:__Target( -2 ) -- Start targeting
end end
end end
+1 -1
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)
-- --
-- === -- ===
-- --
+11 -7
View File
@@ -4,13 +4,13 @@
-- --
-- * Patrol AI airplanes within a given zone. -- * Patrol AI airplanes within a given zone.
-- * Trigger detected events when enemy airplanes are detected. -- * Trigger detected events when enemy airplanes are detected.
-- * Manage a fuel treshold to RTB on time. -- * Manage a fuel threshold to RTB on time.
-- * Engage the enemy when detected. -- * Engage the enemy when detected.
-- --
-- --
-- === -- ===
-- --
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/CAP%20-%20Combat%20Air%20Patrol) -- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/CAP%20-%20Combat%20Air%20Patrol)
-- --
-- === -- ===
-- --
@@ -65,8 +65,8 @@
-- --
-- ![Process](..\Presentations\AI_CAP\Dia10.JPG) -- ![Process](..\Presentations\AI_CAP\Dia10.JPG)
-- --
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. -- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land. -- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
-- --
-- ![Process](..\Presentations\AI_CAP\Dia13.JPG) -- ![Process](..\Presentations\AI_CAP\Dia13.JPG)
-- --
@@ -96,7 +96,7 @@
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets. -- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
-- * **@{#AI_CAP_ZONE.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}. -- * **@{#AI_CAP_ZONE.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}.
-- * **@{#AI_CAP_ZONE.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task. -- * **@{#AI_CAP_ZONE.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task.
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. -- * **Status** ( Group ): The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
-- --
-- ## 3. Set the Range of Engagement -- ## 3. Set the Range of Engagement
-- --
@@ -428,8 +428,12 @@ function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To )
--- Calculate the current route point. --- Calculate the current route point.
local CurrentVec2 = self.Controllable:GetVec2() local CurrentVec2 = self.Controllable:GetVec2()
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). if not CurrentVec2 then -- flight dead at this point
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() return self
end
--DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
local CurrentAltitude = self.Controllable:GetAltitude()
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
local ToEngageZoneSpeed = self.PatrolMaxSpeed local ToEngageZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:WaypointAir( local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
+6 -6
View File
@@ -11,7 +11,7 @@
-- --
-- === -- ===
-- --
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/CAS%20-%20Close%20Air%20Support) -- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/CAS%20-%20Close%20Air%20Support)
-- --
-- === -- ===
-- --
@@ -49,7 +49,7 @@
-- Upon started, The AI will **Route** itself towards the random 3D point within a patrol zone, -- Upon started, The AI will **Route** itself towards the random 3D point within a patrol zone,
-- using a random speed within the given altitude and speed limits. -- using a random speed within the given altitude and speed limits.
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits. -- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
-- This cycle will continue until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. -- This cycle will continue until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
-- --
-- ![Route Event](..\Presentations\AI_CAS\Dia5.JPG) -- ![Route Event](..\Presentations\AI_CAS\Dia5.JPG)
-- --
@@ -87,7 +87,7 @@
-- It will keep patrolling there, until it is notified to RTB or move to another CAS Zone. -- It will keep patrolling there, until it is notified to RTB or move to another CAS Zone.
-- It can be notified to go RTB through the **RTB** event. -- It can be notified to go RTB through the **RTB** event.
-- --
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land. -- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
-- --
-- ![Engage Event](..\Presentations\AI_CAS\Dia12.JPG) -- ![Engage Event](..\Presentations\AI_CAS\Dia12.JPG)
-- --
@@ -117,7 +117,7 @@
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets. -- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
-- * **@{#AI_CAS_ZONE.Destroy}**: The AI has destroyed a target @{Wrapper.Unit}. -- * **@{#AI_CAS_ZONE.Destroy}**: The AI has destroyed a target @{Wrapper.Unit}.
-- * **@{#AI_CAS_ZONE.Destroyed}**: The AI has destroyed all target @{Wrapper.Unit}s assigned in the CAS task. -- * **@{#AI_CAS_ZONE.Destroyed}**: The AI has destroyed all target @{Wrapper.Unit}s assigned in the CAS task.
-- * **Status**: The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. -- * **Status**: The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
-- --
-- === -- ===
-- --
@@ -460,7 +460,7 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
local CurrentVec2 = self.Controllable:GetVec2() local CurrentVec2 = self.Controllable:GetVec2()
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). --TODO: Create GetAltitude function for GROUP, and delete GetUnit(1).
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() local CurrentAltitude = self.Controllable:GetAltitude()
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
local ToEngageZoneSpeed = self.PatrolMaxSpeed local ToEngageZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:WaypointAir( local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
@@ -520,7 +520,7 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
self:SetRefreshTimeInterval( 2 ) self:SetRefreshTimeInterval( 2 )
self:SetDetectionActivated() self:SetDetectionActivated()
self:__Target( -2 ) -- Start Targetting self:__Target( -2 ) -- Start targeting
end end
end end
+27 -23
View File
@@ -4,7 +4,7 @@
-- --
-- * Patrol AI airplanes within a given zone. -- * Patrol AI airplanes within a given zone.
-- * Trigger detected events when enemy airplanes are detected. -- * Trigger detected events when enemy airplanes are detected.
-- * Manage a fuel treshold to RTB on time. -- * Manage a fuel threshold to RTB on time.
-- --
-- === -- ===
-- --
@@ -16,7 +16,7 @@
-- --
-- === -- ===
-- --
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/PAT%20-%20Patrolling) -- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/PAT%20-%20Patrolling)
-- --
-- === -- ===
-- --
@@ -72,8 +72,8 @@
-- --
-- ![Process](..\Presentations\AI_PATROL\Dia10.JPG) -- ![Process](..\Presentations\AI_PATROL\Dia10.JPG)
-- --
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB. -- Until a fuel or damage threshold has been reached by the AI, or when the AI is commanded to RTB.
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land. -- When the fuel threshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
-- --
-- ![Process](..\Presentations\AI_PATROL\Dia11.JPG) -- ![Process](..\Presentations\AI_PATROL\Dia11.JPG)
-- --
@@ -101,7 +101,7 @@
-- * **RTB** ( Group ): Route the AI to the home base. -- * **RTB** ( Group ): Route the AI to the home base.
-- * **Detect** ( Group ): The AI is detecting targets. -- * **Detect** ( Group ): The AI is detecting targets.
-- * **Detected** ( Group ): The AI has detected new targets. -- * **Detected** ( Group ): The AI has detected new targets.
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. -- * **Status** ( Group ): The AI is checking status (fuel and damage). When the thresholds have been reached, the AI will RTB.
-- --
-- ## 3. Set or Get the AI controllable -- ## 3. Set or Get the AI controllable
-- --
@@ -133,8 +133,8 @@
-- ## 6. Manage the "out of fuel" in the AI_PATROL_ZONE -- ## 6. Manage the "out of fuel" in the AI_PATROL_ZONE
-- --
-- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base. -- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base.
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel treshold is calculated. -- Therefore, with a parameter and a calculation of the distance to the home base, the fuel threshold is calculated.
-- When the fuel treshold is reached, the AI will continue for a given time its patrol task in orbit, -- When the fuel threshold is reached, the AI will continue for a given time its patrol task in orbit,
-- while a new AI is targetted to the AI_PATROL_ZONE. -- while a new AI is targetted to the AI_PATROL_ZONE.
-- Once the time is finished, the old AI will return to the base. -- Once the time is finished, the old AI will return to the base.
-- Use the method @{#AI_PATROL_ZONE.ManageFuel}() to have this proces in place. -- Use the method @{#AI_PATROL_ZONE.ManageFuel}() to have this proces in place.
@@ -142,7 +142,7 @@
-- ## 7. Manage "damage" behaviour of the AI in the AI_PATROL_ZONE -- ## 7. Manage "damage" behaviour of the AI in the AI_PATROL_ZONE
-- --
-- When the AI is damaged, it is required that a new AIControllable is started. However, damage cannon be foreseen early on. -- When the AI is damaged, it is required that a new AIControllable is started. However, damage cannon be foreseen early on.
-- Therefore, when the damage treshold is reached, the AI will return immediately to the home base (RTB). -- Therefore, when the damage threshold is reached, the AI will return immediately to the home base (RTB).
-- Use the method @{#AI_PATROL_ZONE.ManageDamage}() to have this proces in place. -- Use the method @{#AI_PATROL_ZONE.ManageDamage}() to have this proces in place.
-- --
-- === -- ===
@@ -581,11 +581,11 @@ function AI_PATROL_ZONE:ClearDetectedUnits()
end end
--- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base. --- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base.
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel treshold is calculated. -- Therefore, with a parameter and a calculation of the distance to the home base, the fuel threshold is calculated.
-- When the fuel treshold is reached, the AI will continue for a given time its patrol task in orbit, while a new AIControllable is targetted to the AI_PATROL_ZONE. -- When the fuel threshold is reached, the AI will continue for a given time its patrol task in orbit, while a new AIControllable is targetted to the AI_PATROL_ZONE.
-- Once the time is finished, the old AI will return to the base. -- Once the time is finished, the old AI will return to the base.
-- @param #AI_PATROL_ZONE self -- @param #AI_PATROL_ZONE self
-- @param #number PatrolFuelThresholdPercentage The treshold in percentage (between 0 and 1) when the AIControllable is considered to get out of fuel. -- @param #number PatrolFuelThresholdPercentage The threshold in percentage (between 0 and 1) when the AIControllable is considered to get out of fuel.
-- @param #number PatrolOutOfFuelOrbitTime The amount of seconds the out of fuel AIControllable will orbit before returning to the base. -- @param #number PatrolOutOfFuelOrbitTime The amount of seconds the out of fuel AIControllable will orbit before returning to the base.
-- @return #AI_PATROL_ZONE self -- @return #AI_PATROL_ZONE self
function AI_PATROL_ZONE:ManageFuel( PatrolFuelThresholdPercentage, PatrolOutOfFuelOrbitTime ) function AI_PATROL_ZONE:ManageFuel( PatrolFuelThresholdPercentage, PatrolOutOfFuelOrbitTime )
@@ -596,14 +596,14 @@ function AI_PATROL_ZONE:ManageFuel( PatrolFuelThresholdPercentage, PatrolOutOfFu
return self return self
end end
--- When the AI is damaged beyond a certain treshold, it is required that the AI returns to the home base. --- When the AI is damaged beyond a certain threshold, it is required that the AI returns to the home base.
-- However, damage cannot be foreseen early on. -- However, damage cannot be foreseen early on.
-- Therefore, when the damage treshold is reached, -- Therefore, when the damage threshold is reached,
-- the AI will return immediately to the home base (RTB). -- the AI will return immediately to the home base (RTB).
-- Note that for groups, the average damage of the complete group will be calculated. -- Note that for groups, the average damage of the complete group will be calculated.
-- So, in a group of 4 airplanes, 2 lost and 2 with damage 0.2, the damage treshold will be 0.25. -- So, in a group of 4 airplanes, 2 lost and 2 with damage 0.2, the damage threshold will be 0.25.
-- @param #AI_PATROL_ZONE self -- @param #AI_PATROL_ZONE self
-- @param #number PatrolDamageThreshold The treshold in percentage (between 0 and 1) when the AI is considered to be damaged. -- @param #number PatrolDamageThreshold The threshold in percentage (between 0 and 1) when the AI is considered to be damaged.
-- @return #AI_PATROL_ZONE self -- @return #AI_PATROL_ZONE self
function AI_PATROL_ZONE:ManageDamage( PatrolDamageThreshold ) function AI_PATROL_ZONE:ManageDamage( PatrolDamageThreshold )
@@ -726,7 +726,8 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
end end
if self.Controllable:IsAlive() then local life = self.Controllable:GetLife() or 0
if self.Controllable:IsAlive() and life > 1 then
-- Determine if the AIControllable is within the PatrolZone. -- Determine if the AIControllable is within the PatrolZone.
-- If not, make a waypoint within the to that the AIControllable will fly at maximum speed to that point. -- If not, make a waypoint within the to that the AIControllable will fly at maximum speed to that point.
@@ -743,8 +744,9 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
if self.Controllable:InAir() == false then if self.Controllable:InAir() == false then
self:T( "Not in the air, finding route path within PatrolZone" ) self:T( "Not in the air, finding route path within PatrolZone" )
local CurrentVec2 = self.Controllable:GetVec2() local CurrentVec2 = self.Controllable:GetVec2()
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). if not CurrentVec2 then return end
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() --DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
local CurrentAltitude = self.Controllable:GetAltitude()
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
local ToPatrolZoneSpeed = self.PatrolMaxSpeed local ToPatrolZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:WaypointAir( local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
@@ -758,8 +760,9 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
else else
self:T( "In the air, finding route path within PatrolZone" ) self:T( "In the air, finding route path within PatrolZone" )
local CurrentVec2 = self.Controllable:GetVec2() local CurrentVec2 = self.Controllable:GetVec2()
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). if not CurrentVec2 then return end
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() --DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
local CurrentAltitude = self.Controllable:GetAltitude()
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
local ToPatrolZoneSpeed = self.PatrolMaxSpeed local ToPatrolZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:WaypointAir( local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
@@ -870,9 +873,10 @@ function AI_PATROL_ZONE:onafterRTB()
--- Calculate the current route point. --- Calculate the current route point.
local CurrentVec2 = self.Controllable:GetVec2() local CurrentVec2 = self.Controllable:GetVec2()
if not CurrentVec2 then return end
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). --DONE: Create GetAltitude function for GROUP, and delete GetUnit(1).
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() --local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
local CurrentAltitude = self.Controllable:GetAltitude()
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
local ToPatrolZoneSpeed = self.PatrolMaxSpeed local ToPatrolZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:WaypointAir( local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
+2 -2
View File
@@ -194,7 +194,7 @@
-- * is of type `Workmaterials` -- * is of type `Workmaterials`
-- * will report when a carrier is within 500 meters -- * will report when a carrier is within 500 meters
-- * will board to carriers when the carrier is within 500 meters from the cargo object -- * will board to carriers when the carrier is within 500 meters from the cargo object
-- * will dissapear when the cargo is within 25 meters from the carrier during boarding -- * will disappear when the cargo is within 25 meters from the carrier during boarding
-- --
-- So the overall syntax of the #CARGO naming tag and arguments are: -- So the overall syntax of the #CARGO naming tag and arguments are:
-- --
@@ -220,7 +220,7 @@
-- * is of type `Workmaterials` -- * is of type `Workmaterials`
-- * will report when a carrier is within 500 meters -- * will report when a carrier is within 500 meters
-- * will board to carriers when the carrier is within 500 meters from the cargo object -- * will board to carriers when the carrier is within 500 meters from the cargo object
-- * will dissapear when the cargo is within 25 meters from the carrier during boarding -- * will disappear when the cargo is within 25 meters from the carrier during boarding
-- --
-- So the overall syntax of the #CARGO naming tag and arguments are: -- So the overall syntax of the #CARGO naming tag and arguments are:
-- --
+2 -1
View File
@@ -731,13 +731,14 @@ end
-- @param #BASE self -- @param #BASE self
-- @param DCS#Time EventTime The time stamp of the event. -- @param DCS#Time EventTime The time stamp of the event.
-- @param DCS#Object Initiator The initiating object of the event. -- @param DCS#Object Initiator The initiating object of the event.
function BASE:CreateEventCrash( EventTime, Initiator ) function BASE:CreateEventCrash( EventTime, Initiator, IniObjectCategory )
self:F( { EventTime, Initiator } ) self:F( { EventTime, Initiator } )
local Event = { local Event = {
id = world.event.S_EVENT_CRASH, id = world.event.S_EVENT_CRASH,
time = EventTime, time = EventTime,
initiator = Initiator, initiator = Initiator,
IniObjectCategory = IniObjectCategory,
} }
world.onEvent( Event ) world.onEvent( Event )
+133 -95
View File
@@ -16,29 +16,29 @@
--- *In order for the light to shine so brightly, the darkness must be present.* -- Francis Bacon --- *In order for the light to shine so brightly, the darkness must be present.* -- Francis Bacon
-- --
-- After attaching a @{#BEACON} to your @{Wrapper.Positionable#POSITIONABLE}, you need to select the right function to activate the kind of beacon you want. -- After attaching a @{#BEACON} to your @{Wrapper.Positionable#POSITIONABLE}, you need to select the right function to activate the kind of beacon you want.
-- There are two types of BEACONs available : the AA TACAN Beacon and the general purpose Radio Beacon. -- There are two types of BEACONs available : the (aircraft) TACAN Beacon and the general purpose Radio Beacon.
-- Note that in both case, you can set an optional parameter : the `BeaconDuration`. This can be very usefull to simulate the battery time if your BEACON is -- Note that in both case, you can set an optional parameter : the `BeaconDuration`. This can be very useful to simulate the battery time if your BEACON is
-- attach to a cargo crate, for exemple. -- attach to a cargo crate, for exemple.
-- --
-- ## AA TACAN Beacon usage -- ## Aircraft TACAN Beacon usage
-- --
-- This beacon only works with airborne @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}. Use @{#BEACON:AATACAN}() to set the beacon parameters and start the beacon. -- This beacon only works with airborne @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}. Use @{#BEACON.ActivateTACAN}() to set the beacon parameters and start the beacon.
-- Use @#BEACON:StopAATACAN}() to stop it. -- Use @{#BEACON.StopRadioBeacon}() to stop it.
-- --
-- ## General Purpose Radio Beacon usage -- ## General Purpose Radio Beacon usage
-- --
-- This beacon will work with any @{Wrapper.Positionable#POSITIONABLE}, but **it won't follow the @{Wrapper.Positionable#POSITIONABLE}** ! This means that you should only use it with -- This beacon will work with any @{Wrapper.Positionable#POSITIONABLE}, but **it won't follow the @{Wrapper.Positionable#POSITIONABLE}** ! This means that you should only use it with
-- @{Wrapper.Positionable#POSITIONABLE} that don't move, or move very slowly. Use @{#BEACON:RadioBeacon}() to set the beacon parameters and start the beacon. -- @{Wrapper.Positionable#POSITIONABLE} that don't move, or move very slowly. Use @{#BEACON.RadioBeacon}() to set the beacon parameters and start the beacon.
-- Use @{#BEACON:StopRadioBeacon}() to stop it. -- Use @{#BEACON.StopRadioBeacon}() to stop it.
-- --
-- @type BEACON -- @type BEACON
-- @field #string ClassName Name of the class "BEACON". -- @field #string ClassName Name of the class "BEACON".
-- @field Wrapper.Controllable#CONTROLLABLE Positionable The @{#CONTROLLABLE} that will receive radio capabilities. -- @field Wrapper.Controllable#CONTROLLABLE Positionable The @{#CONTROLLABLE} that will receive radio capabilities.
-- @extends Core.Base#BASE -- @extends Core.Base#BASE
BEACON = { BEACON = {
ClassName = "BEACON", ClassName = "BEACON",
Positionable = nil, Positionable = nil,
name = nil, name = nil,
} }
--- Beacon types supported by DCS. --- Beacon types supported by DCS.
@@ -95,13 +95,13 @@ BEACON.Type={
-- @field #number TACAN TACtical Air Navigation system on ground. -- @field #number TACAN TACtical Air Navigation system on ground.
-- @field #number TACAN_TANKER_X TACtical Air Navigation system for tankers on X band. -- @field #number TACAN_TANKER_X TACtical Air Navigation system for tankers on X band.
-- @field #number TACAN_TANKER_Y TACtical Air Navigation system for tankers on Y band. -- @field #number TACAN_TANKER_Y TACtical Air Navigation system for tankers on Y band.
-- @field #number VOR Very High Frequency Omnidirectional Range -- @field #number VOR Very High Frequency Omni-Directional Range
-- @field #number ILS_LOCALIZER ILS localizer -- @field #number ILS_LOCALIZER ILS localizer
-- @field #number ILS_GLIDESLOPE ILS glide slope. -- @field #number ILS_GLIDESLOPE ILS glide slope.
-- @field #number PRGM_LOCALIZER PRGM localizer. -- @field #number PRGM_LOCALIZER PRGM localizer.
-- @field #number PRGM_GLIDESLOPE PRGM glide slope. -- @field #number PRGM_GLIDESLOPE PRGM glide slope.
-- @field #number BROADCAST_STATION Broadcast station. -- @field #number BROADCAST_STATION Broadcast station.
-- @field #number VORTAC Radio-based navigational aid for aircraft pilots consisting of a co-located VHF omnidirectional range (VOR) beacon and a tactical air navigation system (TACAN) beacon. -- @field #number VORTAC Radio-based navigational aid for aircraft pilots consisting of a co-located VHF omni-directional range (VOR) beacon and a tactical air navigation system (TACAN) beacon.
-- @field #number TACAN_AA_MODE_X TACtical Air Navigation for aircraft on X band. -- @field #number TACAN_AA_MODE_X TACtical Air Navigation for aircraft on X band.
-- @field #number TACAN_AA_MODE_Y TACtical Air Navigation for aircraft on Y band. -- @field #number TACAN_AA_MODE_Y TACtical Air Navigation for aircraft on Y band.
-- @field #number VORDME Radio beacon that combines a VHF omnidirectional range (VOR) with a distance measuring equipment (DME). -- @field #number VORDME Radio beacon that combines a VHF omnidirectional range (VOR) with a distance measuring equipment (DME).
@@ -131,27 +131,28 @@ BEACON.System={
-- If you want to create a BEACON, you probably should use @{Wrapper.Positionable#POSITIONABLE.GetBeacon}() instead. -- If you want to create a BEACON, you probably should use @{Wrapper.Positionable#POSITIONABLE.GetBeacon}() instead.
-- @param #BEACON self -- @param #BEACON self
-- @param Wrapper.Positionable#POSITIONABLE Positionable The @{Positionable} that will receive radio capabilities. -- @param Wrapper.Positionable#POSITIONABLE Positionable The @{Positionable} that will receive radio capabilities.
-- @return #BEACON Beacon object or #nil if the POSITIONABLE is invalid. -- @return #BEACON Beacon object or #nil if the positionable is invalid.
function BEACON:New( Positionable ) function BEACON:New(Positionable)
-- Inherit BASE. -- Inherit BASE.
local self = BASE:Inherit( self, BASE:New() ) -- #BEACON local self=BASE:Inherit(self, BASE:New()) --#BEACON
-- Debug. -- Debug.
self:F( Positionable ) self:F(Positionable)
-- Set positionable. -- Set positionable.
if Positionable:GetPointVec2() then -- It's stupid, but the only way I found to make sure POSITIONABLE is valid if Positionable:GetPointVec2() then -- It's stupid, but the only way I found to make sure positionable is valid
self.Positionable = Positionable self.Positionable = Positionable
self.name = Positionable:GetName() self.name=Positionable:GetName()
self:I( string.format( "New BEACON %s", tostring( self.name ) ) ) self:I(string.format("New BEACON %s", tostring(self.name)))
return self return self
end end
self:E( { "The passed POSITIONABLE is invalid, no BEACON created", Positionable } ) self:E({"The passed positionable is invalid, no BEACON created", Positionable})
return nil return nil
end end
--- Activates a TACAN BEACON. --- Activates a TACAN BEACON.
-- @param #BEACON self -- @param #BEACON self
-- @param #number Channel TACAN channel, i.e. the "10" part in "10Y". -- @param #number Channel TACAN channel, i.e. the "10" part in "10Y".
@@ -161,53 +162,58 @@ end
-- @param #number Duration How long will the beacon last in seconds. Omit for forever. -- @param #number Duration How long will the beacon last in seconds. Omit for forever.
-- @return #BEACON self -- @return #BEACON self
-- @usage -- @usage
--
-- -- Let's create a TACAN Beacon for a tanker -- -- Let's create a TACAN Beacon for a tanker
-- local myUnit = UNIT:FindByName("MyUnit") -- local myUnit = UNIT:FindByName("MyUnit")
-- local myBeacon = myUnit:GetBeacon() -- Creates the beacon -- local myBeacon = myUnit:GetBeacon() -- Creates the beacon
-- --
-- myBeacon:ActivateTACAN(20, "Y", "TEXACO", true) -- Activate the beacon -- myBeacon:ActivateTACAN(20, "Y", "TEXACO", true) -- Activate the beacon
-- function BEACON:ActivateTACAN(Channel, Mode, Message, Bearing, Duration)
function BEACON:ActivateTACAN( Channel, Mode, Message, Bearing, Duration ) self:T({channel=Channel, mode=Mode, callsign=Message, bearing=Bearing, duration=Duration})
self:T( { channel = Channel, mode = Mode, callsign = Message, bearing = Bearing, duration = Duration } )
Mode=Mode or "Y"
-- Get frequency. -- Get frequency.
local Frequency = UTILS.TACANToFrequency( Channel, Mode ) local Frequency=UTILS.TACANToFrequency(Channel, Mode)
-- Check. -- Check.
if not Frequency then if not Frequency then
self:E( { "The passed TACAN channel is invalid, the BEACON is not emitting" } ) self:E({"The passed TACAN channel is invalid, the BEACON is not emitting"})
return self return self
end end
-- Beacon type. -- Beacon type.
local Type = BEACON.Type.TACAN local Type=BEACON.Type.TACAN
-- Beacon system. -- Beacon system.
local System = BEACON.System.TACAN local System=BEACON.System.TACAN
-- Check if unit is an aircraft and set system accordingly. -- Check if unit is an aircraft and set system accordingly.
local AA = self.Positionable:IsAir() local AA=self.Positionable:IsAir()
if AA then if AA then
System = 5 -- NOTE: 5 is how you cat the correct tanker behaviour! --BEACON.System.TACAN_TANKER System=5 --NOTE: 5 is how you cat the correct tanker behaviour! --BEACON.System.TACAN_TANKER
-- Check if "Y" mode is selected for aircraft. -- Check if "Y" mode is selected for aircraft.
if Mode ~= "Y" then if Mode=="X" then
self:E( { "WARNING: The POSITIONABLE you want to attach the AA TACAN Beacon is an aircraft: Mode should Y! The BEACON is not emitting.", self.Positionable } ) --self:E({"WARNING: The POSITIONABLE you want to attach the AA Tacan Beacon is an aircraft: Mode should Y!", self.Positionable})
System=BEACON.System.TACAN_TANKER_X
else
System=BEACON.System.TACAN_TANKER_Y
end end
end end
-- Attached unit. -- Attached unit.
local UnitID = self.Positionable:GetID() local UnitID=self.Positionable:GetID()
-- Debug. -- 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 ) ) } ) 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. -- Start beacon.
self.Positionable:CommandActivateBeacon( Type, System, Frequency, UnitID, Channel, Mode, AA, Message, Bearing ) self.Positionable:CommandActivateBeacon(Type, System, Frequency, UnitID, Channel, Mode, AA, Message, Bearing)
-- Stop scheduler. -- Stop scheduler.
if Duration then if Duration then
self.Positionable:DeactivateBeacon( Duration ) self.Positionable:DeactivateBeacon(Duration)
end end
return self return self
@@ -219,27 +225,55 @@ end
-- @param #string Callsign The Message that is going to be coded in Morse and broadcasted by the beacon. -- @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. -- @param #number Duration How long will the beacon last in seconds. Omit for forever.
-- @return #BEACON self -- @return #BEACON self
function BEACON:ActivateICLS( Channel, Callsign, Duration ) function BEACON:ActivateICLS(Channel, Callsign, Duration)
self:F( { Channel = Channel, Callsign = Callsign, Duration = Duration } ) self:F({Channel=Channel, Callsign=Callsign, Duration=Duration})
-- Attached unit. -- Attached unit.
local UnitID = self.Positionable:GetID() local UnitID=self.Positionable:GetID()
-- Debug -- Debug
self:T2( { "ICLS BEACON started!" } ) self:T2({"ICLS BEACON started!"})
-- Start beacon. -- Start beacon.
self.Positionable:CommandActivateICLS( Channel, UnitID, Callsign ) self.Positionable:CommandActivateICLS(Channel, UnitID, Callsign)
-- Stop scheduler -- Stop scheduler
if Duration then -- Schedule the stop of the BEACON if asked by the MD if Duration then -- Schedule the stop of the BEACON if asked by the MD
self.Positionable:DeactivateBeacon( Duration ) self.Positionable:DeactivateBeacon(Duration)
end end
return self return self
end end
--- Activates a TACAN BEACON on an Aircraft. --- Activates a LINK4 BEACON. The unit the BEACON is attached to should be an aircraft carrier supporting this system.
-- @param #BEACON self
-- @param #number Frequency LINK4 FRequency in MHz, eg 336.
-- @param #string Morse The ID that is going to be coded in Morse and broadcasted by the beacon.
-- @param #number Duration How long will the beacon last in seconds. Omit for forever.
-- @return #BEACON self
function BEACON:ActivateLink4(Frequency, Morse, Duration)
self:F({Frequency=Frequency, Morse=Morse, Duration=Duration})
-- Attached unit.
local UnitID=self.Positionable:GetID()
-- Debug
self:T2({"LINK4 BEACON started!"})
-- Start beacon.
self.Positionable:CommandActivateLink4(Frequency,UnitID,Morse)
-- Stop sheduler
if Duration then -- Schedule the stop of the BEACON if asked by the MD
self.Positionable:DeactivateLink4(Duration)
end
return self
end
--- DEPRECATED: Please use @{BEACON:ActivateTACAN}() instead.
-- Activates a TACAN BEACON on an Aircraft.
-- @param #BEACON self -- @param #BEACON self
-- @param #number TACANChannel (the "10" part in "10Y"). Note that AA TACAN are only available on Y Channels -- @param #number TACANChannel (the "10" part in "10Y"). Note that AA TACAN are only available on Y Channels
-- @param #string Message The Message that is going to be coded in Morse and broadcasted by the beacon -- @param #string Message The Message that is going to be coded in Morse and broadcasted by the beacon
@@ -247,54 +281,55 @@ end
-- @param #number BeaconDuration How long will the beacon last in seconds. Omit for forever. -- @param #number BeaconDuration How long will the beacon last in seconds. Omit for forever.
-- @return #BEACON self -- @return #BEACON self
-- @usage -- @usage
--
-- -- Let's create a TACAN Beacon for a tanker -- -- Let's create a TACAN Beacon for a tanker
-- local myUnit = UNIT:FindByName("MyUnit") -- local myUnit = UNIT:FindByName("MyUnit")
-- local myBeacon = myUnit:GetBeacon() -- Creates the beacon -- local myBeacon = myUnit:GetBeacon() -- Creates the beacon
-- --
-- myBeacon:AATACAN(20, "TEXACO", true) -- Activate the beacon -- myBeacon:AATACAN(20, "TEXACO", true) -- Activate the beacon
-- function BEACON:AATACAN(TACANChannel, Message, Bearing, BeaconDuration)
function BEACON:AATACAN( TACANChannel, Message, Bearing, BeaconDuration ) self:F({TACANChannel, Message, Bearing, BeaconDuration})
self:F( { TACANChannel, Message, Bearing, BeaconDuration } )
local IsValid = true local IsValid = true
if not self.Positionable:IsAir() then if not self.Positionable:IsAir() then
self:E( { "The POSITIONABLE you want to attach the AA TACAN Beacon is not an aircraft! The BEACON is not emitting", self.Positionable } ) self:E({"The POSITIONABLE you want to attach the AA Tacan Beacon is not an aircraft ! The BEACON is not emitting", self.Positionable})
IsValid = false IsValid = false
end end
local Frequency = self:_TACANToFrequency( TACANChannel, "Y" ) local Frequency = self:_TACANToFrequency(TACANChannel, "Y")
if not Frequency then if not Frequency then
self:E( { "The passed TACAN channel is invalid, the BEACON is not emitting" } ) self:E({"The passed TACAN channel is invalid, the BEACON is not emitting"})
IsValid = false IsValid = false
end end
-- I'm using the beacon type 4 (BEACON_TYPE_TACAN). For System, I'm using 5 (TACAN_TANKER_MODE_Y) if the bearing shows its bearing -- I'm using the beacon type 4 (BEACON_TYPE_TACAN). For System, I'm using 5 (TACAN_TANKER_MODE_Y) if the bearing shows its bearing or 14 (TACAN_AA_MODE_Y) if it does not
-- or 14 (TACAN_AA_MODE_Y) if it does not
local System local System
if Bearing then if Bearing then
System = 5 System = BEACON.System.TACAN_TANKER_Y
else else
System = 14 System = BEACON.System.TACAN_AA_MODE_Y
end end
if IsValid then -- Starts the BEACON if IsValid then -- Starts the BEACON
self:T2( { "AA TACAN BEACON started !" } ) self:T2({"AA TACAN BEACON started !"})
self.Positionable:SetCommand( { self.Positionable:SetCommand({
id = "ActivateBeacon", id = "ActivateBeacon",
params = { params = {
type = 4, type = BEACON.Type.TACAN,
system = System, system = System,
callsign = Message, callsign = Message,
AA = true,
frequency = Frequency, frequency = Frequency,
}, bearing = Bearing,
} ) modeChannel = "Y",
}
})
if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD
SCHEDULER:New( nil, function() SCHEDULER:New(nil,
function()
self:StopAATACAN() self:StopAATACAN()
end, {}, BeaconDuration ) end, {}, BeaconDuration)
end end
end end
@@ -307,19 +342,21 @@ end
function BEACON:StopAATACAN() function BEACON:StopAATACAN()
self:F() self:F()
if not self.Positionable then if not self.Positionable then
self:E( { "Start the beacon first before stopping it!" } ) self:E({"Start the beacon first before stoping it !"})
else else
self.Positionable:SetCommand( { self.Positionable:SetCommand({
id = 'DeactivateBeacon', id = 'DeactivateBeacon',
params = {}, params = {
} ) }
})
end end
end end
--- Activates a general purpose Radio Beacon --- Activates a general purpose Radio Beacon
-- This uses the very generic singleton function "trigger.action.radioTransmission()" provided by DCS to broadcast a sound file on a specific frequency. -- This uses the very generic singleton function "trigger.action.radioTransmission()" provided by DCS to broadcast a sound file on a specific frequency.
-- Although any frequency could be used, only 2 DCS Modules can home on radio beacons at the time of writing : the Huey and the Mi-8. -- Although any frequency could be used, only a few DCS Modules can home on radio beacons at the time of writing, i.e. the Mi-8, Huey, Gazelle etc.
-- They can home in on these specific frequencies : -- The following e.g. can home in on these specific frequencies :
-- * **Mi8** -- * **Mi8**
-- * R-828 -> 20-60MHz -- * R-828 -> 20-60MHz
-- * ARKUD -> 100-150MHz (canal 1 : 114166, canal 2 : 114333, canal 3 : 114583, canal 4 : 121500, canal 5 : 123100, canal 6 : 124100) AM -- * ARKUD -> 100-150MHz (canal 1 : 114166, canal 2 : 114333, canal 3 : 114583, canal 4 : 121500, canal 5 : 123100, canal 6 : 124100) AM
@@ -342,63 +379,64 @@ end
-- --
-- -- Set the beacon and start it -- -- Set the beacon and start it
-- UnitBeacon:RadioBeacon("MySoundFileSOS.ogg", 40, radio.modulation.FM, 20, 5*60) -- UnitBeacon:RadioBeacon("MySoundFileSOS.ogg", 40, radio.modulation.FM, 20, 5*60)
function BEACON:RadioBeacon( FileName, Frequency, Modulation, Power, BeaconDuration ) function BEACON:RadioBeacon(FileName, Frequency, Modulation, Power, BeaconDuration)
self:F( { FileName, Frequency, Modulation, Power, BeaconDuration } ) self:F({FileName, Frequency, Modulation, Power, BeaconDuration})
local IsValid = false local IsValid = false
-- Check the filename -- Check the filename
if type( FileName ) == "string" then if type(FileName) == "string" then
if FileName:find( ".ogg" ) or FileName:find( ".wav" ) then if FileName:find(".ogg") or FileName:find(".wav") then
if not FileName:find( "l10n/DEFAULT/" ) then if not FileName:find("l10n/DEFAULT/") then
FileName = "l10n/DEFAULT/" .. FileName FileName = "l10n/DEFAULT/" .. FileName
end end
IsValid = true IsValid = true
end end
end end
if not IsValid then if not IsValid then
self:E( { "File name invalid. Maybe something wrong with the extension? ", FileName } ) self:E({"File name invalid. Maybe something wrong with the extension ? ", FileName})
end end
-- Check the Frequency -- Check the Frequency
if type( Frequency ) ~= "number" and IsValid then if type(Frequency) ~= "number" and IsValid then
self:E( { "Frequency invalid. ", Frequency } ) self:E({"Frequency invalid. ", Frequency})
IsValid = false IsValid = false
end end
Frequency = Frequency * 1000000 -- Conversion to Hz Frequency = Frequency * 1000000 -- Conversion to Hz
-- Check the modulation -- Check the modulation
if Modulation ~= radio.modulation.AM and Modulation ~= radio.modulation.FM and IsValid then -- TODO: Maybe make this future proof if ED decides to add an other modulation ? if Modulation ~= radio.modulation.AM and Modulation ~= radio.modulation.FM and IsValid then --TODO Maybe make this future proof if ED decides to add an other modulation ?
self:E( { "Modulation is invalid. Use DCS's enum radio.modulation.", Modulation } ) self:E({"Modulation is invalid. Use DCS's enum radio.modulation.", Modulation})
IsValid = false IsValid = false
end end
-- Check the Power -- Check the Power
if type( Power ) ~= "number" and IsValid then if type(Power) ~= "number" and IsValid then
self:E( { "Power is invalid. ", Power } ) self:E({"Power is invalid. ", Power})
IsValid = false IsValid = false
end end
Power = math.floor( math.abs( Power ) ) -- TODO: Find what is the maximum power allowed by DCS and limit power to that Power = math.floor(math.abs(Power)) --TODO Find what is the maximum power allowed by DCS and limit power to that
if IsValid then if IsValid then
self:T2( { "Activating Beacon on ", Frequency, Modulation } ) self:T2({"Activating Beacon on ", Frequency, Modulation})
-- Note that this is looped. I have to give this transmission a unique name, I use the class ID -- 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 ) ) trigger.action.radioTransmission(FileName, self.Positionable:GetPositionVec3(), Modulation, true, Frequency, Power, tostring(self.ID))
if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD
SCHEDULER:New( nil, function() SCHEDULER:New( nil,
self:StopRadioBeacon() function()
end, {}, BeaconDuration ) self:StopRadioBeacon()
end end, {}, BeaconDuration)
end
end end
end end
--- Stops the AA TACAN BEACON --- Stop the Radio Beacon
-- @param #BEACON self -- @param #BEACON self
-- @return #BEACON self -- @return #BEACON self
function BEACON:StopRadioBeacon() function BEACON:StopRadioBeacon()
self:F() self:F()
-- The unique name of the transmission is the class ID -- The unique name of the transmission is the class ID
trigger.action.stopRadioTransmission( tostring( self.ID ) ) trigger.action.stopRadioTransmission(tostring(self.ID))
return self return self
end end
@@ -406,21 +444,21 @@ end
-- @param #BEACON self -- @param #BEACON self
-- @param #number TACANChannel -- @param #number TACANChannel
-- @param #string TACANMode -- @param #string TACANMode
-- @return #number Frequency -- @return #number Frequecy
-- @return #nil if parameters are invalid -- @return #nil if parameters are invalid
function BEACON:_TACANToFrequency( TACANChannel, TACANMode ) function BEACON:_TACANToFrequency(TACANChannel, TACANMode)
self:F3( { TACANChannel, TACANMode } ) self:F3({TACANChannel, TACANMode})
if type( TACANChannel ) ~= "number" then if type(TACANChannel) ~= "number" then
if TACANMode ~= "X" and TACANMode ~= "Y" then if TACANMode ~= "X" and TACANMode ~= "Y" then
return nil -- error in arguments return nil -- error in arguments
end end
end end
-- This code is largely based on ED's code, in DCS World\Scripts\World\Radio\BeaconTypes.lua, line 137. -- 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 -- I have no idea what it does but it seems to work
local A = 1151 -- 'X', channel >= 64 local A = 1151 -- 'X', channel >= 64
local B = 64 -- channel >= 64 local B = 64 -- channel >= 64
if TACANChannel < 64 then if TACANChannel < 64 then
B = 1 B = 1
+8 -15
View File
@@ -241,16 +241,6 @@ function DATABASE:DeleteAirbase( AirbaseName )
self.AIRBASES[AirbaseName] = nil self.AIRBASES[AirbaseName] = nil
end end
--- Finds an AIRBASE based on the AirbaseName.
-- @param #DATABASE self
-- @param #string AirbaseName
-- @return Wrapper.Airbase#AIRBASE The found AIRBASE.
function DATABASE:FindAirbase( AirbaseName )
local AirbaseFound = self.AIRBASES[AirbaseName]
return AirbaseFound
end
do -- Zones do -- Zones
--- Finds a @{Zone} based on the zone name. --- Finds a @{Zone} based on the zone name.
@@ -757,7 +747,9 @@ function DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, Category
local StaticTemplate = UTILS.DeepCopy( StaticTemplate ) local StaticTemplate = UTILS.DeepCopy( StaticTemplate )
local StaticTemplateName = env.getValueDictByKey( StaticTemplate.name ) local StaticTemplateGroupName = env.getValueDictByKey( StaticTemplate.name )
local StaticTemplateName=StaticTemplate.units[1].name
self.Templates.Statics[StaticTemplateName] = self.Templates.Statics[StaticTemplateName] or {} self.Templates.Statics[StaticTemplateName] = self.Templates.Statics[StaticTemplateName] or {}
@@ -765,7 +757,7 @@ function DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, Category
StaticTemplate.CoalitionID = CoalitionID StaticTemplate.CoalitionID = CoalitionID
StaticTemplate.CountryID = CountryID StaticTemplate.CountryID = CountryID
self.Templates.Statics[StaticTemplateName].StaticName = StaticTemplateName self.Templates.Statics[StaticTemplateName].StaticName = StaticTemplateGroupName
self.Templates.Statics[StaticTemplateName].GroupTemplate = StaticTemplate self.Templates.Statics[StaticTemplateName].GroupTemplate = StaticTemplate
self.Templates.Statics[StaticTemplateName].UnitTemplate = StaticTemplate.units[1] self.Templates.Statics[StaticTemplateName].UnitTemplate = StaticTemplate.units[1]
self.Templates.Statics[StaticTemplateName].CategoryID = CategoryID self.Templates.Statics[StaticTemplateName].CategoryID = CategoryID
@@ -903,7 +895,7 @@ function DATABASE:_RegisterPlayers()
return self return self
end end
--- Private method that registers all Groups and Units within in the mission. --- Private method that registers all Groups and Units within the mission.
-- @param #DATABASE self -- @param #DATABASE self
-- @return #DATABASE self -- @return #DATABASE self
function DATABASE:_RegisterGroupsAndUnits() function DATABASE:_RegisterGroupsAndUnits()
@@ -944,7 +936,7 @@ function DATABASE:_RegisterGroupsAndUnits()
return self return self
end end
--- Private method that registers all Units of skill Client or Player within in the mission. --- Private method that registers all Units of skill Client or Player within the mission.
-- @param #DATABASE self -- @param #DATABASE self
-- @return #DATABASE self -- @return #DATABASE self
function DATABASE:_RegisterClients() function DATABASE:_RegisterClients()
@@ -957,7 +949,8 @@ function DATABASE:_RegisterClients()
return self return self
end end
--- @param #DATABASE self --- Private method that registers all Statics within the mission.
-- @param #DATABASE self
function DATABASE:_RegisterStatics() function DATABASE:_RegisterStatics()
local CoalitionsData = { GroupsRed = coalition.getStaticObjects( coalition.side.RED ), GroupsBlue = coalition.getStaticObjects( coalition.side.BLUE ), GroupsNeutral = coalition.getStaticObjects( coalition.side.NEUTRAL ) } local CoalitionsData = { GroupsRed = coalition.getStaticObjects( coalition.side.RED ), GroupsBlue = coalition.getStaticObjects( coalition.side.BLUE ), GroupsNeutral = coalition.getStaticObjects( coalition.side.NEUTRAL ) }
+171 -143
View File
@@ -32,11 +32,11 @@
-- --
-- ![Objects](..\Presentations\EVENT\Dia5.JPG) -- ![Objects](..\Presentations\EVENT\Dia5.JPG)
-- --
-- There are 5 levels of kind of objects that the _EVENTDISPATCHER services: -- There are 5 types/levels of objects that the _EVENTDISPATCHER services:
-- --
-- * _DATABASE object: The core of the MOOSE objects. Any object that is created, deleted or updated, is done in this database. -- * _DATABASE object: The core of the MOOSE objects. Any object that is created, deleted or updated, is done in this database.
-- * SET_ derived classes: Subsets of the _DATABASE object. These subsets are updated by the _EVENTDISPATCHER as the second priority. -- * SET_ derived classes: These are subsets of the _DATABASE object. These subsets are updated by the _EVENTDISPATCHER as the second priority.
-- * UNIT objects: UNIT objects can subscribe to DCS events. Each DCS event will be directly published to teh subscribed UNIT object. -- * UNIT objects: UNIT objects can subscribe to DCS events. Each DCS event will be directly published to the subscribed UNIT object.
-- * GROUP objects: GROUP objects can subscribe to DCS events. Each DCS event will be directly published to the subscribed GROUP object. -- * GROUP objects: GROUP objects can subscribe to DCS events. Each DCS event will be directly published to the subscribed GROUP object.
-- * Any other object: Various other objects can subscribe to DCS events. Each DCS event triggered will be published to each subscribed object. -- * Any other object: Various other objects can subscribe to DCS events. Each DCS event triggered will be published to each subscribed object.
-- --
@@ -141,6 +141,7 @@
-- EventData.IniUnit:SmokeGreen() -- EventData.IniUnit:SmokeGreen()
-- end -- end
-- --
--
-- Find below an overview which events populate which information categories: -- Find below an overview which events populate which information categories:
-- --
-- ![Objects](..\Presentations\EVENT\Dia14.JPG) -- ![Objects](..\Presentations\EVENT\Dia14.JPG)
@@ -171,6 +172,7 @@
-- @module Core.Event -- @module Core.Event
-- @image Core_Event.JPG -- @image Core_Event.JPG
--- @type EVENT --- @type EVENT
-- @field #EVENT.Events Events -- @field #EVENT.Events Events
-- @extends Core.Base#BASE -- @extends Core.Base#BASE
@@ -192,6 +194,7 @@ world.event.S_EVENT_DELETE_ZONE_GOAL = world.event.S_EVENT_MAX + 1005
world.event.S_EVENT_REMOVE_UNIT = world.event.S_EVENT_MAX + 1006 world.event.S_EVENT_REMOVE_UNIT = world.event.S_EVENT_MAX + 1006
world.event.S_EVENT_PLAYER_ENTER_AIRCRAFT = world.event.S_EVENT_MAX + 1007 world.event.S_EVENT_PLAYER_ENTER_AIRCRAFT = world.event.S_EVENT_MAX + 1007
--- The different types of events supported by MOOSE. --- The different types of events supported by MOOSE.
-- Use this structure to subscribe to events using the @{Core.Base#BASE.HandleEvent}() method. -- Use this structure to subscribe to events using the @{Core.Base#BASE.HandleEvent}() method.
-- @type EVENTS -- @type EVENTS
@@ -224,13 +227,13 @@ EVENTS = {
MarkChange = world.event.S_EVENT_MARK_CHANGE, MarkChange = world.event.S_EVENT_MARK_CHANGE,
MarkRemoved = world.event.S_EVENT_MARK_REMOVED, MarkRemoved = world.event.S_EVENT_MARK_REMOVED,
-- Moose Events -- Moose Events
NewCargo = world.event.S_EVENT_NEW_CARGO, NewCargo = world.event.S_EVENT_NEW_CARGO,
DeleteCargo = world.event.S_EVENT_DELETE_CARGO, DeleteCargo = world.event.S_EVENT_DELETE_CARGO,
NewZone = world.event.S_EVENT_NEW_ZONE, NewZone = world.event.S_EVENT_NEW_ZONE,
DeleteZone = world.event.S_EVENT_DELETE_ZONE, DeleteZone = world.event.S_EVENT_DELETE_ZONE,
NewZoneGoal = world.event.S_EVENT_NEW_ZONE_GOAL, NewZoneGoal = world.event.S_EVENT_NEW_ZONE_GOAL,
DeleteZoneGoal = world.event.S_EVENT_DELETE_ZONE_GOAL, DeleteZoneGoal = world.event.S_EVENT_DELETE_ZONE_GOAL,
RemoveUnit = world.event.S_EVENT_REMOVE_UNIT, RemoveUnit = world.event.S_EVENT_REMOVE_UNIT,
PlayerEnterAircraft = world.event.S_EVENT_PLAYER_ENTER_AIRCRAFT, PlayerEnterAircraft = world.event.S_EVENT_PLAYER_ENTER_AIRCRAFT,
-- Added with DCS 2.5.6 -- Added with DCS 2.5.6
DetailedFailure = world.event.S_EVENT_DETAILED_FAILURE or -1, --We set this to -1 for backward compatibility to DCS 2.5.5 and earlier DetailedFailure = world.event.S_EVENT_DETAILED_FAILURE or -1, --We set this to -1 for backward compatibility to DCS 2.5.5 and earlier
@@ -301,6 +304,8 @@ EVENTS = {
-- @field Core.ZONE#ZONE Zone The zone object. -- @field Core.ZONE#ZONE Zone The zone object.
-- @field #string ZoneName The name of the zone. -- @field #string ZoneName The name of the zone.
local _EVENTMETA = { local _EVENTMETA = {
[world.event.S_EVENT_SHOT] = { [world.event.S_EVENT_SHOT] = {
Order = 1, Order = 1,
@@ -557,6 +562,7 @@ local _EVENTMETA = {
}, },
} }
--- The Events structure --- The Events structure
-- @type EVENT.Events -- @type EVENT.Events
-- @field #number IniUnit -- @field #number IniUnit
@@ -570,11 +576,12 @@ function EVENT:New()
local self = BASE:Inherit( self, BASE:New() ) local self = BASE:Inherit( self, BASE:New() )
-- Add world event handler. -- Add world event handler.
self.EventHandler = world.addEventHandler( self ) self.EventHandler = world.addEventHandler(self)
return self return self
end end
--- Initializes the Events structure for the event. --- Initializes the Events structure for the event.
-- @param #EVENT self -- @param #EVENT self
-- @param DCS#world.event EventID Event ID. -- @param DCS#world.event EventID Event ID.
@@ -588,7 +595,7 @@ function EVENT:Init( EventID, EventClass )
self.Events[EventID] = {} self.Events[EventID] = {}
end end
-- Each event has a sub-table of EventClasses, ordered by EventPriority. -- Each event has a subtable of EventClasses, ordered by EventPriority.
local EventPriority = EventClass:GetEventPriority() local EventPriority = EventClass:GetEventPriority()
if not self.Events[EventID][EventPriority] then if not self.Events[EventID][EventPriority] then
@@ -596,7 +603,7 @@ function EVENT:Init( EventID, EventClass )
end end
if not self.Events[EventID][EventPriority][EventClass] then if not self.Events[EventID][EventPriority][EventClass] then
self.Events[EventID][EventPriority][EventClass] = {} self.Events[EventID][EventPriority][EventClass] = {}
end end
return self.Events[EventID][EventPriority][EventClass] return self.Events[EventID][EventPriority][EventClass]
@@ -607,7 +614,7 @@ end
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is. -- @param Core.Base#BASE EventClass The self instance of the class for which the event is.
-- @param DCS#world.event EventID Event ID. -- @param DCS#world.event EventID Event ID.
-- @return #EVENT self -- @return #EVENT self
function EVENT:RemoveEvent( EventClass, EventID ) function EVENT:RemoveEvent( EventClass, EventID )
-- Debug info. -- Debug info.
self:F2( { "Removing subscription for class: ", EventClass:GetClassNameAndID() } ) self:F2( { "Removing subscription for class: ", EventClass:GetClassNameAndID() } )
@@ -631,7 +638,7 @@ end
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is. -- @param Core.Base#BASE EventClass The self instance of the class for which the event is.
-- @param DCS#world.event EventID Event ID. -- @param DCS#world.event EventID Event ID.
-- @return #EVENT.Events -- @return #EVENT.Events
function EVENT:Reset( EventObject ) -- R2.1 function EVENT:Reset( EventObject ) --R2.1
self:F( { "Resetting subscriptions for class: ", EventObject:GetClassNameAndID() } ) self:F( { "Resetting subscriptions for class: ", EventObject:GetClassNameAndID() } )
@@ -650,11 +657,12 @@ function EVENT:Reset( EventObject ) -- R2.1
end end
end end
--- Clears all event subscriptions for a @{Core.Base#BASE} derived object. --- Clears all event subscriptions for a @{Core.Base#BASE} derived object.
-- @param #EVENT self -- @param #EVENT self
-- @param Core.Base#BASE EventClass The self class object for which the events are removed. -- @param Core.Base#BASE EventClass The self class object for which the events are removed.
-- @return #EVENT self -- @return #EVENT self
function EVENT:RemoveAll( EventClass ) function EVENT:RemoveAll(EventClass)
local EventClassName = EventClass:GetClassNameAndID() local EventClassName = EventClass:GetClassNameAndID()
@@ -668,6 +676,8 @@ function EVENT:RemoveAll( EventClass )
return self return self
end end
--- Create an OnDead event handler for a group --- Create an OnDead event handler for a group
-- @param #EVENT self -- @param #EVENT self
-- @param #table EventTemplate -- @param #table EventTemplate
@@ -699,6 +709,7 @@ function EVENT:OnEventGeneric( EventFunction, EventClass, EventID )
return self return self
end end
--- Set a new listener for an `S_EVENT_X` event for a UNIT. --- Set a new listener for an `S_EVENT_X` event for a UNIT.
-- @param #EVENT self -- @param #EVENT self
-- @param #string UnitName The name of the UNIT. -- @param #string UnitName The name of the UNIT.
@@ -786,6 +797,7 @@ do -- OnDead
end end
do -- OnLand do -- OnLand
--- Create an OnLand event handler for a group --- Create an OnLand event handler for a group
@@ -852,7 +864,7 @@ do -- Event Creation
id = EVENTS.NewCargo, id = EVENTS.NewCargo,
time = timer.getTime(), time = timer.getTime(),
cargo = Cargo, cargo = Cargo,
} }
world.onEvent( Event ) world.onEvent( Event )
end end
@@ -867,7 +879,7 @@ do -- Event Creation
id = EVENTS.DeleteCargo, id = EVENTS.DeleteCargo,
time = timer.getTime(), time = timer.getTime(),
cargo = Cargo, cargo = Cargo,
} }
world.onEvent( Event ) world.onEvent( Event )
end end
@@ -882,7 +894,7 @@ do -- Event Creation
id = EVENTS.NewZone, id = EVENTS.NewZone,
time = timer.getTime(), time = timer.getTime(),
zone = Zone, zone = Zone,
} }
world.onEvent( Event ) world.onEvent( Event )
end end
@@ -897,7 +909,7 @@ do -- Event Creation
id = EVENTS.DeleteZone, id = EVENTS.DeleteZone,
time = timer.getTime(), time = timer.getTime(),
zone = Zone, zone = Zone,
} }
world.onEvent( Event ) world.onEvent( Event )
end end
@@ -912,11 +924,12 @@ do -- Event Creation
id = EVENTS.NewZoneGoal, id = EVENTS.NewZoneGoal,
time = timer.getTime(), time = timer.getTime(),
ZoneGoal = ZoneGoal, ZoneGoal = ZoneGoal,
} }
world.onEvent( Event ) world.onEvent( Event )
end end
--- Creation of a ZoneGoal Deletion Event. --- Creation of a ZoneGoal Deletion Event.
-- @param #EVENT self -- @param #EVENT self
-- @param Core.ZoneGoal#ZONE_GOAL ZoneGoal The ZoneGoal created. -- @param Core.ZoneGoal#ZONE_GOAL ZoneGoal The ZoneGoal created.
@@ -927,11 +940,12 @@ do -- Event Creation
id = EVENTS.DeleteZoneGoal, id = EVENTS.DeleteZoneGoal,
time = timer.getTime(), time = timer.getTime(),
ZoneGoal = ZoneGoal, ZoneGoal = ZoneGoal,
} }
world.onEvent( Event ) world.onEvent( Event )
end end
--- Creation of a S_EVENT_PLAYER_ENTER_UNIT Event. --- Creation of a S_EVENT_PLAYER_ENTER_UNIT Event.
-- @param #EVENT self -- @param #EVENT self
-- @param Wrapper.Unit#UNIT PlayerUnit. -- @param Wrapper.Unit#UNIT PlayerUnit.
@@ -941,8 +955,8 @@ do -- Event Creation
local Event = { local Event = {
id = EVENTS.PlayerEnterUnit, id = EVENTS.PlayerEnterUnit,
time = timer.getTime(), time = timer.getTime(),
initiator = PlayerUnit:GetDCSObject(), initiator = PlayerUnit:GetDCSObject()
} }
world.onEvent( Event ) world.onEvent( Event )
end end
@@ -956,8 +970,8 @@ do -- Event Creation
local Event = { local Event = {
id = EVENTS.PlayerEnterAircraft, id = EVENTS.PlayerEnterAircraft,
time = timer.getTime(), time = timer.getTime(),
initiator = PlayerUnit:GetDCSObject(), initiator = PlayerUnit:GetDCSObject()
} }
world.onEvent( Event ) world.onEvent( Event )
end end
@@ -969,24 +983,25 @@ end
-- @param #EVENTDATA Event Event data table. -- @param #EVENTDATA Event Event data table.
function EVENT:onEvent( Event ) function EVENT:onEvent( Event )
--- Function to handle errors.
local ErrorHandler = function( errmsg ) local ErrorHandler = function( errmsg )
env.info( "Error in SCHEDULER function:" .. errmsg ) env.info( "Error in SCHEDULER function:" .. errmsg )
if BASE.Debug ~= nil then if BASE.Debug ~= nil then
env.info( debug.traceback() ) env.info( debug.traceback() )
end end
return errmsg return errmsg
end end
-- Get event meta data. -- Get event meta data.
local EventMeta = _EVENTMETA[Event.id] local EventMeta = _EVENTMETA[Event.id]
-- Check if this is a known event? -- Check if this is a known event?
if EventMeta then if EventMeta then
if self and self.Events and self.Events[Event.id] and self.MissionEnd == false and (Event.initiator ~= nil or (Event.initiator == nil and Event.id ~= EVENTS.PlayerLeaveUnit)) then if self and self.Events and self.Events[Event.id] and self.MissionEnd==false and (Event.initiator~=nil or (Event.initiator==nil and Event.id~=EVENTS.PlayerLeaveUnit)) then
-- Check if mission has ended.
if Event.id and Event.id == EVENTS.MissionEnd then if Event.id and Event.id == EVENTS.MissionEnd then
self.MissionEnd = true self.MissionEnd = true
end end
@@ -995,49 +1010,26 @@ function EVENT:onEvent( Event )
Event.IniObjectCategory = Event.initiator:getCategory() Event.IniObjectCategory = Event.initiator:getCategory()
if Event.IniObjectCategory == Object.Category.UNIT then if Event.IniObjectCategory == Object.Category.STATIC then
Event.IniDCSUnit = Event.initiator ---
Event.IniDCSUnitName = Event.IniDCSUnit:getName() -- Static
Event.IniUnitName = Event.IniDCSUnitName ---
Event.IniDCSGroup = Event.IniDCSUnit:getGroup() if Event.id==31 then
Event.IniUnit = UNIT:FindByName( Event.IniDCSUnitName )
if not Event.IniUnit then
-- Unit can be a CLIENT. Most likely this will be the case ...
Event.IniUnit = CLIENT:FindByName( Event.IniDCSUnitName, '', true )
end
Event.IniDCSGroupName = ""
if Event.IniDCSGroup and Event.IniDCSGroup:isExist() then
Event.IniDCSGroupName = Event.IniDCSGroup:getName()
Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName )
-- if Event.IniGroup then
Event.IniGroupName = Event.IniDCSGroupName
-- end
end
Event.IniPlayerName = Event.IniDCSUnit:getPlayerName()
Event.IniCoalition = Event.IniDCSUnit:getCoalition()
Event.IniTypeName = Event.IniDCSUnit:getTypeName()
Event.IniCategory = Event.IniDCSUnit:getDesc().category
end
if Event.IniObjectCategory == Object.Category.STATIC then
if Event.id == 31 then
-- Event.initiator is a Static object representing the pilot. But getName() errors due to DCS bug. -- Event.initiator is a Static object representing the pilot. But getName() errors due to DCS bug.
Event.IniDCSUnit = Event.initiator Event.IniDCSUnit = Event.initiator
local ID = Event.initiator.id_ local ID=Event.initiator.id_
Event.IniDCSUnitName = string.format( "Ejected Pilot ID %s", tostring( ID ) ) Event.IniDCSUnitName = string.format("Ejected Pilot ID %s", tostring(ID))
Event.IniUnitName = Event.IniDCSUnitName Event.IniUnitName = Event.IniDCSUnitName
Event.IniCoalition = 0 Event.IniCoalition = 0
Event.IniCategory = 0 Event.IniCategory = 0
Event.IniTypeName = "Ejected Pilot" Event.IniTypeName = "Ejected Pilot"
elseif Event.id == 33 then -- ejection seat discarded elseif Event.id == 33 then -- ejection seat discarded
Event.IniDCSUnit = Event.initiator Event.IniDCSUnit = Event.initiator
local ID = Event.initiator.id_ local ID=Event.initiator.id_
Event.IniDCSUnitName = string.format( "Ejection Seat ID %s", tostring( ID ) ) Event.IniDCSUnitName = string.format("Ejection Seat ID %s", tostring(ID))
Event.IniUnitName = Event.IniDCSUnitName Event.IniUnitName = Event.IniDCSUnitName
Event.IniCoalition = 0 Event.IniCoalition = 0
Event.IniCategory = 0 Event.IniCategory = 0
Event.IniTypeName = "Ejection Seat" Event.IniTypeName = "Ejection Seat"
else else
Event.IniDCSUnit = Event.initiator Event.IniDCSUnit = Event.initiator
@@ -1048,9 +1040,47 @@ function EVENT:onEvent( Event )
Event.IniCategory = Event.IniDCSUnit:getDesc().category Event.IniCategory = Event.IniDCSUnit:getDesc().category
Event.IniTypeName = Event.IniDCSUnit:getTypeName() Event.IniTypeName = Event.IniDCSUnit:getTypeName()
end end
-- Dead events of units can be delayed and the initiator changed to a static.
-- Take care of that.
local Unit=UNIT:FindByName(Event.IniDCSUnitName)
if Unit then
Event.IniObjectCategory = Object.Category.UNIT
end
end
if Event.IniObjectCategory == Object.Category.UNIT then
---
-- Unit
---
Event.IniDCSUnit = Event.initiator
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
Event.IniUnitName = Event.IniDCSUnitName
Event.IniDCSGroup = Event.IniDCSUnit:getGroup()
Event.IniUnit = UNIT:FindByName( Event.IniDCSUnitName )
if not Event.IniUnit then
-- Unit can be a CLIENT. Most likely this will be the case ...
Event.IniUnit = CLIENT:FindByName( Event.IniDCSUnitName, '', true )
end
Event.IniDCSGroupName = Event.IniUnit and Event.IniUnit.GroupName or ""
if Event.IniDCSGroup and Event.IniDCSGroup:isExist() then
Event.IniDCSGroupName = Event.IniDCSGroup:getName()
Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName )
Event.IniGroupName = Event.IniDCSGroupName
end
Event.IniPlayerName = Event.IniDCSUnit:getPlayerName()
Event.IniCoalition = Event.IniDCSUnit:getCoalition()
Event.IniTypeName = Event.IniDCSUnit:getTypeName()
Event.IniCategory = Event.IniDCSUnit:getDesc().category
end end
if Event.IniObjectCategory == Object.Category.CARGO then if Event.IniObjectCategory == Object.Category.CARGO then
---
-- Cargo
---
Event.IniDCSUnit = Event.initiator Event.IniDCSUnit = Event.initiator
Event.IniDCSUnitName = Event.IniDCSUnit:getName() Event.IniDCSUnitName = Event.IniDCSUnit:getName()
Event.IniUnitName = Event.IniDCSUnitName Event.IniUnitName = Event.IniDCSUnitName
@@ -1061,19 +1091,26 @@ function EVENT:onEvent( Event )
end end
if Event.IniObjectCategory == Object.Category.SCENERY then if Event.IniObjectCategory == Object.Category.SCENERY then
---
-- Scenery
---
Event.IniDCSUnit = Event.initiator Event.IniDCSUnit = Event.initiator
Event.IniDCSUnitName = Event.IniDCSUnit:getName() Event.IniDCSUnitName = Event.IniDCSUnit:getName()
Event.IniUnitName = Event.IniDCSUnitName Event.IniUnitName = Event.IniDCSUnitName
Event.IniUnit = SCENERY:Register( Event.IniDCSUnitName, Event.initiator ) Event.IniUnit = SCENERY:Register( Event.IniDCSUnitName, Event.initiator )
Event.IniCategory = Event.IniDCSUnit:getDesc().category Event.IniCategory = Event.IniDCSUnit:getDesc().category
Event.IniTypeName = Event.initiator:isExist() and Event.IniDCSUnit:getTypeName() or "SCENERY" -- TODO: Bug fix for 2.1! Event.IniTypeName = Event.initiator:isExist() and Event.IniDCSUnit:getTypeName() or "SCENERY"
end end
if Event.IniObjectCategory == Object.Category.BASE then if Event.IniObjectCategory == Object.Category.BASE then
---
-- Base Object
---
Event.IniDCSUnit = Event.initiator Event.IniDCSUnit = Event.initiator
Event.IniDCSUnitName = Event.IniDCSUnit:getName() Event.IniDCSUnitName = Event.IniDCSUnit:getName()
Event.IniUnitName = Event.IniDCSUnitName Event.IniUnitName = Event.IniDCSUnitName
Event.IniUnit = AIRBASE:FindByName( Event.IniDCSUnitName ) Event.IniUnit = AIRBASE:FindByName(Event.IniDCSUnitName)
Event.IniCoalition = Event.IniDCSUnit:getCoalition() Event.IniCoalition = Event.IniDCSUnit:getCoalition()
Event.IniCategory = Event.IniDCSUnit:getDesc().category Event.IniCategory = Event.IniDCSUnit:getDesc().category
Event.IniTypeName = Event.IniDCSUnit:getTypeName() Event.IniTypeName = Event.IniDCSUnit:getTypeName()
@@ -1082,6 +1119,11 @@ function EVENT:onEvent( Event )
if Event.target then if Event.target then
---
-- TARGET
---
-- Target category.
Event.TgtObjectCategory = Event.target:getCategory() Event.TgtObjectCategory = Event.target:getCategory()
if Event.TgtObjectCategory == Object.Category.UNIT then if Event.TgtObjectCategory == Object.Category.UNIT then
@@ -1094,9 +1136,7 @@ function EVENT:onEvent( Event )
if Event.TgtDCSGroup and Event.TgtDCSGroup:isExist() then if Event.TgtDCSGroup and Event.TgtDCSGroup:isExist() then
Event.TgtDCSGroupName = Event.TgtDCSGroup:getName() Event.TgtDCSGroupName = Event.TgtDCSGroup:getName()
Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName ) Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName )
-- if Event.TgtGroup then
Event.TgtGroupName = Event.TgtDCSGroupName Event.TgtGroupName = Event.TgtDCSGroupName
-- end
end end
Event.TgtPlayerName = Event.TgtDCSUnit:getPlayerName() Event.TgtPlayerName = Event.TgtDCSUnit:getPlayerName()
Event.TgtCoalition = Event.TgtDCSUnit:getCoalition() Event.TgtCoalition = Event.TgtDCSUnit:getCoalition()
@@ -1115,18 +1155,18 @@ function EVENT:onEvent( Event )
Event.TgtCategory = Event.TgtDCSUnit:getDesc().category Event.TgtCategory = Event.TgtDCSUnit:getDesc().category
Event.TgtTypeName = Event.TgtDCSUnit:getTypeName() Event.TgtTypeName = Event.TgtDCSUnit:getTypeName()
else else
Event.TgtDCSUnitName = string.format( "No target object for Event ID %s", tostring( Event.id ) ) Event.TgtDCSUnitName = string.format("No target object for Event ID %s", tostring(Event.id))
Event.TgtUnitName = Event.TgtDCSUnitName Event.TgtUnitName = Event.TgtDCSUnitName
Event.TgtUnit = nil Event.TgtUnit = nil
Event.TgtCoalition = 0 Event.TgtCoalition = 0
Event.TgtCategory = 0 Event.TgtCategory = 0
if Event.id == 6 then if Event.id == 6 then
Event.TgtTypeName = "Ejected Pilot" Event.TgtTypeName = "Ejected Pilot"
Event.TgtDCSUnitName = string.format( "Ejected Pilot ID %s", tostring( Event.IniDCSUnitName ) ) Event.TgtDCSUnitName = string.format("Ejected Pilot ID %s", tostring(Event.IniDCSUnitName))
Event.TgtUnitName = Event.TgtDCSUnitName Event.TgtUnitName = Event.TgtDCSUnitName
elseif Event.id == 33 then elseif Event.id == 33 then
Event.TgtTypeName = "Ejection Seat" Event.TgtTypeName = "Ejection Seat"
Event.TgtDCSUnitName = string.format( "Ejection Seat ID %s", tostring( Event.IniDCSUnitName ) ) Event.TgtDCSUnitName = string.format("Ejection Seat ID %s", tostring(Event.IniDCSUnitName))
Event.TgtUnitName = Event.TgtDCSUnitName Event.TgtUnitName = Event.TgtDCSUnitName
else else
Event.TgtTypeName = "Static" Event.TgtTypeName = "Static"
@@ -1144,6 +1184,7 @@ function EVENT:onEvent( Event )
end end
end end
-- Weapon.
if Event.weapon then if Event.weapon then
Event.Weapon = Event.weapon Event.Weapon = Event.weapon
Event.WeaponName = Event.Weapon:getTypeName() Event.WeaponName = Event.Weapon:getTypeName()
@@ -1152,49 +1193,48 @@ function EVENT:onEvent( Event )
Event.WeaponCoalition = Event.WeaponUNIT and Event.Weapon:getCoalition() Event.WeaponCoalition = Event.WeaponUNIT and Event.Weapon:getCoalition()
Event.WeaponCategory = Event.WeaponUNIT and Event.Weapon:getDesc().category Event.WeaponCategory = Event.WeaponUNIT and Event.Weapon:getDesc().category
Event.WeaponTypeName = Event.WeaponUNIT and Event.Weapon:getTypeName() Event.WeaponTypeName = Event.WeaponUNIT and Event.Weapon:getTypeName()
-- Event.WeaponTgtDCSUnit = Event.Weapon:getTarget() --Event.WeaponTgtDCSUnit = Event.Weapon:getTarget()
end end
-- Place should be given for takeoff and landing events as well as base captured. It should be a DCS airbase. -- Place should be given for takeoff and landing events as well as base captured. It should be a DCS airbase.
if Event.place then if Event.place then
if Event.id == EVENTS.LandingAfterEjection then if Event.id==EVENTS.LandingAfterEjection then
-- Place is here the UNIT of which the pilot ejected. -- Place is here the UNIT of which the pilot ejected.
-- local name=Event.place:getName() -- This returns a DCS error "Airbase doesn't exit" :( --local name=Event.place:getName() -- This returns a DCS error "Airbase doesn't exit" :(
-- However, this is not a big thing, as the aircraft the pilot ejected from is usually long crashed before the ejected pilot touches the ground. -- However, this is not a big thing, as the aircraft the pilot ejected from is usually long crashed before the ejected pilot touches the ground.
-- Event.Place=UNIT:Find(Event.place) --Event.Place=UNIT:Find(Event.place)
else else
Event.Place = AIRBASE:Find( Event.place ) Event.Place=AIRBASE:Find(Event.place)
Event.PlaceName = Event.Place:GetName() Event.PlaceName=Event.Place:GetName()
end end
end end
-- Mark points. -- Mark points.
if Event.idx then if Event.idx then
Event.MarkID = Event.idx Event.MarkID=Event.idx
Event.MarkVec3 = Event.pos Event.MarkVec3=Event.pos
Event.MarkCoordinate = COORDINATE:NewFromVec3( Event.pos ) Event.MarkCoordinate=COORDINATE:NewFromVec3(Event.pos)
Event.MarkText = Event.text Event.MarkText=Event.text
Event.MarkCoalition = Event.coalition Event.MarkCoalition=Event.coalition
Event.MarkGroupID = Event.groupID Event.MarkGroupID = Event.groupID
end end
-- Cargo object.
if Event.cargo then if Event.cargo then
Event.Cargo = Event.cargo Event.Cargo = Event.cargo
Event.CargoName = Event.cargo.Name Event.CargoName = Event.cargo.Name
end end
-- Zone object.
if Event.zone then if Event.zone then
Event.Zone = Event.zone Event.Zone = Event.zone
Event.ZoneName = Event.zone.ZoneName Event.ZoneName = Event.zone.ZoneName
end end
-- Priority order.
local PriorityOrder = EventMeta.Order local PriorityOrder = EventMeta.Order
local PriorityBegin = PriorityOrder == -1 and 5 or 1 local PriorityBegin = PriorityOrder == -1 and 5 or 1
local PriorityEnd = PriorityOrder == -1 and 1 or 5 local PriorityEnd = PriorityOrder == -1 and 1 or 5
if Event.IniObjectCategory ~= Object.Category.STATIC then
self:F( { EventMeta.Text, Event, Event.IniDCSUnitName, Event.TgtDCSUnitName, PriorityOrder } )
end
for EventPriority = PriorityBegin, PriorityEnd, PriorityOrder do for EventPriority = PriorityBegin, PriorityEnd, PriorityOrder do
@@ -1203,12 +1243,12 @@ function EVENT:onEvent( Event )
-- Okay, we got the event from DCS. Now loop the SORTED self.EventSorted[] table for the received Event.id, and for each EventData registered, check if a function needs to be called. -- Okay, we got the event from DCS. Now loop the SORTED self.EventSorted[] table for the received Event.id, and for each EventData registered, check if a function needs to be called.
for EventClass, EventData in pairs( self.Events[Event.id][EventPriority] ) do for EventClass, EventData in pairs( self.Events[Event.id][EventPriority] ) do
-- if Event.IniObjectCategory ~= Object.Category.STATIC then --if Event.IniObjectCategory ~= Object.Category.STATIC then
-- self:E( { "Evaluating: ", EventClass:GetClassNameAndID() } ) -- self:E( { "Evaluating: ", EventClass:GetClassNameAndID() } )
-- end --end
Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName ) Event.IniGroup = Event.IniGroup or GROUP:FindByName( Event.IniDCSGroupName )
Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName ) Event.TgtGroup = Event.TgtGroup or GROUP:FindByName( Event.TgtDCSGroupName )
-- If the EventData is for a UNIT, the call directly the EventClass EventFunction for that UNIT. -- If the EventData is for a UNIT, the call directly the EventClass EventFunction for that UNIT.
if EventData.EventUnit then if EventData.EventUnit then
@@ -1218,39 +1258,35 @@ function EVENT:onEvent( Event )
Event.id == EVENTS.PlayerEnterUnit or Event.id == EVENTS.PlayerEnterUnit or
Event.id == EVENTS.Crash or Event.id == EVENTS.Crash or
Event.id == EVENTS.Dead or Event.id == EVENTS.Dead or
Event.id == EVENTS.RemoveUnit then Event.id == EVENTS.RemoveUnit or
Event.id == EVENTS.UnitLost then
local UnitName = EventClass:GetName() local UnitName = EventClass:GetName()
if (EventMeta.Side == "I" and UnitName == Event.IniDCSUnitName) or if ( EventMeta.Side == "I" and UnitName == Event.IniDCSUnitName ) or
(EventMeta.Side == "T" and UnitName == Event.TgtDCSUnitName) then ( EventMeta.Side == "T" and UnitName == Event.TgtDCSUnitName ) then
-- First test if a EventFunction is Set, otherwise search for the default function -- First test if a EventFunction is Set, otherwise search for the default function
if EventData.EventFunction then if EventData.EventFunction then
if Event.IniObjectCategory ~= 3 then local Result, Value = xpcall(
self:F( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } ) function()
end return EventData.EventFunction( EventClass, Event )
end, ErrorHandler )
local Result, Value = xpcall( function()
return EventData.EventFunction( EventClass, Event )
end, ErrorHandler )
else else
-- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object. -- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object.
local EventFunction = EventClass[EventMeta.Event] local EventFunction = EventClass[ EventMeta.Event ]
if EventFunction and type( EventFunction ) == "function" then if EventFunction and type( EventFunction ) == "function" then
-- Now call the default event function. -- Now call the default event function.
if Event.IniObjectCategory ~= 3 then local Result, Value = xpcall(
self:F( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) function()
end return EventFunction( EventClass, Event )
end, ErrorHandler )
local Result, Value = xpcall( function()
return EventFunction( EventClass, Event )
end, ErrorHandler )
end end
end end
end end
else else
@@ -1268,45 +1304,40 @@ function EVENT:onEvent( Event )
Event.id == EVENTS.PlayerEnterUnit or Event.id == EVENTS.PlayerEnterUnit or
Event.id == EVENTS.Crash or Event.id == EVENTS.Crash or
Event.id == EVENTS.Dead or Event.id == EVENTS.Dead or
Event.id == EVENTS.RemoveUnit then Event.id == EVENTS.RemoveUnit or
Event.id == EVENTS.UnitLost then
-- We can get the name of the EventClass, which is now always a GROUP object. -- We can get the name of the EventClass, which is now always a GROUP object.
local GroupName = EventClass:GetName() local GroupName = EventClass:GetName()
if (EventMeta.Side == "I" and GroupName == Event.IniDCSGroupName) or if ( EventMeta.Side == "I" and GroupName == Event.IniDCSGroupName ) or
(EventMeta.Side == "T" and GroupName == Event.TgtDCSGroupName) then ( EventMeta.Side == "T" and GroupName == Event.TgtDCSGroupName ) then
-- First test if a EventFunction is Set, otherwise search for the default function -- First test if a EventFunction is Set, otherwise search for the default function
if EventData.EventFunction then if EventData.EventFunction then
if Event.IniObjectCategory ~= 3 then local Result, Value = xpcall(
self:F( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } ) function()
end return EventData.EventFunction( EventClass, Event, unpack( EventData.Params ) )
end, ErrorHandler )
local Result, Value = xpcall( function()
return EventData.EventFunction( EventClass, Event, unpack( EventData.Params ) )
end, ErrorHandler )
else else
-- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object. -- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object.
local EventFunction = EventClass[EventMeta.Event] local EventFunction = EventClass[ EventMeta.Event ]
if EventFunction and type( EventFunction ) == "function" then if EventFunction and type( EventFunction ) == "function" then
-- Now call the default event function. -- Now call the default event function.
if Event.IniObjectCategory ~= 3 then local Result, Value = xpcall(
self:F( { "Calling " .. EventMeta.Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } ) function()
end return EventFunction( EventClass, Event, unpack( EventData.Params ) )
end, ErrorHandler )
local Result, Value = xpcall( function()
return EventFunction( EventClass, Event, unpack( EventData.Params ) )
end, ErrorHandler )
end end
end end
end end
else else
-- The EventClass is not alive anymore, we remove it from the EventHandlers... -- The EventClass is not alive anymore, we remove it from the EventHandlers...
-- self:RemoveEvent( EventClass, Event.id ) --self:RemoveEvent( EventClass, Event.id )
end end
else else
@@ -1318,28 +1349,25 @@ function EVENT:onEvent( Event )
if EventData.EventFunction then if EventData.EventFunction then
-- There is an EventFunction defined, so call the EventFunction. -- There is an EventFunction defined, so call the EventFunction.
if Event.IniObjectCategory ~= 3 then local Result, Value = xpcall(
self:F2( { "Calling EventFunction for Class ", EventClass:GetClassNameAndID(), EventPriority } ) function()
end return EventData.EventFunction( EventClass, Event )
local Result, Value = xpcall( function() end, ErrorHandler )
return EventData.EventFunction( EventClass, Event )
end, ErrorHandler )
else else
-- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object. -- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object.
local EventFunction = EventClass[EventMeta.Event] local EventFunction = EventClass[ EventMeta.Event ]
if EventFunction and type( EventFunction ) == "function" then if EventFunction and type( EventFunction ) == "function" then
-- Now call the default event function. -- Now call the default event function.
if Event.IniObjectCategory ~= 3 then local Result, Value = xpcall(
self:F2( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) function()
end local Result, Value = EventFunction( EventClass, Event )
return Result, Value
end, ErrorHandler )
local Result, Value = xpcall( function()
local Result, Value = EventFunction( EventClass, Event )
return Result, Value
end, ErrorHandler )
end end
end end
end end
@@ -1362,7 +1390,7 @@ function EVENT:onEvent( Event )
self:T( { EventMeta.Text, Event } ) self:T( { EventMeta.Text, Event } )
end end
else else
self:E( string.format( "WARNING: Could not get EVENTMETA data for event ID=%d! Is this an unknown/new DCS event?", tostring( Event.id ) ) ) self:E(string.format("WARNING: Could not get EVENTMETA data for event ID=%d! Is this an unknown/new DCS event?", tostring(Event.id)))
end end
Event = nil Event = nil
+11 -11
View File
@@ -21,8 +21,8 @@
-- A FSM can only be in one of a finite number of states. -- A FSM can only be in one of a finite number of states.
-- The machine is in only one state at a time; the state it is in at any given time is called the **current state**. -- The machine is in only one state at a time; the state it is in at any given time is called the **current state**.
-- It can change from one state to another when initiated by an **__internal__ or __external__ triggering event**, which is called a **transition**. -- It can change from one state to another when initiated by an **__internal__ or __external__ triggering event**, which is called a **transition**.
-- An **FSM implementation** is defined by **a list of its states**, **its initial state**, and **the triggering events** for **each possible transition**. -- A **FSM implementation** is defined by **a list of its states**, **its initial state**, and **the triggering events** for **each possible transition**.
-- An FSM implementation is composed out of **two parts**, a set of **state transition rules**, and an implementation set of **state transition handlers**, implementing those transitions. -- A FSM implementation is composed out of **two parts**, a set of **state transition rules**, and an implementation set of **state transition handlers**, implementing those transitions.
-- --
-- The FSM class supports a **hierarchical implementation of a Finite State Machine**, -- The FSM class supports a **hierarchical implementation of a Finite State Machine**,
-- that is, it allows to **embed existing FSM implementations in a master FSM**. -- that is, it allows to **embed existing FSM implementations in a master FSM**.
@@ -34,21 +34,21 @@
-- orders him to destroy x targets and account the results. -- orders him to destroy x targets and account the results.
-- Other examples of ready made FSM could be: -- Other examples of ready made FSM could be:
-- --
-- * route a plane to a zone flown by a human -- * Route a plane to a zone flown by a human.
-- * detect targets by an AI and report to humans -- * Detect targets by an AI and report to humans.
-- * account for destroyed targets by human players -- * Account for destroyed targets by human players.
-- * handle AI infantry to deploy from or embark to a helicopter or airplane or vehicle -- * Handle AI infantry to deploy from or embark to a helicopter or airplane or vehicle.
-- * let an AI patrol a zone -- * Let an AI patrol a zone.
-- --
-- The **MOOSE framework** uses extensively the FSM class and derived FSM\_ classes, -- The **MOOSE framework** extensively uses the FSM class and derived FSM\_ classes,
-- because **the goal of MOOSE is to simplify mission design complexity for mission building**. -- because **the goal of MOOSE is to simplify mission design complexity for mission building**.
-- By efficiently utilizing the FSM class and derived classes, MOOSE allows mission designers to quickly build processes. -- By efficiently utilizing the FSM class and derived classes, MOOSE allows mission designers to quickly build processes.
-- **Ready made FSM-based implementations classes** exist within the MOOSE framework that **can easily be re-used, -- **Ready made FSM-based implementations classes** exist within the MOOSE framework that **can easily be re-used,
-- and tailored** by mission designers through **the implementation of Transition Handlers**. -- and tailored** by mission designers through **the implementation of Transition Handlers**.
-- Each of these FSM implementation classes start either with: -- Each of these FSM implementation classes start either with:
-- --
-- * an acronym **AI\_**, which indicates an FSM implementation directing **AI controlled** @{GROUP} and/or @{UNIT}. These AI\_ classes derive the @{#FSM_CONTROLLABLE} class. -- * an acronym **AI\_**, which indicates a FSM implementation directing **AI controlled** @{GROUP} and/or @{UNIT}. These AI\_ classes derive the @{#FSM_CONTROLLABLE} class.
-- * an acronym **TASK\_**, which indicates an FSM implementation executing a @{TASK} executed by Groups of players. These TASK\_ classes derive the @{#FSM_TASK} class. -- * an acronym **TASK\_**, which indicates a FSM implementation executing a @{TASK} executed by Groups of players. These TASK\_ classes derive the @{#FSM_TASK} class.
-- * an acronym **ACT\_**, which indicates an Sub-FSM implementation, directing **Humans actions** that need to be done in a @{TASK}, seated in a @{CLIENT} (slot) or a @{UNIT} (CA join). These ACT\_ classes derive the @{#FSM_PROCESS} class. -- * an acronym **ACT\_**, which indicates an Sub-FSM implementation, directing **Humans actions** that need to be done in a @{TASK}, seated in a @{CLIENT} (slot) or a @{UNIT} (CA join). These ACT\_ classes derive the @{#FSM_PROCESS} class.
-- --
-- Detailed explanations and API specifics are further below clarified and FSM derived class specifics are described in those class documentation sections. -- Detailed explanations and API specifics are further below clarified and FSM derived class specifics are described in those class documentation sections.
@@ -59,7 +59,7 @@
-- I've reworked this development (taken the concept), and created a **hierarchical state machine** out of it, embedded within the DCS simulator. -- I've reworked this development (taken the concept), and created a **hierarchical state machine** out of it, embedded within the DCS simulator.
-- Additionally, I've added extendability and created an API that allows seamless FSM implementation. -- Additionally, I've added extendability and created an API that allows seamless FSM implementation.
-- --
-- The following derived classes are available in the MOOSE framework, that implement a specialised form of a FSM: -- The following derived classes are available in the MOOSE framework, that implement a specialized form of a FSM:
-- --
-- * @{#FSM_TASK}: Models Finite State Machines for @{Task}s. -- * @{#FSM_TASK}: Models Finite State Machines for @{Task}s.
-- * @{#FSM_PROCESS}: Models Finite State Machines for @{Task} actions, which control @{Client}s. -- * @{#FSM_PROCESS}: Models Finite State Machines for @{Task} actions, which control @{Client}s.
+52 -144
View File
@@ -64,7 +64,6 @@ MENU_INDEX.Coalition[coalition.side.RED].Menus = {}
MENU_INDEX.Group = {} MENU_INDEX.Group = {}
function MENU_INDEX:ParentPath( ParentMenu, MenuText ) function MENU_INDEX:ParentPath( ParentMenu, MenuText )
local Path = ParentMenu and "@" .. table.concat( ParentMenu.MenuPath or {}, "@" ) or "" local Path = ParentMenu and "@" .. table.concat( ParentMenu.MenuPath or {}, "@" ) or ""
if ParentMenu then if ParentMenu then
if ParentMenu:IsInstanceOf( "MENU_GROUP" ) or ParentMenu:IsInstanceOf( "MENU_GROUP_COMMAND" ) then if ParentMenu:IsInstanceOf( "MENU_GROUP" ) or ParentMenu:IsInstanceOf( "MENU_GROUP_COMMAND" ) then
@@ -92,22 +91,20 @@ function MENU_INDEX:ParentPath( ParentMenu, MenuText )
Path = Path .. "@" .. MenuText Path = Path .. "@" .. MenuText
return Path return Path
end end
function MENU_INDEX:PrepareMission() function MENU_INDEX:PrepareMission()
self.MenuMission.Menus = self.MenuMission.Menus or {} self.MenuMission.Menus = self.MenuMission.Menus or {}
end end
function MENU_INDEX:PrepareCoalition( CoalitionSide ) function MENU_INDEX:PrepareCoalition( CoalitionSide )
self.Coalition[CoalitionSide] = self.Coalition[CoalitionSide] or {} self.Coalition[CoalitionSide] = self.Coalition[CoalitionSide] or {}
self.Coalition[CoalitionSide].Menus = self.Coalition[CoalitionSide].Menus or {} self.Coalition[CoalitionSide].Menus = self.Coalition[CoalitionSide].Menus or {}
end end
--- ---
-- @param Wrapper.Group#GROUP Group -- @param Wrapper.Group#GROUP Group
function MENU_INDEX:PrepareGroup( Group ) function MENU_INDEX:PrepareGroup( Group )
if Group and Group:IsAlive() ~= nil then -- something was changed here! if Group and Group:IsAlive() ~= nil then -- something was changed here!
local GroupName = Group:GetName() local GroupName = Group:GetName()
self.Group[GroupName] = self.Group[GroupName] or {} self.Group[GroupName] = self.Group[GroupName] or {}
self.Group[GroupName].Menus = self.Group[GroupName].Menus or {} self.Group[GroupName].Menus = self.Group[GroupName].Menus or {}
@@ -115,32 +112,22 @@ function MENU_INDEX:PrepareGroup( Group )
end end
function MENU_INDEX:HasMissionMenu( Path ) function MENU_INDEX:HasMissionMenu( Path )
return self.MenuMission.Menus[Path] return self.MenuMission.Menus[Path]
end end
function MENU_INDEX:SetMissionMenu( Path, Menu ) function MENU_INDEX:SetMissionMenu( Path, Menu )
self.MenuMission.Menus[Path] = Menu self.MenuMission.Menus[Path] = Menu
end end
function MENU_INDEX:ClearMissionMenu( Path ) function MENU_INDEX:ClearMissionMenu( Path )
self.MenuMission.Menus[Path] = nil self.MenuMission.Menus[Path] = nil
end end
function MENU_INDEX:HasCoalitionMenu( Coalition, Path ) function MENU_INDEX:HasCoalitionMenu( Coalition, Path )
return self.Coalition[Coalition].Menus[Path] return self.Coalition[Coalition].Menus[Path]
end end
function MENU_INDEX:SetCoalitionMenu( Coalition, Path, Menu ) function MENU_INDEX:SetCoalitionMenu( Coalition, Path, Menu )
self.Coalition[Coalition].Menus[Path] = Menu self.Coalition[Coalition].Menus[Path] = Menu
end end
function MENU_INDEX:ClearCoalitionMenu( Coalition, Path ) function MENU_INDEX:ClearCoalitionMenu( Coalition, Path )
self.Coalition[Coalition].Menus[Path] = nil self.Coalition[Coalition].Menus[Path] = nil
end end
@@ -151,46 +138,36 @@ function MENU_INDEX:HasGroupMenu( Group, Path )
end end
return nil return nil
end end
function MENU_INDEX:SetGroupMenu( Group, Path, Menu ) function MENU_INDEX:SetGroupMenu( Group, Path, Menu )
local MenuGroupName = Group:GetName() local MenuGroupName = Group:GetName()
Group:F( { MenuGroupName = MenuGroupName, Path = Path } ) Group:F({MenuGroupName=MenuGroupName,Path=Path})
self.Group[MenuGroupName].Menus[Path] = Menu self.Group[MenuGroupName].Menus[Path] = Menu
end end
function MENU_INDEX:ClearGroupMenu( Group, Path ) function MENU_INDEX:ClearGroupMenu( Group, Path )
local MenuGroupName = Group:GetName() local MenuGroupName = Group:GetName()
self.Group[MenuGroupName].Menus[Path] = nil self.Group[MenuGroupName].Menus[Path] = nil
end end
function MENU_INDEX:Refresh( Group ) function MENU_INDEX:Refresh( Group )
for MenuID, Menu in pairs( self.MenuMission.Menus ) do
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
for MenuID, Menu in pairs( self.MenuMission.Menus ) do return self
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
end end
do -- MENU_BASE do -- MENU_BASE
--- @type MENU_BASE --- @type MENU_BASE
-- @extends Core.Base#BASE -- @extends Core.Base#BASE
--- Defines the main MENU class where other MENU classes are derived from. --- Defines the main MENU class where other MENU classes are derived from.
-- This is an abstract class, so don't use it. -- This is an abstract class, so don't use it.
-- @field #MENU_BASE -- @field #MENU_BASE
@@ -210,14 +187,13 @@ do -- MENU_BASE
if ParentMenu ~= nil then if ParentMenu ~= nil then
MenuParentPath = ParentMenu.MenuPath MenuParentPath = ParentMenu.MenuPath
end end
local self = BASE:Inherit( self, BASE:New() ) local self = BASE:Inherit( self, BASE:New() )
self.MenuPath = nil self.MenuPath = nil
self.MenuText = MenuText self.MenuText = MenuText
self.ParentMenu = ParentMenu self.ParentMenu = ParentMenu
self.MenuParentPath = MenuParentPath self.MenuParentPath = MenuParentPath
self.Path = (self.ParentMenu and "@" .. table.concat( self.MenuParentPath or {}, "@" ) or "") .. "@" .. self.MenuText self.Path = ( self.ParentMenu and "@" .. table.concat( self.MenuParentPath or {}, "@" ) or "" ) .. "@" .. self.MenuText
self.Menus = {} self.Menus = {}
self.MenuCount = 0 self.MenuCount = 0
self.MenuStamp = timer.getTime() self.MenuStamp = timer.getTime()
@@ -230,7 +206,6 @@ do -- MENU_BASE
return self return self
end end
function MENU_BASE:SetParentMenu( MenuText, Menu ) function MENU_BASE:SetParentMenu( MenuText, Menu )
if self.ParentMenu then if self.ParentMenu then
self.ParentMenu.Menus = self.ParentMenu.Menus or {} self.ParentMenu.Menus = self.ParentMenu.Menus or {}
@@ -238,23 +213,21 @@ do -- MENU_BASE
self.ParentMenu.MenuCount = self.ParentMenu.MenuCount + 1 self.ParentMenu.MenuCount = self.ParentMenu.MenuCount + 1
end end
end end
function MENU_BASE:ClearParentMenu( MenuText ) function MENU_BASE:ClearParentMenu( MenuText )
if self.ParentMenu and self.ParentMenu.Menus[MenuText] then if self.ParentMenu and self.ParentMenu.Menus[MenuText] then
self.ParentMenu.Menus[MenuText] = nil self.ParentMenu.Menus[MenuText] = nil
self.ParentMenu.MenuCount = self.ParentMenu.MenuCount - 1 self.ParentMenu.MenuCount = self.ParentMenu.MenuCount - 1
if self.ParentMenu.MenuCount == 0 then if self.ParentMenu.MenuCount == 0 then
-- self.ParentMenu:Remove() --self.ParentMenu:Remove()
end end
end end
end end
--- Sets a @{Menu} to remove automatically the parent menu when the menu removed is the last child menu of that parent @{Menu}. --- Sets a @{Menu} to remove automatically the parent menu when the menu removed is the last child menu of that parent @{Menu}.
-- @param #MENU_BASE self -- @param #MENU_BASE self
-- @param #boolean RemoveParent If true, the parent menu is automatically removed when this menu is the last child menu of that parent @{Menu}. -- @param #boolean RemoveParent If true, the parent menu is automatically removed when this menu is the last child menu of that parent @{Menu}.
-- @return #MENU_BASE -- @return #MENU_BASE
function MENU_BASE:SetRemoveParent( RemoveParent ) function MENU_BASE:SetRemoveParent( RemoveParent )
-- self:F( { RemoveParent } ) --self:F( { RemoveParent } )
self.MenuRemoveParent = RemoveParent self.MenuRemoveParent = RemoveParent
return self return self
end end
@@ -266,7 +239,6 @@ do -- MENU_BASE
function MENU_BASE:GetMenu( MenuText ) function MENU_BASE:GetMenu( MenuText )
return self.Menus[MenuText] return self.Menus[MenuText]
end end
--- Sets a menu stamp for later prevention of menu removal. --- Sets a menu stamp for later prevention of menu removal.
-- @param #MENU_BASE self -- @param #MENU_BASE self
-- @param MenuStamp -- @param MenuStamp
@@ -276,6 +248,7 @@ do -- MENU_BASE
return self return self
end end
--- Gets a menu stamp for later prevention of menu removal. --- Gets a menu stamp for later prevention of menu removal.
-- @param #MENU_BASE self -- @param #MENU_BASE self
-- @return MenuStamp -- @return MenuStamp
@@ -283,6 +256,7 @@ do -- MENU_BASE
return timer.getTime() return timer.getTime()
end end
--- Sets a time stamp for later prevention of menu removal. --- Sets a time stamp for later prevention of menu removal.
-- @param #MENU_BASE self -- @param #MENU_BASE self
-- @param MenuStamp -- @param MenuStamp
@@ -302,9 +276,7 @@ do -- MENU_BASE
end end
end end
do -- MENU_COMMAND_BASE do -- MENU_COMMAND_BASE
--- @type MENU_COMMAND_BASE --- @type MENU_COMMAND_BASE
-- @field #function MenuCallHandler -- @field #function MenuCallHandler
-- @extends Core.Menu#MENU_BASE -- @extends Core.Menu#MENU_BASE
@@ -326,7 +298,6 @@ do -- MENU_COMMAND_BASE
function MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, CommandMenuArguments ) function MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, CommandMenuArguments )
local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) -- #MENU_COMMAND_BASE local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) -- #MENU_COMMAND_BASE
-- When a menu function goes into error, DCS displays an obscure menu message. -- When a menu function goes into error, DCS displays an obscure menu message.
-- This error handler catches the menu error and displays the full call stack. -- This error handler catches the menu error and displays the full call stack.
local ErrorHandler = function( errmsg ) local ErrorHandler = function( errmsg )
@@ -359,7 +330,6 @@ do -- MENU_COMMAND_BASE
self.CommandMenuFunction = CommandMenuFunction self.CommandMenuFunction = CommandMenuFunction
return self return self
end end
--- This sets the new command arguments of a menu, --- This sets the new command arguments of a menu,
-- so that if a menu is regenerated, or if command arguments change, -- so that if a menu is regenerated, or if command arguments change,
-- that the arguments set for the menu are loosely coupled with the menu itself!!! -- that the arguments set for the menu are loosely coupled with the menu itself!!!
@@ -370,14 +340,11 @@ do -- MENU_COMMAND_BASE
self.CommandMenuArguments = CommandMenuArguments self.CommandMenuArguments = CommandMenuArguments
return self return self
end end
end end
do -- MENU_MISSION do -- MENU_MISSION
--- @type MENU_MISSION --- @type MENU_MISSION
-- @extends Core.Menu#MENU_BASE -- @extends Core.Menu#MENU_BASE
--- Manages the main menus for a complete mission. --- Manages the main menus for a complete mission.
-- --
-- You can add menus with the @{#MENU_MISSION.New} method, which constructs a MENU_MISSION object and returns you the object reference. -- You can add menus with the @{#MENU_MISSION.New} method, which constructs a MENU_MISSION object and returns you the object reference.
@@ -397,7 +364,6 @@ do -- MENU_MISSION
MENU_INDEX:PrepareMission() MENU_INDEX:PrepareMission()
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText ) local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
local MissionMenu = MENU_INDEX:HasMissionMenu( Path ) local MissionMenu = MENU_INDEX:HasMissionMenu( Path )
if MissionMenu then if MissionMenu then
return MissionMenu return MissionMenu
else else
@@ -410,17 +376,15 @@ do -- MENU_MISSION
end end
end end
--- Refreshes a radio item for a mission --- Refreshes a radio item for a mission
-- @param #MENU_MISSION self -- @param #MENU_MISSION self
-- @return #MENU_MISSION -- @return #MENU_MISSION
function MENU_MISSION:Refresh() function MENU_MISSION:Refresh()
do do
missionCommands.removeItem( self.MenuPath ) missionCommands.removeItem( self.MenuPath )
self.MenuPath = missionCommands.addSubMenu( self.MenuText, self.MenuParentPath ) self.MenuPath = missionCommands.addSubMenu( self.MenuText, self.MenuParentPath )
end end
return self
end end
--- Removes the sub menus recursively of this MENU_MISSION. Note that the main menu is kept! --- Removes the sub menus recursively of this MENU_MISSION. Note that the main menu is kept!
@@ -444,11 +408,10 @@ do -- MENU_MISSION
MENU_INDEX:PrepareMission() MENU_INDEX:PrepareMission()
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText ) local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
local MissionMenu = MENU_INDEX:HasMissionMenu( Path ) local MissionMenu = MENU_INDEX:HasMissionMenu( Path )
if MissionMenu == self then if MissionMenu == self then
self:RemoveSubMenus() self:RemoveSubMenus()
if not MenuStamp or self.MenuStamp ~= MenuStamp then if not MenuStamp or self.MenuStamp ~= MenuStamp then
if (not MenuTag) or (MenuTag and self.MenuTag and MenuTag == self.MenuTag) then if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
self:F( { Text = self.MenuText, Path = self.MenuPath } ) self:F( { Text = self.MenuText, Path = self.MenuPath } )
if self.MenuPath ~= nil then if self.MenuPath ~= nil then
missionCommands.removeItem( self.MenuPath ) missionCommands.removeItem( self.MenuPath )
@@ -466,7 +429,6 @@ do -- MENU_MISSION
end end
end end
do -- MENU_MISSION_COMMAND do -- MENU_MISSION_COMMAND
--- @type MENU_MISSION_COMMAND --- @type MENU_MISSION_COMMAND
@@ -494,7 +456,6 @@ do -- MENU_MISSION_COMMAND
MENU_INDEX:PrepareMission() MENU_INDEX:PrepareMission()
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText ) local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
local MissionMenu = MENU_INDEX:HasMissionMenu( Path ) local MissionMenu = MENU_INDEX:HasMissionMenu( Path )
if MissionMenu then if MissionMenu then
MissionMenu:SetCommandMenuFunction( CommandMenuFunction ) MissionMenu:SetCommandMenuFunction( CommandMenuFunction )
MissionMenu:SetCommandMenuArguments( arg ) MissionMenu:SetCommandMenuArguments( arg )
@@ -508,17 +469,15 @@ do -- MENU_MISSION_COMMAND
return self return self
end end
end end
--- Refreshes a radio item for a mission --- Refreshes a radio item for a mission
-- @param #MENU_MISSION_COMMAND self -- @param #MENU_MISSION_COMMAND self
-- @return #MENU_MISSION_COMMAND -- @return #MENU_MISSION_COMMAND
function MENU_MISSION_COMMAND:Refresh() function MENU_MISSION_COMMAND:Refresh()
do do
missionCommands.removeItem( self.MenuPath ) missionCommands.removeItem( self.MenuPath )
missionCommands.addCommand( self.MenuText, self.MenuParentPath, self.MenuCallHandler ) missionCommands.addCommand( self.MenuText, self.MenuParentPath, self.MenuCallHandler )
end end
return self
end end
--- Removes a radio command item for a coalition --- Removes a radio command item for a coalition
@@ -529,10 +488,9 @@ do -- MENU_MISSION_COMMAND
MENU_INDEX:PrepareMission() MENU_INDEX:PrepareMission()
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText ) local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
local MissionMenu = MENU_INDEX:HasMissionMenu( Path ) local MissionMenu = MENU_INDEX:HasMissionMenu( Path )
if MissionMenu == self then if MissionMenu == self then
if not MenuStamp or self.MenuStamp ~= MenuStamp then if not MenuStamp or self.MenuStamp ~= MenuStamp then
if (not MenuTag) or (MenuTag and self.MenuTag and MenuTag == self.MenuTag) then if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
self:F( { Text = self.MenuText, Path = self.MenuPath } ) self:F( { Text = self.MenuText, Path = self.MenuPath } )
if self.MenuPath ~= nil then if self.MenuPath ~= nil then
missionCommands.removeItem( self.MenuPath ) missionCommands.removeItem( self.MenuPath )
@@ -548,11 +506,8 @@ do -- MENU_MISSION_COMMAND
return self return self
end end
end end
do -- MENU_COALITION do -- MENU_COALITION
--- @type MENU_COALITION --- @type MENU_COALITION
-- @extends Core.Menu#MENU_BASE -- @extends Core.Menu#MENU_BASE
@@ -601,7 +556,7 @@ do -- MENU_COALITION
-- --
-- @field #MENU_COALITION -- @field #MENU_COALITION
MENU_COALITION = { MENU_COALITION = {
ClassName = "MENU_COALITION", ClassName = "MENU_COALITION"
} }
--- MENU_COALITION constructor. Creates a new MENU_COALITION object and creates the menu for a complete coalition. --- MENU_COALITION constructor. Creates a new MENU_COALITION object and creates the menu for a complete coalition.
@@ -611,15 +566,12 @@ do -- MENU_COALITION
-- @param #table ParentMenu The parent menu. This parameter can be ignored if you want the menu to be located at the parent menu of DCS world (under F10 other). -- @param #table ParentMenu The parent menu. This parameter can be ignored if you want the menu to be located at the parent menu of DCS world (under F10 other).
-- @return #MENU_COALITION self -- @return #MENU_COALITION self
function MENU_COALITION:New( Coalition, MenuText, ParentMenu ) function MENU_COALITION:New( Coalition, MenuText, ParentMenu )
MENU_INDEX:PrepareCoalition( Coalition ) MENU_INDEX:PrepareCoalition( Coalition )
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText ) local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
local CoalitionMenu = MENU_INDEX:HasCoalitionMenu( Coalition, Path ) local CoalitionMenu = MENU_INDEX:HasCoalitionMenu( Coalition, Path )
if CoalitionMenu then if CoalitionMenu then
return CoalitionMenu return CoalitionMenu
else else
local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) )
MENU_INDEX:SetCoalitionMenu( Coalition, Path, self ) MENU_INDEX:SetCoalitionMenu( Coalition, Path, self )
@@ -630,17 +582,15 @@ do -- MENU_COALITION
return self return self
end end
end end
--- Refreshes a radio item for a coalition --- Refreshes a radio item for a coalition
-- @param #MENU_COALITION self -- @param #MENU_COALITION self
-- @return #MENU_COALITION -- @return #MENU_COALITION
function MENU_COALITION:Refresh() function MENU_COALITION:Refresh()
do do
missionCommands.removeItemForCoalition( self.Coalition, self.MenuPath ) missionCommands.removeItemForCoalition( self.Coalition, self.MenuPath )
missionCommands.addSubMenuForCoalition( self.Coalition, self.MenuText, self.MenuParentPath ) missionCommands.addSubMenuForCoalition( self.Coalition, self.MenuText, self.MenuParentPath )
end end
return self
end end
--- Removes the sub menus recursively of this MENU_COALITION. Note that the main menu is kept! --- Removes the sub menus recursively of this MENU_COALITION. Note that the main menu is kept!
@@ -663,11 +613,10 @@ do -- MENU_COALITION
MENU_INDEX:PrepareCoalition( self.Coalition ) MENU_INDEX:PrepareCoalition( self.Coalition )
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText ) local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
local CoalitionMenu = MENU_INDEX:HasCoalitionMenu( self.Coalition, Path ) local CoalitionMenu = MENU_INDEX:HasCoalitionMenu( self.Coalition, Path )
if CoalitionMenu == self then if CoalitionMenu == self then
self:RemoveSubMenus() self:RemoveSubMenus()
if not MenuStamp or self.MenuStamp ~= MenuStamp then if not MenuStamp or self.MenuStamp ~= MenuStamp then
if (not MenuTag) or (MenuTag and self.MenuTag and MenuTag == self.MenuTag) then if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
self:F( { Coalition = self.Coalition, Text = self.MenuText, Path = self.MenuPath } ) self:F( { Coalition = self.Coalition, Text = self.MenuText, Path = self.MenuPath } )
if self.MenuPath ~= nil then if self.MenuPath ~= nil then
missionCommands.removeItemForCoalition( self.Coalition, self.MenuPath ) missionCommands.removeItemForCoalition( self.Coalition, self.MenuPath )
@@ -683,9 +632,7 @@ do -- MENU_COALITION
return self return self
end end
end end
do -- MENU_COALITION_COMMAND do -- MENU_COALITION_COMMAND
--- @type MENU_COALITION_COMMAND --- @type MENU_COALITION_COMMAND
@@ -698,7 +645,7 @@ do -- MENU_COALITION_COMMAND
-- --
-- @field #MENU_COALITION_COMMAND -- @field #MENU_COALITION_COMMAND
MENU_COALITION_COMMAND = { MENU_COALITION_COMMAND = {
ClassName = "MENU_COALITION_COMMAND", ClassName = "MENU_COALITION_COMMAND"
} }
--- MENU_COALITION constructor. Creates a new radio command item for a coalition, which can invoke a function with parameters. --- MENU_COALITION constructor. Creates a new radio command item for a coalition, which can invoke a function with parameters.
@@ -714,7 +661,6 @@ do -- MENU_COALITION_COMMAND
MENU_INDEX:PrepareCoalition( Coalition ) MENU_INDEX:PrepareCoalition( Coalition )
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText ) local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
local CoalitionMenu = MENU_INDEX:HasCoalitionMenu( Coalition, Path ) local CoalitionMenu = MENU_INDEX:HasCoalitionMenu( Coalition, Path )
if CoalitionMenu then if CoalitionMenu then
CoalitionMenu:SetCommandMenuFunction( CommandMenuFunction ) CoalitionMenu:SetCommandMenuFunction( CommandMenuFunction )
CoalitionMenu:SetCommandMenuArguments( arg ) CoalitionMenu:SetCommandMenuArguments( arg )
@@ -729,19 +675,17 @@ do -- MENU_COALITION_COMMAND
self:SetParentMenu( self.MenuText, self ) self:SetParentMenu( self.MenuText, self )
return self return self
end end
end end
--- Refreshes a radio item for a coalition --- Refreshes a radio item for a coalition
-- @param #MENU_COALITION_COMMAND self -- @param #MENU_COALITION_COMMAND self
-- @return #MENU_COALITION_COMMAND -- @return #MENU_COALITION_COMMAND
function MENU_COALITION_COMMAND:Refresh() function MENU_COALITION_COMMAND:Refresh()
do do
missionCommands.removeItemForCoalition( self.Coalition, self.MenuPath ) missionCommands.removeItemForCoalition( self.Coalition, self.MenuPath )
missionCommands.addCommandForCoalition( self.Coalition, self.MenuText, self.MenuParentPath, self.MenuCallHandler ) missionCommands.addCommandForCoalition( self.Coalition, self.MenuText, self.MenuParentPath, self.MenuCallHandler )
end end
return self
end end
--- Removes a radio command item for a coalition --- Removes a radio command item for a coalition
@@ -752,10 +696,9 @@ do -- MENU_COALITION_COMMAND
MENU_INDEX:PrepareCoalition( self.Coalition ) MENU_INDEX:PrepareCoalition( self.Coalition )
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText ) local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
local CoalitionMenu = MENU_INDEX:HasCoalitionMenu( self.Coalition, Path ) local CoalitionMenu = MENU_INDEX:HasCoalitionMenu( self.Coalition, Path )
if CoalitionMenu == self then if CoalitionMenu == self then
if not MenuStamp or self.MenuStamp ~= MenuStamp then if not MenuStamp or self.MenuStamp ~= MenuStamp then
if (not MenuTag) or (MenuTag and self.MenuTag and MenuTag == self.MenuTag) then if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
self:F( { Coalition = self.Coalition, Text = self.MenuText, Path = self.MenuPath } ) self:F( { Coalition = self.Coalition, Text = self.MenuText, Path = self.MenuPath } )
if self.MenuPath ~= nil then if self.MenuPath ~= nil then
missionCommands.removeItemForCoalition( self.Coalition, self.MenuPath ) missionCommands.removeItemForCoalition( self.Coalition, self.MenuPath )
@@ -771,22 +714,20 @@ do -- MENU_COALITION_COMMAND
return self return self
end end
end end
--- MENU_GROUP --- MENU_GROUP
do do
-- This local variable is used to cache the menus registered under groups. -- This local variable is used to cache the menus registered under groups.
-- Menus don't dissapear when groups for players are destroyed and restarted. -- Menus don't disappear when groups for players are destroyed and restarted.
-- So every menu for a client created must be tracked so that program logic accidentally does not create. -- So every menu for a client created must be tracked so that program logic accidentally does not create.
-- the same menus twice during initialization logic. -- the same menus twice during initialization logic.
-- These menu classes are handling this logic with this variable. -- These menu classes are handling this logic with this variable.
local _MENUGROUPS = {} local _MENUGROUPS = {}
--- @type MENU_GROUP --- @type MENU_GROUP
-- @extends Core.Menu#MENU_BASE -- @extends Core.Menu#MENU_BASE
--- Manages the main menus for @{Wrapper.Group}s. --- Manages the main menus for @{Wrapper.Group}s.
-- --
-- You can add menus with the @{#MENU_GROUP.New} method, which constructs a MENU_GROUP object and returns you the object reference. -- You can add menus with the @{#MENU_GROUP.New} method, which constructs a MENU_GROUP object and returns you the object reference.
@@ -844,7 +785,7 @@ do
-- --
-- @field #MENU_GROUP -- @field #MENU_GROUP
MENU_GROUP = { MENU_GROUP = {
ClassName = "MENU_GROUP", ClassName = "MENU_GROUP"
} }
--- MENU_GROUP constructor. Creates a new radio menu item for a group. --- MENU_GROUP constructor. Creates a new radio menu item for a group.
@@ -858,16 +799,13 @@ do
MENU_INDEX:PrepareGroup( Group ) MENU_INDEX:PrepareGroup( Group )
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText ) local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
local GroupMenu = MENU_INDEX:HasGroupMenu( Group, Path ) local GroupMenu = MENU_INDEX:HasGroupMenu( Group, Path )
if GroupMenu then if GroupMenu then
return GroupMenu return GroupMenu
else else
self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) )
MENU_INDEX:SetGroupMenu( Group, Path, self ) MENU_INDEX:SetGroupMenu( Group, Path, self )
self.Group = Group self.Group = Group
self.GroupID = Group:GetID() self.GroupID = Group:GetID()
self.MenuPath = missionCommands.addSubMenuForGroup( self.GroupID, MenuText, self.MenuParentPath ) self.MenuPath = missionCommands.addSubMenuForGroup( self.GroupID, MenuText, self.MenuParentPath )
self:SetParentMenu( self.MenuText, self ) self:SetParentMenu( self.MenuText, self )
@@ -875,12 +813,10 @@ do
end end
end end
--- Refreshes a new radio item for a group and submenus --- Refreshes a new radio item for a group and submenus
-- @param #MENU_GROUP self -- @param #MENU_GROUP self
-- @return #MENU_GROUP -- @return #MENU_GROUP
function MENU_GROUP:Refresh() function MENU_GROUP:Refresh()
do do
missionCommands.removeItemForGroup( self.GroupID, self.MenuPath ) missionCommands.removeItemForGroup( self.GroupID, self.MenuPath )
missionCommands.addSubMenuForGroup( self.GroupID, self.MenuText, self.MenuParentPath ) missionCommands.addSubMenuForGroup( self.GroupID, self.MenuText, self.MenuParentPath )
@@ -890,6 +826,7 @@ do
end end
end end
return self
end end
--- Removes the sub menus recursively of this MENU_GROUP. --- Removes the sub menus recursively of this MENU_GROUP.
@@ -898,7 +835,6 @@ do
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set. -- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
-- @return #MENU_GROUP self -- @return #MENU_GROUP self
function MENU_GROUP:RemoveSubMenus( MenuStamp, MenuTag ) function MENU_GROUP:RemoveSubMenus( MenuStamp, MenuTag )
for MenuText, Menu in pairs( self.Menus or {} ) do for MenuText, Menu in pairs( self.Menus or {} ) do
Menu:Remove( MenuStamp, MenuTag ) Menu:Remove( MenuStamp, MenuTag )
end end
@@ -913,15 +849,13 @@ do
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set. -- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
-- @return #nil -- @return #nil
function MENU_GROUP:Remove( MenuStamp, MenuTag ) function MENU_GROUP:Remove( MenuStamp, MenuTag )
MENU_INDEX:PrepareGroup( self.Group ) MENU_INDEX:PrepareGroup( self.Group )
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText ) local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
local GroupMenu = MENU_INDEX:HasGroupMenu( self.Group, Path ) local GroupMenu = MENU_INDEX:HasGroupMenu( self.Group, Path )
if GroupMenu == self then if GroupMenu == self then
self:RemoveSubMenus( MenuStamp, MenuTag ) self:RemoveSubMenus( MenuStamp, MenuTag )
if not MenuStamp or self.MenuStamp ~= MenuStamp then if not MenuStamp or self.MenuStamp ~= MenuStamp then
if (not MenuTag) or (MenuTag and self.MenuTag and MenuTag == self.MenuTag) then if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
if self.MenuPath ~= nil then if self.MenuPath ~= nil then
self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } ) self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } )
missionCommands.removeItemForGroup( self.GroupID, self.MenuPath ) missionCommands.removeItemForGroup( self.GroupID, self.MenuPath )
@@ -939,6 +873,7 @@ do
return self return self
end end
--- @type MENU_GROUP_COMMAND --- @type MENU_GROUP_COMMAND
-- @extends Core.Menu#MENU_COMMAND_BASE -- @extends Core.Menu#MENU_COMMAND_BASE
@@ -948,7 +883,7 @@ do
-- --
-- @field #MENU_GROUP_COMMAND -- @field #MENU_GROUP_COMMAND
MENU_GROUP_COMMAND = { MENU_GROUP_COMMAND = {
ClassName = "MENU_GROUP_COMMAND", ClassName = "MENU_GROUP_COMMAND"
} }
--- Creates a new radio command item for a group --- Creates a new radio command item for a group
@@ -960,18 +895,15 @@ do
-- @param CommandMenuArgument An argument for the function. -- @param CommandMenuArgument An argument for the function.
-- @return #MENU_GROUP_COMMAND -- @return #MENU_GROUP_COMMAND
function MENU_GROUP_COMMAND:New( Group, MenuText, ParentMenu, CommandMenuFunction, ... ) function MENU_GROUP_COMMAND:New( Group, MenuText, ParentMenu, CommandMenuFunction, ... )
MENU_INDEX:PrepareGroup( Group ) MENU_INDEX:PrepareGroup( Group )
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText ) local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
local GroupMenu = MENU_INDEX:HasGroupMenu( Group, Path ) local GroupMenu = MENU_INDEX:HasGroupMenu( Group, Path )
if GroupMenu then if GroupMenu then
GroupMenu:SetCommandMenuFunction( CommandMenuFunction ) GroupMenu:SetCommandMenuFunction( CommandMenuFunction )
GroupMenu:SetCommandMenuArguments( arg ) GroupMenu:SetCommandMenuArguments( arg )
return GroupMenu return GroupMenu
else else
self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) ) self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) )
MENU_INDEX:SetGroupMenu( Group, Path, self ) MENU_INDEX:SetGroupMenu( Group, Path, self )
self.Group = Group self.Group = Group
@@ -982,19 +914,17 @@ do
self:SetParentMenu( self.MenuText, self ) self:SetParentMenu( self.MenuText, self )
return self return self
end end
end end
--- Refreshes a radio item for a group --- Refreshes a radio item for a group
-- @param #MENU_GROUP_COMMAND self -- @param #MENU_GROUP_COMMAND self
-- @return #MENU_GROUP_COMMAND -- @return #MENU_GROUP_COMMAND
function MENU_GROUP_COMMAND:Refresh() function MENU_GROUP_COMMAND:Refresh()
do do
missionCommands.removeItemForGroup( self.GroupID, self.MenuPath ) missionCommands.removeItemForGroup( self.GroupID, self.MenuPath )
missionCommands.addCommandForGroup( self.GroupID, self.MenuText, self.MenuParentPath, self.MenuCallHandler ) missionCommands.addCommandForGroup( self.GroupID, self.MenuText, self.MenuParentPath, self.MenuCallHandler )
end end
return self
end end
--- Removes a menu structure for a group. --- Removes a menu structure for a group.
@@ -1003,16 +933,14 @@ do
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set. -- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
-- @return #nil -- @return #nil
function MENU_GROUP_COMMAND:Remove( MenuStamp, MenuTag ) function MENU_GROUP_COMMAND:Remove( MenuStamp, MenuTag )
MENU_INDEX:PrepareGroup( self.Group ) MENU_INDEX:PrepareGroup( self.Group )
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText ) local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
local GroupMenu = MENU_INDEX:HasGroupMenu( self.Group, Path ) local GroupMenu = MENU_INDEX:HasGroupMenu( self.Group, Path )
if GroupMenu == self then if GroupMenu == self then
if not MenuStamp or self.MenuStamp ~= MenuStamp then if not MenuStamp or self.MenuStamp ~= MenuStamp then
if (not MenuTag) or (MenuTag and self.MenuTag and MenuTag == self.MenuTag) then if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
if self.MenuPath ~= nil then if self.MenuPath ~= nil then
self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } ) self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } )
missionCommands.removeItemForGroup( self.GroupID, self.MenuPath ) missionCommands.removeItemForGroup( self.GroupID, self.MenuPath )
end end
MENU_INDEX:ClearGroupMenu( self.Group, Path ) MENU_INDEX:ClearGroupMenu( self.Group, Path )
@@ -1026,16 +954,13 @@ do
return self return self
end end
end end
--- MENU_GROUP_DELAYED --- MENU_GROUP_DELAYED
do do
--- @type MENU_GROUP_DELAYED --- @type MENU_GROUP_DELAYED
-- @extends Core.Menu#MENU_BASE -- @extends Core.Menu#MENU_BASE
--- The MENU_GROUP_DELAYED class manages the main menus for groups. --- The MENU_GROUP_DELAYED class manages the main menus for groups.
-- You can add menus with the @{#MENU_GROUP.New} method, which constructs a MENU_GROUP object and returns you the object reference. -- You can add menus with the @{#MENU_GROUP.New} method, which constructs a MENU_GROUP object and returns you the object reference.
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_GROUP.Remove}. -- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_GROUP.Remove}.
@@ -1045,7 +970,7 @@ do
-- --
-- @field #MENU_GROUP_DELAYED -- @field #MENU_GROUP_DELAYED
MENU_GROUP_DELAYED = { MENU_GROUP_DELAYED = {
ClassName = "MENU_GROUP_DELAYED", ClassName = "MENU_GROUP_DELAYED"
} }
--- MENU_GROUP_DELAYED constructor. Creates a new radio menu item for a group. --- MENU_GROUP_DELAYED constructor. Creates a new radio menu item for a group.
@@ -1059,16 +984,13 @@ do
MENU_INDEX:PrepareGroup( Group ) MENU_INDEX:PrepareGroup( Group )
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText ) local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
local GroupMenu = MENU_INDEX:HasGroupMenu( Group, Path ) local GroupMenu = MENU_INDEX:HasGroupMenu( Group, Path )
if GroupMenu then if GroupMenu then
return GroupMenu return GroupMenu
else else
self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) )
MENU_INDEX:SetGroupMenu( Group, Path, self ) MENU_INDEX:SetGroupMenu( Group, Path, self )
self.Group = Group self.Group = Group
self.GroupID = Group:GetID() self.GroupID = Group:GetID()
if self.MenuParentPath then if self.MenuParentPath then
self.MenuPath = UTILS.DeepCopy( self.MenuParentPath ) self.MenuPath = UTILS.DeepCopy( self.MenuParentPath )
else else
@@ -1086,7 +1008,6 @@ do
-- @param #MENU_GROUP_DELAYED self -- @param #MENU_GROUP_DELAYED self
-- @return #MENU_GROUP_DELAYED -- @return #MENU_GROUP_DELAYED
function MENU_GROUP_DELAYED:Set() function MENU_GROUP_DELAYED:Set()
do do
if not self.MenuSet then if not self.MenuSet then
missionCommands.addSubMenuForGroup( self.GroupID, self.MenuText, self.MenuParentPath ) missionCommands.addSubMenuForGroup( self.GroupID, self.MenuText, self.MenuParentPath )
@@ -1097,14 +1018,12 @@ do
Menu:Set() Menu:Set()
end end
end end
end end
--- Refreshes a new radio item for a group and submenus --- Refreshes a new radio item for a group and submenus
-- @param #MENU_GROUP_DELAYED self -- @param #MENU_GROUP_DELAYED self
-- @return #MENU_GROUP_DELAYED -- @return #MENU_GROUP_DELAYED
function MENU_GROUP_DELAYED:Refresh() function MENU_GROUP_DELAYED:Refresh()
do do
missionCommands.removeItemForGroup( self.GroupID, self.MenuPath ) missionCommands.removeItemForGroup( self.GroupID, self.MenuPath )
missionCommands.addSubMenuForGroup( self.GroupID, self.MenuText, self.MenuParentPath ) missionCommands.addSubMenuForGroup( self.GroupID, self.MenuText, self.MenuParentPath )
@@ -1114,6 +1033,7 @@ do
end end
end end
return self
end end
--- Removes the sub menus recursively of this MENU_GROUP_DELAYED. --- Removes the sub menus recursively of this MENU_GROUP_DELAYED.
@@ -1122,7 +1042,6 @@ do
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set. -- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
-- @return #MENU_GROUP_DELAYED self -- @return #MENU_GROUP_DELAYED self
function MENU_GROUP_DELAYED:RemoveSubMenus( MenuStamp, MenuTag ) function MENU_GROUP_DELAYED:RemoveSubMenus( MenuStamp, MenuTag )
for MenuText, Menu in pairs( self.Menus or {} ) do for MenuText, Menu in pairs( self.Menus or {} ) do
Menu:Remove( MenuStamp, MenuTag ) Menu:Remove( MenuStamp, MenuTag )
end end
@@ -1137,15 +1056,13 @@ do
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set. -- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
-- @return #nil -- @return #nil
function MENU_GROUP_DELAYED:Remove( MenuStamp, MenuTag ) function MENU_GROUP_DELAYED:Remove( MenuStamp, MenuTag )
MENU_INDEX:PrepareGroup( self.Group ) MENU_INDEX:PrepareGroup( self.Group )
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText ) local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
local GroupMenu = MENU_INDEX:HasGroupMenu( self.Group, Path ) local GroupMenu = MENU_INDEX:HasGroupMenu( self.Group, Path )
if GroupMenu == self then if GroupMenu == self then
self:RemoveSubMenus( MenuStamp, MenuTag ) self:RemoveSubMenus( MenuStamp, MenuTag )
if not MenuStamp or self.MenuStamp ~= MenuStamp then if not MenuStamp or self.MenuStamp ~= MenuStamp then
if (not MenuTag) or (MenuTag and self.MenuTag and MenuTag == self.MenuTag) then if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
if self.MenuPath ~= nil then if self.MenuPath ~= nil then
self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } ) self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } )
missionCommands.removeItemForGroup( self.GroupID, self.MenuPath ) missionCommands.removeItemForGroup( self.GroupID, self.MenuPath )
@@ -1163,6 +1080,7 @@ do
return self return self
end end
--- @type MENU_GROUP_COMMAND_DELAYED --- @type MENU_GROUP_COMMAND_DELAYED
-- @extends Core.Menu#MENU_COMMAND_BASE -- @extends Core.Menu#MENU_COMMAND_BASE
@@ -1173,7 +1091,7 @@ do
-- --
-- @field #MENU_GROUP_COMMAND_DELAYED -- @field #MENU_GROUP_COMMAND_DELAYED
MENU_GROUP_COMMAND_DELAYED = { MENU_GROUP_COMMAND_DELAYED = {
ClassName = "MENU_GROUP_COMMAND_DELAYED", ClassName = "MENU_GROUP_COMMAND_DELAYED"
} }
--- Creates a new radio command item for a group --- Creates a new radio command item for a group
@@ -1185,18 +1103,15 @@ do
-- @param CommandMenuArgument An argument for the function. -- @param CommandMenuArgument An argument for the function.
-- @return #MENU_GROUP_COMMAND_DELAYED -- @return #MENU_GROUP_COMMAND_DELAYED
function MENU_GROUP_COMMAND_DELAYED:New( Group, MenuText, ParentMenu, CommandMenuFunction, ... ) function MENU_GROUP_COMMAND_DELAYED:New( Group, MenuText, ParentMenu, CommandMenuFunction, ... )
MENU_INDEX:PrepareGroup( Group ) MENU_INDEX:PrepareGroup( Group )
local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText ) local Path = MENU_INDEX:ParentPath( ParentMenu, MenuText )
local GroupMenu = MENU_INDEX:HasGroupMenu( Group, Path ) local GroupMenu = MENU_INDEX:HasGroupMenu( Group, Path )
if GroupMenu then if GroupMenu then
GroupMenu:SetCommandMenuFunction( CommandMenuFunction ) GroupMenu:SetCommandMenuFunction( CommandMenuFunction )
GroupMenu:SetCommandMenuArguments( arg ) GroupMenu:SetCommandMenuArguments( arg )
return GroupMenu return GroupMenu
else else
self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) ) self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) )
MENU_INDEX:SetGroupMenu( Group, Path, self ) MENU_INDEX:SetGroupMenu( Group, Path, self )
self.Group = Group self.Group = Group
@@ -1212,33 +1127,29 @@ do
self:SetParentMenu( self.MenuText, self ) self:SetParentMenu( self.MenuText, self )
return self return self
end end
end end
--- Refreshes a radio item for a group --- Refreshes a radio item for a group
-- @param #MENU_GROUP_COMMAND_DELAYED self -- @param #MENU_GROUP_COMMAND_DELAYED self
-- @return #MENU_GROUP_COMMAND_DELAYED -- @return #MENU_GROUP_COMMAND_DELAYED
function MENU_GROUP_COMMAND_DELAYED:Set() function MENU_GROUP_COMMAND_DELAYED:Set()
do do
if not self.MenuSet then if not self.MenuSet then
self.MenuPath = missionCommands.addCommandForGroup( self.GroupID, self.MenuText, self.MenuParentPath, self.MenuCallHandler ) self.MenuPath = missionCommands.addCommandForGroup( self.GroupID, self.MenuText, self.MenuParentPath, self.MenuCallHandler )
self.MenuSet = true self.MenuSet = true
end end
end end
end end
--- Refreshes a radio item for a group --- Refreshes a radio item for a group
-- @param #MENU_GROUP_COMMAND_DELAYED self -- @param #MENU_GROUP_COMMAND_DELAYED self
-- @return #MENU_GROUP_COMMAND_DELAYED -- @return #MENU_GROUP_COMMAND_DELAYED
function MENU_GROUP_COMMAND_DELAYED:Refresh() function MENU_GROUP_COMMAND_DELAYED:Refresh()
do do
missionCommands.removeItemForGroup( self.GroupID, self.MenuPath ) missionCommands.removeItemForGroup( self.GroupID, self.MenuPath )
missionCommands.addCommandForGroup( self.GroupID, self.MenuText, self.MenuParentPath, self.MenuCallHandler ) missionCommands.addCommandForGroup( self.GroupID, self.MenuText, self.MenuParentPath, self.MenuCallHandler )
end end
return self
end end
--- Removes a menu structure for a group. --- Removes a menu structure for a group.
@@ -1247,14 +1158,12 @@ do
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set. -- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
-- @return #nil -- @return #nil
function MENU_GROUP_COMMAND_DELAYED:Remove( MenuStamp, MenuTag ) function MENU_GROUP_COMMAND_DELAYED:Remove( MenuStamp, MenuTag )
MENU_INDEX:PrepareGroup( self.Group ) MENU_INDEX:PrepareGroup( self.Group )
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText ) local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
local GroupMenu = MENU_INDEX:HasGroupMenu( self.Group, Path ) local GroupMenu = MENU_INDEX:HasGroupMenu( self.Group, Path )
if GroupMenu == self then if GroupMenu == self then
if not MenuStamp or self.MenuStamp ~= MenuStamp then if not MenuStamp or self.MenuStamp ~= MenuStamp then
if (not MenuTag) or (MenuTag and self.MenuTag and MenuTag == self.MenuTag) then if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
if self.MenuPath ~= nil then if self.MenuPath ~= nil then
self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } ) self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } )
missionCommands.removeItemForGroup( self.GroupID, self.MenuPath ) missionCommands.removeItemForGroup( self.GroupID, self.MenuPath )
@@ -1270,6 +1179,5 @@ do
return self return self
end end
end end
+34 -3
View File
@@ -10,6 +10,8 @@
-- * Send message to all players. -- * Send message to all players.
-- * Send messages to a coalition. -- * Send messages to a coalition.
-- * Send messages to a specific group. -- * Send messages to a specific group.
-- * Send messages to a specific unit or client.
--
-- --
-- === -- ===
-- --
@@ -35,6 +37,7 @@
-- --
-- * To a @{Client} using @{#MESSAGE.ToClient}(). -- * To a @{Client} using @{#MESSAGE.ToClient}().
-- * To a @{Wrapper.Group} using @{#MESSAGE.ToGroup}() -- * To a @{Wrapper.Group} using @{#MESSAGE.ToGroup}()
-- * To a @{Wrapper.Unit} using @{#MESSAGE.ToUnit}()
-- * To a coalition using @{#MESSAGE.ToCoalition}(). -- * To a coalition using @{#MESSAGE.ToCoalition}().
-- * To the red coalition using @{#MESSAGE.ToRed}(). -- * To the red coalition using @{#MESSAGE.ToRed}().
-- * To the blue coalition using @{#MESSAGE.ToBlue}(). -- * To the blue coalition using @{#MESSAGE.ToBlue}().
@@ -194,15 +197,18 @@ function MESSAGE:ToClient( Client, Settings )
if Client and Client:GetClientGroupID() then if Client and Client:GetClientGroupID() then
if self.MessageType then if self.MessageType then
local Settings = Settings or (Client and _DATABASE:GetPlayerSettings( Client:GetPlayerName() )) or _SETTINGS -- Core.Settings#SETTINGS local Settings = Settings or ( Client and _DATABASE:GetPlayerSettings( Client:GetPlayerName() ) ) or _SETTINGS -- Core.Settings#SETTINGS
self.MessageDuration = Settings:GetMessageTime( self.MessageType ) self.MessageDuration = Settings:GetMessageTime( self.MessageType )
self.MessageCategory = "" -- self.MessageType .. ": " self.MessageCategory = "" -- self.MessageType .. ": "
end end
local Unit = Client:GetClient()
if self.MessageDuration ~= 0 then if self.MessageDuration ~= 0 then
local ClientGroupID = Client:GetClientGroupID() local ClientGroupID = Client:GetClientGroupID()
self:T( self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ) .. " / " .. self.MessageDuration ) self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
trigger.action.outTextForGroup( ClientGroupID, self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ), self.MessageDuration, self.ClearScreen ) --trigger.action.outTextForGroup( ClientGroupID, self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration , self.ClearScreen)
trigger.action.outTextForUnit( Unit:GetID(), self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration , self.ClearScreen)
end end
end end
@@ -232,6 +238,31 @@ function MESSAGE:ToGroup( Group, Settings )
return self return self
end end
--- Sends a MESSAGE to a Unit.
-- @param #MESSAGE self
-- @param Wrapper.Unit#UNIT Unit to which the message is displayed.
-- @return #MESSAGE Message object.
function MESSAGE:ToUnit( Unit, Settings )
self:F( Unit.IdentifiableName )
if Unit then
if self.MessageType then
local Settings = Settings or ( Unit and _DATABASE:GetPlayerSettings( Unit:GetPlayerName() ) ) or _SETTINGS -- Core.Settings#SETTINGS
self.MessageDuration = Settings:GetMessageTime( self.MessageType )
self.MessageCategory = "" -- self.MessageType .. ": "
end
if self.MessageDuration ~= 0 then
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
trigger.action.outTextForUnit( Unit:GetID(), self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration, self.ClearScreen )
end
end
return self
end
--- Sends a MESSAGE to the Blue coalition. --- Sends a MESSAGE to the Blue coalition.
-- @param #MESSAGE self -- @param #MESSAGE self
-- @return #MESSAGE -- @return #MESSAGE
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -14,7 +14,7 @@
-- --
-- # Demo Missions -- # Demo Missions
-- --
-- ### [SCHEDULER Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/SCH%20-%20Scheduler) -- ### [SCHEDULER Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SCH%20-%20Scheduler)
-- --
-- ### [SCHEDULER Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SCH%20-%20Scheduler) -- ### [SCHEDULER Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SCH%20-%20Scheduler)
-- --
+20 -9
View File
@@ -201,7 +201,7 @@ do -- SET_BASE
self:F2( { ObjectName = ObjectName } ) self:F2( { ObjectName = ObjectName } )
local TriggerEvent = true local TriggerEvent = true
if NoTriggerEvent == false then if NoTriggerEvent then
TriggerEvent = false TriggerEvent = false
end end
@@ -317,7 +317,7 @@ do -- SET_BASE
for _, Object in pairs( union.Set ) do for _, Object in pairs( union.Set ) do
if self:IsIncludeObject( Object ) and SetB:IsIncludeObject( Object ) then if self:IsIncludeObject( Object ) and SetB:IsIncludeObject( Object ) then
intersection:AddObject( intersection ) intersection:AddObject( Object )
end end
end end
@@ -1004,7 +1004,7 @@ do -- SET_GROUP
--- Gets the Set. --- Gets the Set.
-- @param #SET_GROUP self -- @param #SET_GROUP self
-- @return #SET_GROUP self -- @return #table Table of objects
function SET_GROUP:GetAliveSet() function SET_GROUP:GetAliveSet()
self:F2() self:F2()
@@ -1065,9 +1065,14 @@ do -- SET_GROUP
self:Add( group:GetName(), group ) self:Add( group:GetName(), group )
-- I set the default cargo bay weight limit each time a new group is added to the set. if not DontSetCargoBayLimit then
for UnitID, UnitData in pairs( group:GetUnits() ) do -- I set the default cargo bay weight limit each time a new group is added to the set.
UnitData:SetCargoBayWeightLimit() -- TODO Why is this here in the first place?
for UnitID, UnitData in pairs( group:GetUnits() ) do
if UnitData and UnitData:IsAlive() then
UnitData:SetCargoBayWeightLimit()
end
end
end end
return self return self
@@ -1123,7 +1128,9 @@ do -- SET_GROUP
local NearestGroup = nil -- Wrapper.Group#GROUP local NearestGroup = nil -- Wrapper.Group#GROUP
local ClosestDistance = nil local ClosestDistance = nil
for ObjectID, ObjectData in pairs( self.Set ) do local Set = self:GetAliveSet()
for ObjectID, ObjectData in pairs( Set ) do
if NearestGroup == nil then if NearestGroup == nil then
NearestGroup = ObjectData NearestGroup = ObjectData
ClosestDistance = PointVec2:DistanceFromPointVec2( ObjectData:GetCoordinate() ) ClosestDistance = PointVec2:DistanceFromPointVec2( ObjectData:GetCoordinate() )
@@ -1327,7 +1334,11 @@ do -- SET_GROUP
if Event.IniDCSUnit then if Event.IniDCSUnit then
local ObjectName, Object = self:FindInDatabase( Event ) local ObjectName, Object = self:FindInDatabase( Event )
if ObjectName then if ObjectName then
if Event.IniDCSGroup:getSize() == 1 then -- Only remove if the last unit of the group was destroyed. local size = 1
if Event.IniDCSGroup then
size = Event.IniDCSGroup:getSize()
end
if size == 1 then -- Only remove if the last unit of the group was destroyed.
self:Remove( ObjectName ) self:Remove( ObjectName )
end end
end end
@@ -5618,7 +5629,7 @@ do -- SET_ZONE
if self.Filter.Prefixes then if self.Filter.Prefixes then
local MZonePrefix = false local MZonePrefix = false
for ZonePrefixId, ZonePrefix in pairs( self.Filter.Prefixes ) do for ZonePrefixId, ZonePrefix in pairs( self.Filter.Prefixes ) do
self:T3( { "Prefix:", string.find( MZoneName, ZonePrefix, 1 ), ZonePrefix } ) self:T2( { "Prefix:", string.find( MZoneName, ZonePrefix, 1 ), ZonePrefix } )
if string.find( MZoneName, ZonePrefix, 1 ) then if string.find( MZoneName, ZonePrefix, 1 ) then
MZonePrefix = true MZonePrefix = true
end end
+40 -18
View File
@@ -30,7 +30,7 @@
-- --
-- === -- ===
-- --
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/SPA%20-%20Spawning) -- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SPA%20-%20Spawning)
-- --
-- === -- ===
-- --
@@ -1373,7 +1373,7 @@ function SPAWN:SpawnWithIndex( SpawnIndex, NoBirth )
SpawnTemplate.modulation = self.SpawnInitModu SpawnTemplate.modulation = self.SpawnInitModu
end end
-- Set country, coaliton and categroy. -- Set country, coalition and category.
SpawnTemplate.CategoryID = self.SpawnInitCategory or SpawnTemplate.CategoryID SpawnTemplate.CategoryID = self.SpawnInitCategory or SpawnTemplate.CategoryID
SpawnTemplate.CountryID = self.SpawnInitCountry or SpawnTemplate.CountryID SpawnTemplate.CountryID = self.SpawnInitCountry or SpawnTemplate.CountryID
SpawnTemplate.CoalitionID = self.SpawnInitCoalition or SpawnTemplate.CoalitionID SpawnTemplate.CoalitionID = self.SpawnInitCoalition or SpawnTemplate.CoalitionID
@@ -2360,7 +2360,7 @@ end
-- The known AIRBASE objects are automatically imported at mission start by MOOSE. -- The known AIRBASE objects are automatically imported at mission start by MOOSE.
-- Therefore, there isn't any New() constructor defined for AIRBASE objects. -- Therefore, there isn't any New() constructor defined for AIRBASE objects.
-- --
-- Ships and Farps are added within the mission, and are therefore not known. -- Ships and FARPs are added within the mission, and are therefore not known.
-- For these AIRBASE objects, there isn't an @{Wrapper.Airbase#AIRBASE} enumeration defined. -- For these AIRBASE objects, there isn't an @{Wrapper.Airbase#AIRBASE} enumeration defined.
-- You need to provide the **exact name** of the airbase as the parameter to the @{Wrapper.Airbase#AIRBASE.FindByName}() method! -- You need to provide the **exact name** of the airbase as the parameter to the @{Wrapper.Airbase#AIRBASE.FindByName}() method!
-- --
@@ -2827,18 +2827,37 @@ end
-- The method will search for a #-mark, and will return the text before the #-mark. -- The method will search for a #-mark, and will return the text before the #-mark.
-- It will return nil of no prefix was found. -- It will return nil of no prefix was found.
-- @param #SPAWN self -- @param #SPAWN self
-- @param DCS#UNIT DCSUnit The @{DCSUnit} to be searched. -- @param Wrapper.Group#GROUP SpawnGroup The GROUP object.
-- @return #string The prefix -- @return #string The prefix or #nil if nothing was found.
-- @return #nil Nothing found
function SPAWN:_GetPrefixFromGroup( SpawnGroup ) function SPAWN:_GetPrefixFromGroup( SpawnGroup )
self:F3( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnGroup } )
local GroupName = SpawnGroup:GetName() local GroupName = SpawnGroup:GetName()
if GroupName then if GroupName then
local SpawnPrefix = string.match( GroupName, ".*#" )
local SpawnPrefix=self:_GetPrefixFromGroupName(GroupName)
return SpawnPrefix
end
return nil
end
--- Return the prefix of a spawned group.
-- The method will search for a `#`-mark, and will return the text before the `#`-mark. It will return nil of no prefix was found.
-- @param #SPAWN self
-- @param #string SpawnGroupName The name of the spawned group.
-- @return #string The prefix or #nil if nothing was found.
function SPAWN:_GetPrefixFromGroupName(SpawnGroupName)
if SpawnGroupName then
local SpawnPrefix=string.match(SpawnGroupName, ".*#")
if SpawnPrefix then if SpawnPrefix then
SpawnPrefix = SpawnPrefix:sub( 1, -2 ) SpawnPrefix = SpawnPrefix:sub(1, -2)
end end
return SpawnPrefix return SpawnPrefix
end end
@@ -3235,24 +3254,27 @@ function SPAWN:_OnBirth( EventData )
end end
--- Obscolete
-- @todo Need to delete this... _DATABASE does this now ...
--- @param #SPAWN self --- @param #SPAWN self
-- @param Core.Event#EVENTDATA EventData -- @param Core.Event#EVENTDATA EventData
function SPAWN:_OnDeadOrCrash( EventData ) function SPAWN:_OnDeadOrCrash( EventData )
self:F( self.SpawnTemplatePrefix ) self:F( self.SpawnTemplatePrefix )
local SpawnGroup = EventData.IniGroup local unit=UNIT:FindByName(EventData.IniUnitName)
if unit then
local EventPrefix = self:_GetPrefixFromGroupName(unit.GroupName)
if SpawnGroup then
local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup )
if EventPrefix then -- EventPrefix can be nil if no # is found, which means, no spawnable group! if EventPrefix then -- EventPrefix can be nil if no # is found, which means, no spawnable group!
self:T( { "Dead event: " .. EventPrefix } ) self:T( { "Dead event: " .. EventPrefix } )
if EventPrefix == self.SpawnTemplatePrefix or (self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix) then
self.AliveUnits = self.AliveUnits - 1 if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then
self:T( "Alive Units: " .. self.AliveUnits )
self.AliveUnits = self.AliveUnits - 1
self:T( "Alive Units: " .. self.AliveUnits )
end end
end end
end end
end end
+23 -12
View File
@@ -46,14 +46,14 @@
-- @field #number InitOffsetAngle Link offset angle in degrees. -- @field #number InitOffsetAngle Link offset angle in degrees.
-- @field #number InitStaticHeading Heading of the static. -- @field #number InitStaticHeading Heading of the static.
-- @field #string InitStaticLivery Livery for aircraft. -- @field #string InitStaticLivery Livery for aircraft.
-- @field #string InitStaticShape Shape of teh static. -- @field #string InitStaticShape Shape of the static.
-- @field #string InitStaticType Type of the static. -- @field #string InitStaticType Type of the static.
-- @field #string InitStaticCategory Categrory of the static. -- @field #string InitStaticCategory Categrory of the static.
-- @field #string InitStaticName Name of the static. -- @field #string InitStaticName Name of the static.
-- @field Core.Point#COORDINATE InitStaticCoordinate Coordinate where to spawn the static. -- @field Core.Point#COORDINATE InitStaticCoordinate Coordinate where to spawn the static.
-- @field #boolean InitDead Set static to be dead if true. -- @field #boolean InitStaticDead Set static to be dead if true.
-- @field #boolean InitCargo If true, static can act as cargo. -- @field #boolean InitStaticCargo If true, static can act as cargo.
-- @field #number InitCargoMass Mass of cargo in kg. -- @field #number InitStaticCargoMass Mass of cargo in kg.
-- @extends Core.Base#BASE -- @extends Core.Base#BASE
@@ -260,7 +260,7 @@ end
-- @param #number Mass Mass of the cargo in kg. -- @param #number Mass Mass of the cargo in kg.
-- @return #SPAWNSTATIC self -- @return #SPAWNSTATIC self
function SPAWNSTATIC:InitCargoMass(Mass) function SPAWNSTATIC:InitCargoMass(Mass)
self.InitCargoMass=Mass self.InitStaticCargoMass=Mass
return self return self
end end
@@ -269,7 +269,16 @@ end
-- @param #boolean IsCargo If true, this static can act as cargo. -- @param #boolean IsCargo If true, this static can act as cargo.
-- @return #SPAWNSTATIC self -- @return #SPAWNSTATIC self
function SPAWNSTATIC:InitCargo(IsCargo) function SPAWNSTATIC:InitCargo(IsCargo)
self.InitCargo=IsCargo self.InitStaticCargo=IsCargo
return self
end
--- Initialize as dead.
-- @param #SPAWNSTATIC self
-- @param #boolean IsCargo If true, this static is dead.
-- @return #SPAWNSTATIC self
function SPAWNSTATIC:InitDead(IsDead)
self.InitStaticDead=IsDead
return self return self
end end
@@ -417,16 +426,16 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
Template.livery_id=self.InitStaticLivery Template.livery_id=self.InitStaticLivery
end end
if self.InitDead~=nil then if self.InitStaticDead~=nil then
Template.dead=self.InitDead Template.dead=self.InitStaticDead
end end
if self.InitCargo~=nil then if self.InitStaticCargo~=nil then
Template.canCargo=self.InitCargo Template.canCargo=self.InitStaticCargo
end end
if self.InitCargoMass~=nil then if self.InitStaticCargoMass~=nil then
Template.mass=self.InitCargoMass Template.mass=self.InitStaticCargoMass
end end
if self.InitLinkUnit then if self.InitLinkUnit then
@@ -479,6 +488,8 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
-- ED's dirty way to spawn FARPS. -- ED's dirty way to spawn FARPS.
Static=coalition.addGroup(CountryID, -1, TemplateGroup) Static=coalition.addGroup(CountryID, -1, TemplateGroup)
else else
self:T("Spawning Static")
self:T2({Template=Template})
Static=coalition.addStaticObject(CountryID, Template) Static=coalition.addStaticObject(CountryID, Template)
end end
+135 -67
View File
@@ -9,7 +9,7 @@
-- * Create polygon zones. -- * Create polygon zones.
-- * Create moving zones around a unit. -- * Create moving zones around a unit.
-- * Create moving zones around a group. -- * Create moving zones around a group.
-- * Provide the zone behaviour. Some zones are static, while others are moveable. -- * Provide the zone behavior. Some zones are static, while others are moveable.
-- * Enquiry if a coordinate is within a zone. -- * Enquiry if a coordinate is within a zone.
-- * Smoke zones. -- * Smoke zones.
-- * Set a zone probability to control zone selection. -- * Set a zone probability to control zone selection.
@@ -20,10 +20,10 @@
-- * Draw zones (circular and polygon) on the F10 map. -- * Draw zones (circular and polygon) on the F10 map.
-- --
-- --
-- There are essentially two core functions that zones accomodate: -- There are essentially two core functions that zones accommodate:
-- --
-- * Test if an object is within the zone boundaries. -- * Test if an object is within the zone boundaries.
-- * Provide the zone behaviour. Some zones are static, while others are moveable. -- * Provide the zone behavior. Some zones are static, while others are moveable.
-- --
-- The object classes are using the zone classes to test the zone boundaries, which can take various forms: -- The object classes are using the zone classes to test the zone boundaries, which can take various forms:
-- --
@@ -59,6 +59,7 @@
-- @field #number ZoneProbability A value between 0 and 1. 0 = 0% and 1 = 100% probability. -- @field #number ZoneProbability A value between 0 and 1. 0 = 0% and 1 = 100% probability.
-- @field #number DrawID Unique ID of the drawn zone on the F10 map. -- @field #number DrawID Unique ID of the drawn zone on the F10 map.
-- @field #table Color Table with four entries, e.g. {1, 0, 0, 0.15}. First three are RGB color code. Fourth is the transparency Alpha value. -- @field #table Color Table with four entries, e.g. {1, 0, 0, 0.15}. First three are RGB color code. Fourth is the transparency Alpha value.
-- @field #number ZoneID ID of zone. Only zones defined in the ME have an ID!
-- @extends Core.Fsm#FSM -- @extends Core.Fsm#FSM
@@ -108,7 +109,8 @@ ZONE_BASE = {
ZoneName = "", ZoneName = "",
ZoneProbability = 1, ZoneProbability = 1,
DrawID=nil, DrawID=nil,
Color={} Color={},
ZoneID=nil,
} }
@@ -130,6 +132,8 @@ function ZONE_BASE:New( ZoneName )
self.ZoneName = ZoneName self.ZoneName = ZoneName
--_DATABASE:AddZone(ZoneName,self)
return self return self
end end
@@ -170,6 +174,7 @@ end
-- @param DCS#Vec3 Vec3 The point to test. -- @param DCS#Vec3 Vec3 The point to test.
-- @return #boolean true if the Vec3 is within the zone. -- @return #boolean true if the Vec3 is within the zone.
function ZONE_BASE:IsVec3InZone( Vec3 ) function ZONE_BASE:IsVec3InZone( Vec3 )
if not Vec3 then return false end
local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } ) local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } )
return InZone return InZone
end end
@@ -288,6 +293,23 @@ function ZONE_BASE:GetCoordinate( Height ) --R2.1
return self.Coordinate return self.Coordinate
end end
--- Get 2D distance to a coordinate.
-- @param #ZONE_BASE self
-- @param Core.Point#COORDINATE Coordinate Reference coordinate. Can also be a DCS#Vec2 or DCS#Vec3 object.
-- @return #number Distance to the reference coordinate in meters.
function ZONE_BASE:Get2DDistance(Coordinate)
local a=self:GetVec2()
local b={}
if Coordinate.z then
b.x=Coordinate.x
b.y=Coordinate.z
else
b.x=Coordinate.x
b.y=Coordinate.y
end
local dist=UTILS.VecDist2D(a,b)
return dist
end
--- Define a random @{DCS#Vec2} within the zone. --- Define a random @{DCS#Vec2} within the zone.
-- @param #ZONE_BASE self -- @param #ZONE_BASE self
@@ -601,7 +623,7 @@ function ZONE_RADIUS:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, Lin
Color=Color or self:GetColorRGB() Color=Color or self:GetColorRGB()
Alpha=Alpha or 1 Alpha=Alpha or 1
FillColor=FillColor or Color FillColor=FillColor or UTILS.DeepCopy(Color)
FillAlpha=FillAlpha or self:GetColorAlpha() FillAlpha=FillAlpha or self:GetColorAlpha()
self.DrawID=coordinate:CircleToAll(Radius, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly) self.DrawID=coordinate:CircleToAll(Radius, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly)
@@ -789,7 +811,7 @@ end
--- Scan the zone for the presence of units of the given ObjectCategories. --- Scan the zone for the presence of units of the given ObjectCategories.
-- Note that after a zone has been scanned, the zone can be evaluated by: -- Note that **only after** a zone has been scanned, the zone can be evaluated by:
-- --
-- * @{ZONE_RADIUS.IsAllInZoneOfCoalition}(): Scan the presence of units in the zone of a coalition. -- * @{ZONE_RADIUS.IsAllInZoneOfCoalition}(): Scan the presence of units in the zone of a coalition.
-- * @{ZONE_RADIUS.IsAllInZoneOfOtherCoalition}(): Scan the presence of units in the zone of an other coalition. -- * @{ZONE_RADIUS.IsAllInZoneOfOtherCoalition}(): Scan the presence of units in the zone of an other coalition.
@@ -798,10 +820,10 @@ end
-- * @{ZONE_RADIUS.IsNoneInZone}(): Scan if the zone is empty. -- * @{ZONE_RADIUS.IsNoneInZone}(): Scan if the zone is empty.
-- @{#ZONE_RADIUS. -- @{#ZONE_RADIUS.
-- @param #ZONE_RADIUS self -- @param #ZONE_RADIUS self
-- @param ObjectCategories An array of categories of the objects to find in the zone. -- @param ObjectCategories An array of categories of the objects to find in the zone. E.g. `{Object.Category.UNIT}`
-- @param UnitCategories An array of unit categories of the objects to find in the zone. -- @param UnitCategories An array of unit categories of the objects to find in the zone. E.g. `{Unit.Category.GROUND_UNIT,Unit.Category.SHIP}`
-- @usage -- @usage
-- self.Zone:Scan() -- self.Zone:Scan({Object.Category.UNIT},{Unit.Category.GROUND_UNIT})
-- local IsAttacked = self.Zone:IsSomeInZoneOfCoalition( self.Coalition ) -- local IsAttacked = self.Zone:IsSomeInZoneOfCoalition( self.Coalition )
function ZONE_RADIUS:Scan( ObjectCategories, UnitCategories ) function ZONE_RADIUS:Scan( ObjectCategories, UnitCategories )
@@ -1135,7 +1157,7 @@ end
-- @return #boolean true if the location is within the zone. -- @return #boolean true if the location is within the zone.
function ZONE_RADIUS:IsVec2InZone( Vec2 ) function ZONE_RADIUS:IsVec2InZone( Vec2 )
self:F2( Vec2 ) self:F2( Vec2 )
if not Vec2 then return false end
local ZoneVec2 = self:GetVec2() local ZoneVec2 = self:GetVec2()
if ZoneVec2 then if ZoneVec2 then
@@ -1153,7 +1175,7 @@ end
-- @return #boolean true if the point is within the zone. -- @return #boolean true if the point is within the zone.
function ZONE_RADIUS:IsVec3InZone( Vec3 ) function ZONE_RADIUS:IsVec3InZone( Vec3 )
self:F2( Vec3 ) self:F2( Vec3 )
if not Vec3 then return false end
local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } ) local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } )
return InZone return InZone
@@ -1161,24 +1183,54 @@ end
--- Returns a random Vec2 location within the zone. --- Returns a random Vec2 location within the zone.
-- @param #ZONE_RADIUS self -- @param #ZONE_RADIUS self
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. -- @param #number inner (Optional) Minimal distance from the center of the zone. Default is 0.
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone. -- @param #number outer (Optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
-- @param #table surfacetypes (Optional) Table of surface types. Can also be a single surface type. We will try max 100 times to find the right type!
-- @return DCS#Vec2 The random location within the zone. -- @return DCS#Vec2 The random location within the zone.
function ZONE_RADIUS:GetRandomVec2( inner, outer ) function ZONE_RADIUS:GetRandomVec2(inner, outer, surfacetypes)
self:F( self.ZoneName, inner, outer )
local Point = {}
local Vec2 = self:GetVec2() local Vec2 = self:GetVec2()
local _inner = inner or 0 local _inner = inner or 0
local _outer = outer or self:GetRadius() local _outer = outer or self:GetRadius()
local angle = math.random() * math.pi * 2; if surfacetypes and type(surfacetypes)~="table" then
Point.x = Vec2.x + math.cos( angle ) * math.random(_inner, _outer); surfacetypes={surfacetypes}
Point.y = Vec2.y + math.sin( angle ) * math.random(_inner, _outer); end
self:T( { Point } ) local function _getpoint()
local point = {}
local angle = math.random() * math.pi * 2
point.x = Vec2.x + math.cos(angle) * math.random(_inner, _outer)
point.y = Vec2.y + math.sin(angle) * math.random(_inner, _outer)
return point
end
return Point local function _checkSurface(point)
local stype=land.getSurfaceType(point)
for _,sf in pairs(surfacetypes) do
if sf==stype then
return true
end
end
return false
end
local point=_getpoint()
if surfacetypes then
local N=1 ; local Nmax=100 ; local gotit=false
while gotit==false and N<=Nmax do
gotit=_checkSurface(point)
if gotit then
--env.info(string.format("Got random coordinate with surface type %d after N=%d/%d iterations", land.getSurfaceType(point), N, Nmax))
else
point=_getpoint()
N=N+1
end
end
end
return point
end end
--- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone. --- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone.
@@ -1230,15 +1282,15 @@ end
--- Returns a @{Core.Point#COORDINATE} object reflecting a random 3D location within the zone. --- Returns a @{Core.Point#COORDINATE} object reflecting a random 3D location within the zone.
-- @param #ZONE_RADIUS self -- @param #ZONE_RADIUS self
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. -- @param #number inner (Optional) Minimal distance from the center of the zone. Default is 0.
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone. -- @param #number outer (Optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
-- @return Core.Point#COORDINATE -- @param #table surfacetypes (Optional) Table of surface types. Can also be a single surface type. We will try max 1000 times to find the right type!
function ZONE_RADIUS:GetRandomCoordinate( inner, outer ) -- @return Core.Point#COORDINATE The random coordinate.
self:F( self.ZoneName, inner, outer ) function ZONE_RADIUS:GetRandomCoordinate(inner, outer, surfacetypes)
local Coordinate = COORDINATE:NewFromVec2( self:GetRandomVec2(inner, outer) ) local vec2=self:GetRandomVec2(inner, outer, surfacetypes)
self:T3( { Coordinate = Coordinate } ) local Coordinate = COORDINATE:NewFromVec2(vec2)
return Coordinate return Coordinate
end end
@@ -1301,7 +1353,7 @@ function ZONE:New( ZoneName )
-- Error! -- Error!
if not Zone then if not Zone then
error( "Zone " .. ZoneName .. " does not exist." ) env.error( "ERROR: Zone " .. ZoneName .. " does not exist!" )
return nil return nil
end end
@@ -1811,7 +1863,8 @@ function ZONE_POLYGON_BASE:DrawZone(Coalition, Color, Alpha, FillColor, FillAlph
Color=Color or self:GetColorRGB() Color=Color or self:GetColorRGB()
Alpha=Alpha or 1 Alpha=Alpha or 1
FillColor=FillColor or Color
FillColor=FillColor or UTILS.DeepCopy(Color)
FillAlpha=FillAlpha or self:GetColorAlpha() FillAlpha=FillAlpha or self:GetColorAlpha()
@@ -1913,7 +1966,7 @@ end
-- @return #boolean true if the location is within the zone. -- @return #boolean true if the location is within the zone.
function ZONE_POLYGON_BASE:IsVec2InZone( Vec2 ) function ZONE_POLYGON_BASE:IsVec2InZone( Vec2 )
self:F2( Vec2 ) self:F2( Vec2 )
if not Vec2 then return false end
local Next local Next
local Prev local Prev
local InPolygon = false local InPolygon = false
@@ -1937,30 +1990,46 @@ function ZONE_POLYGON_BASE:IsVec2InZone( Vec2 )
return InPolygon return InPolygon
end end
--- Returns if a point is within the zone.
-- @param #ZONE_POLYGON_BASE self
-- @param DCS#Vec3 Vec3 The point to test.
-- @return #boolean true if the point is within the zone.
function ZONE_POLYGON_BASE:IsVec3InZone( Vec3 )
self:F2( Vec3 )
if not Vec3 then return false end
local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } )
return InZone
end
--- Define a random @{DCS#Vec2} within the zone. --- Define a random @{DCS#Vec2} within the zone.
-- @param #ZONE_POLYGON_BASE self -- @param #ZONE_POLYGON_BASE self
-- @return DCS#Vec2 The Vec2 coordinate. -- @return DCS#Vec2 The Vec2 coordinate.
function ZONE_POLYGON_BASE:GetRandomVec2() function ZONE_POLYGON_BASE:GetRandomVec2()
self:F2()
--- It is a bit tricky to find a random point within a polygon. Right now i am doing it the dirty and inefficient way... -- It is a bit tricky to find a random point within a polygon. Right now i am doing it the dirty and inefficient way...
local Vec2Found = false
local Vec2 -- Get the bounding square.
local BS = self:GetBoundingSquare() local BS = self:GetBoundingSquare()
self:T2( BS ) local Nmax=1000 ; local n=0
while n<Nmax do
while Vec2Found == false do -- Random point in the bounding square.
Vec2 = { x = math.random( BS.x1, BS.x2 ), y = math.random( BS.y1, BS.y2 ) } local Vec2={x=math.random(BS.x1, BS.x2), y=math.random(BS.y1, BS.y2)}
self:T2( Vec2 )
if self:IsVec2InZone( Vec2 ) then -- Check if this is in the polygon.
Vec2Found = true if self:IsVec2InZone(Vec2) then
return Vec2
end end
n=n+1
end end
self:T2( Vec2 ) self:E("Could not find a random point in the polygon zone!")
return nil
return Vec2
end end
--- Return a @{Core.Point#POINT_VEC2} object representing a random 2D point at landheight within the zone. --- Return a @{Core.Point#POINT_VEC2} object representing a random 2D point at landheight within the zone.
@@ -2075,12 +2144,12 @@ end
-- --
-- ## Declare a ZONE_POLYGON directly in the DCS mission editor! -- ## Declare a ZONE_POLYGON directly in the DCS mission editor!
-- --
-- You can declare a ZONE_POLYGON using the DCS mission editor by adding the ~ZONE_POLYGON tag in the group name. -- You can declare a ZONE_POLYGON using the DCS mission editor by adding the #ZONE_POLYGON tag in the group name.
-- --
-- So, imagine you have a group declared in the mission editor, with group name `DefenseZone~ZONE_POLYGON`. -- So, imagine you have a group declared in the mission editor, with group name `DefenseZone#ZONE_POLYGON`.
-- Then during mission startup, when loading Moose.lua, this group will be detected as a ZONE_POLYGON declaration. -- Then during mission startup, when loading Moose.lua, this group will be detected as a ZONE_POLYGON declaration.
-- Within the background, a ZONE_POLYGON object will be created within the @{Core.Database} using the properties of the group. -- Within the background, a ZONE_POLYGON object will be created within the @{Core.Database} using the properties of the group.
-- The ZONE_POLYGON name will be the group name without the ~ZONE_POLYGON tag. -- The ZONE_POLYGON name will be the group name without the #ZONE_POLYGON tag.
-- --
-- So, you can search yourself for the ZONE_POLYGON by using the @{#ZONE_POLYGON.FindByName}() method. -- So, you can search yourself for the ZONE_POLYGON by using the @{#ZONE_POLYGON.FindByName}() method.
-- In this example, `local PolygonZone = ZONE_POLYGON:FindByName( "DefenseZone" )` would return the ZONE_POLYGON object -- In this example, `local PolygonZone = ZONE_POLYGON:FindByName( "DefenseZone" )` would return the ZONE_POLYGON object
@@ -2152,6 +2221,9 @@ end
do -- ZONE_AIRBASE do -- ZONE_AIRBASE
--- @type ZONE_AIRBASE --- @type ZONE_AIRBASE
-- @field #boolean isShip If `true`, airbase is a ship.
-- @field #boolean isHelipad If `true`, airbase is a helipad.
-- @field #boolean isAirdrome If `true`, airbase is an airdrome.
-- @extends #ZONE_RADIUS -- @extends #ZONE_RADIUS
@@ -2181,6 +2253,20 @@ do -- ZONE_AIRBASE
self._.ZoneAirbase = Airbase self._.ZoneAirbase = Airbase
self._.ZoneVec2Cache = self._.ZoneAirbase:GetVec2() self._.ZoneVec2Cache = self._.ZoneAirbase:GetVec2()
if Airbase:IsShip() then
self.isShip=true
self.isHelipad=false
self.isAirdrome=false
elseif Airbase:IsHelipad() then
self.isShip=false
self.isHelipad=true
self.isAirdrome=false
elseif Airbase:IsAirdrome() then
self.isShip=false
self.isHelipad=false
self.isAirdrome=true
end
-- Zone objects are added to the _DATABASE and SET_ZONE objects. -- Zone objects are added to the _DATABASE and SET_ZONE objects.
_EVENTDISPATCHER:CreateEventNewZone( self ) _EVENTDISPATCHER:CreateEventNewZone( self )
@@ -2194,9 +2280,9 @@ do -- ZONE_AIRBASE
return self._.ZoneAirbase return self._.ZoneAirbase
end end
--- Returns the current location of the @{Wrapper.Group}. --- Returns the current location of the AIRBASE.
-- @param #ZONE_AIRBASE self -- @param #ZONE_AIRBASE self
-- @return DCS#Vec2 The location of the zone based on the @{Wrapper.Group} location. -- @return DCS#Vec2 The location of the zone based on the AIRBASE location.
function ZONE_AIRBASE:GetVec2() function ZONE_AIRBASE:GetVec2()
self:F( self.ZoneName ) self:F( self.ZoneName )
@@ -2214,24 +2300,6 @@ do -- ZONE_AIRBASE
return ZoneVec2 return ZoneVec2
end end
--- Returns a random location within the zone of the @{Wrapper.Group}.
-- @param #ZONE_AIRBASE self
-- @return DCS#Vec2 The random location of the zone based on the @{Wrapper.Group} location.
function ZONE_AIRBASE:GetRandomVec2()
self:F( self.ZoneName )
local Point = {}
local Vec2 = self._.ZoneAirbase:GetVec2()
local angle = math.random() * math.pi*2;
Point.x = Vec2.x + math.cos( angle ) * math.random() * self:GetRadius();
Point.y = Vec2.y + math.sin( angle ) * math.random() * self:GetRadius();
self:T( { Point } )
return Point
end
--- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone. --- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone.
-- @param #ZONE_AIRBASE self -- @param #ZONE_AIRBASE self
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. -- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0.
+3 -1
View File
@@ -487,8 +487,10 @@ do -- Object
-- @field UNIT -- @field UNIT
-- @field WEAPON -- @field WEAPON
-- @field STATIC -- @field STATIC
-- @field SCENERY
-- @field BASE -- @field BASE
-- @field SCENERY
-- @field CARGO
--- @type Object.Desc --- @type Object.Desc
-- @extends #Desc -- @extends #Desc
File diff suppressed because it is too large Load Diff
@@ -73,7 +73,7 @@
-- @field Core.Point#COORDINATE RearmingPlaceCoord Coordinates of the rearming place. If the place is more than 100 m away from the ARTY group, the group will go there. -- @field Core.Point#COORDINATE RearmingPlaceCoord Coordinates of the rearming place. If the place is more than 100 m away from the ARTY group, the group will go there.
-- @field #boolean RearmingArtyOnRoad If true, ARTY group will move to rearming place using mainly roads. Default false. -- @field #boolean RearmingArtyOnRoad If true, ARTY group will move to rearming place using mainly roads. Default false.
-- @field Core.Point#COORDINATE InitialCoord Initial coordinates of the ARTY group. -- @field Core.Point#COORDINATE InitialCoord Initial coordinates of the ARTY group.
-- @field #boolean report Arty group sends messages about their current state or target to its coaliton. -- @field #boolean report Arty group sends messages about their current state or target to its coalition.
-- @field #table ammoshells Table holding names of the shell types which are included when counting the ammo. Default is {"weapons.shells"} which include most shells. -- @field #table ammoshells Table holding names of the shell types which are included when counting the ammo. Default is {"weapons.shells"} which include most shells.
-- @field #table ammorockets Table holding names of the rocket types which are included when counting the ammo. Default is {"weapons.nurs"} which includes most unguided rockets. -- @field #table ammorockets Table holding names of the rocket types which are included when counting the ammo. Default is {"weapons.nurs"} which includes most unguided rockets.
-- @field #table ammomissiles Table holding names of the missile types which are included when counting the ammo. Default is {"weapons.missiles"} which includes some guided missiles. -- @field #table ammomissiles Table holding names of the missile types which are included when counting the ammo. Default is {"weapons.missiles"} which includes some guided missiles.
File diff suppressed because it is too large Load Diff
@@ -949,11 +949,14 @@ function PSEUDOATC:LocalAirports(GID, UID)
for _,airbase in pairs(airports) do for _,airbase in pairs(airports) do
local name=airbase:getName() local name=airbase:getName()
local q=AIRBASE:FindByName(name):GetCoordinate() local a=AIRBASE:FindByName(name)
local d=q:Get2DDistance(pos) if a then
local q=a:GetCoordinate()
local d=q:Get2DDistance(pos)
-- Add to table. -- Add to table.
table.insert(self.group[GID].player[UID].airports, {distance=d, name=name}) table.insert(self.group[GID].player[UID].airports, {distance=d, name=name})
end
end end
end end
+5 -5
View File
@@ -69,7 +69,7 @@
-- @field #string category Category of aircarft: "plane" or "heli". -- @field #string category Category of aircarft: "plane" or "heli".
-- @field #number groupsize Number of aircraft in group. -- @field #number groupsize Number of aircraft in group.
-- @field #string friendly Possible departure/destination airport: all=blue+red+neutral, same=spawn+neutral, spawnonly=spawn, blue=blue+neutral, blueonly=blue, red=red+neutral, redonly=red. -- @field #string friendly Possible departure/destination airport: all=blue+red+neutral, same=spawn+neutral, spawnonly=spawn, blue=blue+neutral, blueonly=blue, red=red+neutral, redonly=red.
-- @field #table ctable Table with the valid coalitons from choice self.friendly. -- @field #table ctable Table with the valid coalitions from choice self.friendly.
-- @field #table aircraft Table which holds the basic aircraft properties (speed, range, ...). -- @field #table aircraft Table which holds the basic aircraft properties (speed, range, ...).
-- @field #number Vcruisemax Max cruise speed in m/s (250 m/s = 900 km/h = 486 kt) set by user. -- @field #number Vcruisemax Max cruise speed in m/s (250 m/s = 900 km/h = 486 kt) set by user.
-- @field #number Vclimb Default climb rate in ft/min. -- @field #number Vclimb Default climb rate in ft/min.
@@ -348,7 +348,7 @@ RAT={
category = nil, -- Category of aircarft: "plane" or "heli". category = nil, -- Category of aircarft: "plane" or "heli".
groupsize=nil, -- Number of aircraft in the group. groupsize=nil, -- Number of aircraft in the group.
friendly = "same", -- Possible departure/destination airport: same=spawn+neutral, spawnonly=spawn, blue=blue+neutral, blueonly=blue, red=red+neutral, redonly=red, neutral. friendly = "same", -- Possible departure/destination airport: same=spawn+neutral, spawnonly=spawn, blue=blue+neutral, blueonly=blue, red=red+neutral, redonly=red, neutral.
ctable = {}, -- Table with the valid coalitons from choice self.friendly. ctable = {}, -- Table with the valid coalitions from choice self.friendly.
aircraft = {}, -- Table which holds the basic aircraft properties (speed, range, ...). aircraft = {}, -- Table which holds the basic aircraft properties (speed, range, ...).
Vcruisemax=nil, -- Max cruise speed in set by user. Vcruisemax=nil, -- Max cruise speed in set by user.
Vclimb=1500, -- Default climb rate in ft/min. Vclimb=1500, -- Default climb rate in ft/min.
@@ -657,7 +657,7 @@ end
-- @param #RAT self -- @param #RAT self
-- @param #number naircraft (Optional) Number of aircraft to spawn. Default is one aircraft. -- @param #number naircraft (Optional) Number of aircraft to spawn. Default is one aircraft.
-- @return #boolean True if spawning was successful or nil if nothing was spawned. -- @return #boolean True if spawning was successful or nil if nothing was spawned.
-- @usage yak:Spawn(5) will spawn five aircraft. By default aircraft will spawn at neutral and red airports if the template group is part of the red coaliton. -- @usage yak:Spawn(5) will spawn five aircraft. By default aircraft will spawn at neutral and red airports if the template group is part of the red coalition.
function RAT:Spawn(naircraft) function RAT:Spawn(naircraft)
-- Make sure that this function is only been called once per RAT object. -- Make sure that this function is only been called once per RAT object.
@@ -1289,7 +1289,7 @@ end
--- Include all airports which lie in a zone as possible destinations. --- Include all airports which lie in a zone as possible destinations.
-- @param #RAT self -- @param #RAT self
-- @param Core.Zone#ZONE zone Zone in which the departure airports lie. Has to be a MOOSE zone. -- @param Core.Zone#ZONE zone Zone in which the destination airports lie. Has to be a MOOSE zone.
-- @return #RAT RAT self object. -- @return #RAT RAT self object.
function RAT:SetDestinationsFromZone(zone) function RAT:SetDestinationsFromZone(zone)
self:F2(zone) self:F2(zone)
@@ -1305,7 +1305,7 @@ end
--- Include all airports which lie in a zone as possible destinations. --- Include all airports which lie in a zone as possible destinations.
-- @param #RAT self -- @param #RAT self
-- @param Core.Zone#ZONE zone Zone in which the destination airports lie. Has to be a MOOSE zone. -- @param Core.Zone#ZONE zone Zone in which the departure airports lie. Has to be a MOOSE zone.
-- @return #RAT RAT self object. -- @return #RAT RAT self object.
function RAT:SetDeparturesFromZone(zone) function RAT:SetDeparturesFromZone(zone)
self:F2(zone) self:F2(zone)
File diff suppressed because it is too large Load Diff
+218 -260
View File
@@ -67,7 +67,7 @@
-- --
-- * Upload scoring to a database or a BI tool to publish the scoring results to the player community. -- * Upload scoring to a database or a BI tool to publish the scoring results to the player community.
-- * Upload scoring in an (online) Excel like tool, using pivot tables and pivot charts to show mission results. -- * Upload scoring in an (online) Excel like tool, using pivot tables and pivot charts to show mission results.
-- * Share scoring amoung players after the mission to discuss mission results. -- * Share scoring among players after the mission to discuss mission results.
-- --
-- Scores can be **reported**. **Menu options** are automatically added to **each player group** when a player joins a client slot or a CA unit. -- Scores can be **reported**. **Menu options** are automatically added to **each player group** when a player joins a client slot or a CA unit.
-- Use the radio menu F10 to consult the scores while running the mission. -- Use the radio menu F10 to consult the scores while running the mission.
@@ -88,7 +88,6 @@
-- @module Functional.Scoring -- @module Functional.Scoring
-- @image Scoring.JPG -- @image Scoring.JPG
--- @type SCORING --- @type SCORING
-- @field Players A collection of the current players that have joined the game. -- @field Players A collection of the current players that have joined the game.
-- @extends Core.Base#BASE -- @extends Core.Base#BASE
@@ -99,7 +98,6 @@
-- --
-- local Scoring = SCORING:New( "Scoring File" ) -- local Scoring = SCORING:New( "Scoring File" )
-- --
--
-- # Set the destroy score or penalty scale: -- # Set the destroy score or penalty scale:
-- --
-- Score scales can be set for scores granted when enemies or friendlies are destroyed. -- Score scales can be set for scores granted when enemies or friendlies are destroyed.
@@ -147,11 +145,11 @@
-- --
-- # (Decommissioned) Configure fratricide level. -- # (Decommissioned) Configure fratricide level.
-- --
-- **This functionality is decomissioned until the DCS bug concerning Unit:destroy() not being functional in multi player for player units has been fixed by ED**. -- **This functionality is decommissioned until the DCS bug concerning Unit:destroy() not being functional in multi player for player units has been fixed by ED**.
-- --
-- When a player commits too much damage to friendlies, his penalty score will reach a certain level. -- When a player commits too much damage to friendlies, his penalty score will reach a certain level.
-- Use the method @{#SCORING.SetFratricide}() to define the level when a player gets kicked. -- Use the method @{#SCORING.SetFratricide}() to define the level when a player gets kicked.
-- By default, the fratricide level is the default penalty mutiplier * 2 for the penalty score. -- By default, the fratricide level is the default penalty multiplier * 2 for the penalty score.
-- --
-- # Penalty score when a player changes the coalition. -- # Penalty score when a player changes the coalition.
-- --
@@ -190,8 +188,8 @@
-- --
-- --Sanitize Mission Scripting environment -- --Sanitize Mission Scripting environment
-- --This makes unavailable some unsecure functions. -- --This makes unavailable some unsecure functions.
-- --Mission downloaded from server to client may contain potentialy harmful lua code that may use these functions. -- --Mission downloaded from server to client may contain potentially harmful lua code that may use these functions.
-- --You can remove the code below and make availble these functions at your own risk. -- --You can remove the code below and make available these functions at your own risk.
-- --
-- The MOOSE designer cannot take any responsibility of any damage inflicted as a result of the de-sanitization. -- The MOOSE designer cannot take any responsibility of any damage inflicted as a result of the de-sanitization.
-- That being said, I hope that the SCORING class provides you with a great add-on to score your squad mates achievements. -- That being said, I hope that the SCORING class provides you with a great add-on to score your squad mates achievements.
@@ -229,20 +227,18 @@ SCORING = {
Players = {}, Players = {},
} }
local _SCORINGCoalition = local _SCORINGCoalition = {
{ [1] = "Red",
[1] = "Red", [2] = "Blue",
[2] = "Blue", }
}
local _SCORINGCategory = local _SCORINGCategory = {
{ [Unit.Category.AIRPLANE] = "Plane",
[Unit.Category.AIRPLANE] = "Plane", [Unit.Category.HELICOPTER] = "Helicopter",
[Unit.Category.HELICOPTER] = "Helicopter", [Unit.Category.GROUND_UNIT] = "Vehicle",
[Unit.Category.GROUND_UNIT] = "Vehicle", [Unit.Category.SHIP] = "Ship",
[Unit.Category.SHIP] = "Ship", [Unit.Category.STRUCTURE] = "Structure",
[Unit.Category.STRUCTURE] = "Structure", }
}
--- Creates a new SCORING object to administer the scoring achieved by players. --- Creates a new SCORING object to administer the scoring achieved by players.
-- @param #SCORING self -- @param #SCORING self
@@ -250,8 +246,8 @@ local _SCORINGCategory =
-- @return #SCORING self -- @return #SCORING self
-- @usage -- @usage
-- --
-- -- Define a new scoring object for the mission Gori Valley. -- -- Define a new scoring object for the mission Gori Valley.
-- ScoringObject = SCORING:New( "Gori Valley" ) -- ScoringObject = SCORING:New( "Gori Valley" )
-- --
function SCORING:New( GameName ) function SCORING:New( GameName )
@@ -264,7 +260,6 @@ function SCORING:New( GameName )
error( "A game name must be given to register the scoring results" ) error( "A game name must be given to register the scoring results" )
end end
-- Additional Object scores -- Additional Object scores
self.ScoringObjects = {} self.ScoringObjects = {}
@@ -284,9 +279,11 @@ function SCORING:New( GameName )
-- Default fratricide penalty level (maximum penalty that can be assigned to a player before he gets kicked). -- Default fratricide penalty level (maximum penalty that can be assigned to a player before he gets kicked).
self:SetFratricide( self.ScaleDestroyPenalty * 3 ) self:SetFratricide( self.ScaleDestroyPenalty * 3 )
self.penaltyonfratricide = true
-- Default penalty when a player changes coalition. -- Default penalty when a player changes coalition.
self:SetCoalitionChangePenalty( self.ScaleDestroyPenalty ) self:SetCoalitionChangePenalty( self.ScaleDestroyPenalty )
self.penaltyoncoalitionchange = true
self:SetDisplayMessagePrefix() self:SetDisplayMessagePrefix()
@@ -295,21 +292,18 @@ function SCORING:New( GameName )
self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash )
self:HandleEvent( EVENTS.Hit, self._EventOnHit ) self:HandleEvent( EVENTS.Hit, self._EventOnHit )
self:HandleEvent( EVENTS.Birth ) self:HandleEvent( EVENTS.Birth )
--self:HandleEvent( EVENTS.PlayerEnterUnit ) -- self:HandleEvent( EVENTS.PlayerEnterUnit )
self:HandleEvent( EVENTS.PlayerLeaveUnit ) self:HandleEvent( EVENTS.PlayerLeaveUnit )
-- During mission startup, especially for single player, -- During mission startup, especially for single player,
-- iterate the database for the player that has joined, and add him to the scoring, and set the menu. -- iterate the database for the player that has joined, and add him to the scoring, and set the menu.
-- But this can only be started one second after the mission has started, so i need to schedule this ... -- But this can only be started one second after the mission has started, so i need to schedule this ...
self.ScoringPlayerScan = BASE:ScheduleOnce( 1, self.ScoringPlayerScan = BASE:ScheduleOnce( 1, function()
function() for PlayerName, PlayerUnit in pairs( _DATABASE:GetPlayerUnits() ) do
for PlayerName, PlayerUnit in pairs( _DATABASE:GetPlayerUnits() ) do self:_AddPlayerFromUnit( PlayerUnit )
self:_AddPlayerFromUnit( PlayerUnit ) self:SetScoringMenu( PlayerUnit:GetGroup() )
self:SetScoringMenu( PlayerUnit:GetGroup() )
end
end end
) end )
-- Create the CSV file. -- Create the CSV file.
self:OpenCSV( GameName ) self:OpenCSV( GameName )
@@ -327,7 +321,6 @@ function SCORING:SetDisplayMessagePrefix( DisplayMessagePrefix )
return self return self
end end
--- Set the scale for scoring valid destroys (enemy destroys). --- Set the scale for scoring valid destroys (enemy destroys).
-- A default calculated score is a value between 1 and 10. -- A default calculated score is a value between 1 and 10.
-- The scale magnifies the scores given to the players. -- The scale magnifies the scores given to the players.
@@ -409,7 +402,6 @@ function SCORING:RemoveStaticScore( ScoreStatic )
return self return self
end end
--- Specify a special additional score for a @{Wrapper.Group}. --- Specify a special additional score for a @{Wrapper.Group}.
-- @param #SCORING self -- @param #SCORING self
-- @param Wrapper.Group#GROUP ScoreGroup The @{Wrapper.Group} for which each @{Wrapper.Unit} a Score is given. -- @param Wrapper.Group#GROUP ScoreGroup The @{Wrapper.Group} for which each @{Wrapper.Unit} a Score is given.
@@ -462,7 +454,6 @@ function SCORING:RemoveZoneScore( ScoreZone )
return self return self
end end
--- Configure to send messages after a target has been hit. --- Configure to send messages after a target has been hit.
-- @param #SCORING self -- @param #SCORING self
-- @param #boolean OnOff If true is given, the messages are sent. -- @param #boolean OnOff If true is given, the messages are sent.
@@ -569,10 +560,9 @@ function SCORING:IfMessagesToCoalition()
return self.MessagesAudience == 2 return self.MessagesAudience == 2
end end
--- When a player commits too much damage to friendlies, his penalty score will reach a certain level. --- When a player commits too much damage to friendlies, his penalty score will reach a certain level.
-- Use this method to define the level when a player gets kicked. -- Use this method to define the level when a player gets kicked.
-- By default, the fratricide level is the default penalty mutiplier * 2 for the penalty score. -- By default, the fratricide level is the default penalty multiplier * 2 for the penalty score.
-- @param #SCORING self -- @param #SCORING self
-- @param #number Fratricide The amount of maximum penalty that may be inflicted by a friendly player before he gets kicked. -- @param #number Fratricide The amount of maximum penalty that may be inflicted by a friendly player before he gets kicked.
-- @return #SCORING -- @return #SCORING
@@ -582,6 +572,23 @@ function SCORING:SetFratricide( Fratricide )
return self return self
end end
--- Decide if fratricide is leading to penalties (true) or not (false)
-- @param #SCORING self
-- @param #boolean OnOff Switch for Fratricide
-- @return #SCORING
function SCORING:SwitchFratricide( OnOff )
self.penaltyonfratricide = OnOff
return self
end
--- Decide if a change of coalition is leading to penalties (true) or not (false)
-- @param #SCORING self
-- @param #boolean OnOff Switch for Coalition Changes.
-- @return #SCORING
function SCORING:SwitchTreason( OnOff )
self.penaltyoncoalitionchange = OnOff
return self
end
--- When a player changes the coalition, he can receive a penalty score. --- When a player changes the coalition, he can receive a penalty score.
-- Use the method @{#SCORING.SetCoalitionChangePenalty}() to define the penalty when a player changes coalition. -- Use the method @{#SCORING.SetCoalitionChangePenalty}() to define the penalty when a player changes coalition.
@@ -595,20 +602,18 @@ function SCORING:SetCoalitionChangePenalty( CoalitionChangePenalty )
return self return self
end end
--- Sets the scoring menu. --- Sets the scoring menu.
-- @param #SCORING self -- @param #SCORING self
-- @return #SCORING -- @return #SCORING
function SCORING:SetScoringMenu( ScoringGroup ) function SCORING:SetScoringMenu( ScoringGroup )
local Menu = MENU_GROUP:New( ScoringGroup, 'Scoring and Statistics' ) local Menu = MENU_GROUP:New( ScoringGroup, 'Scoring and Statistics' )
local ReportGroupSummary = MENU_GROUP_COMMAND:New( ScoringGroup, 'Summary report players in group', Menu, SCORING.ReportScoreGroupSummary, self, ScoringGroup ) local ReportGroupSummary = MENU_GROUP_COMMAND:New( ScoringGroup, 'Summary report players in group', Menu, SCORING.ReportScoreGroupSummary, self, ScoringGroup )
local ReportGroupDetailed = MENU_GROUP_COMMAND:New( ScoringGroup, 'Detailed report players in group', Menu, SCORING.ReportScoreGroupDetailed, self, ScoringGroup ) local ReportGroupDetailed = MENU_GROUP_COMMAND:New( ScoringGroup, 'Detailed report players in group', Menu, SCORING.ReportScoreGroupDetailed, self, ScoringGroup )
local ReportToAllSummary = MENU_GROUP_COMMAND:New( ScoringGroup, 'Summary report all players', Menu, SCORING.ReportScoreAllSummary, self, ScoringGroup ) local ReportToAllSummary = MENU_GROUP_COMMAND:New( ScoringGroup, 'Summary report all players', Menu, SCORING.ReportScoreAllSummary, self, ScoringGroup )
self:SetState( ScoringGroup, "ScoringMenu", Menu ) self:SetState( ScoringGroup, "ScoringMenu", Menu )
return self return self
end end
--- Add a new player entering a Unit. --- Add a new player entering a Unit.
-- @param #SCORING self -- @param #SCORING self
-- @param Wrapper.Unit#UNIT UnitData -- @param Wrapper.Unit#UNIT UnitData
@@ -647,15 +652,15 @@ function SCORING:_AddPlayerFromUnit( UnitData )
if not self.Players[PlayerName].UnitCoalition then if not self.Players[PlayerName].UnitCoalition then
self.Players[PlayerName].UnitCoalition = UnitCoalition self.Players[PlayerName].UnitCoalition = UnitCoalition
else else
if self.Players[PlayerName].UnitCoalition ~= UnitCoalition then -- TODO: switch for coalition changes, make penalty alterable
self.Players[PlayerName].Penalty = self.Players[PlayerName].Penalty + 50 if self.Players[PlayerName].UnitCoalition ~= UnitCoalition and self.penaltyoncoalitionchange then
self.Players[PlayerName].Penalty = self.Players[PlayerName].Penalty + self.CoalitionChangePenalty or 50
self.Players[PlayerName].PenaltyCoalition = self.Players[PlayerName].PenaltyCoalition + 1 self.Players[PlayerName].PenaltyCoalition = self.Players[PlayerName].PenaltyCoalition + 1
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' changed coalition from " .. _SCORINGCoalition[self.Players[PlayerName].UnitCoalition] .. " to " .. _SCORINGCoalition[UnitCoalition] .. MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' changed coalition from " .. _SCORINGCoalition[self.Players[PlayerName].UnitCoalition] .. " to " .. _SCORINGCoalition[UnitCoalition] .. "(changed " .. self.Players[PlayerName].PenaltyCoalition .. " times the coalition). " ..
"(changed " .. self.Players[PlayerName].PenaltyCoalition .. " times the coalition). 50 Penalty points added.", self.CoalitionChangePenalty .. "Penalty points added.",
MESSAGE.Type.Information MESSAGE.Type.Information )
):ToAll() :ToAll()
self:ScoreCSV( PlayerName, "", "COALITION_PENALTY", 1, -50, self.Players[PlayerName].UnitName, _SCORINGCoalition[self.Players[PlayerName].UnitCoalition], _SCORINGCategory[self.Players[PlayerName].UnitCategory], self.Players[PlayerName].UnitType, self:ScoreCSV( PlayerName, "", "COALITION_PENALTY", 1, -1 * self.CoalitionChangePenalty, self.Players[PlayerName].UnitName, _SCORINGCoalition[self.Players[PlayerName].UnitCoalition], _SCORINGCategory[self.Players[PlayerName].UnitCategory], self.Players[PlayerName].UnitType, UnitName, _SCORINGCoalition[UnitCoalition], _SCORINGCategory[UnitCategory], UnitData:GetTypeName() )
UnitName, _SCORINGCoalition[UnitCoalition], _SCORINGCategory[UnitCategory], UnitData:GetTypeName() )
end end
end end
@@ -667,29 +672,25 @@ function SCORING:_AddPlayerFromUnit( UnitData )
self.Players[PlayerName].ThreatLevel = UnitThreatLevel self.Players[PlayerName].ThreatLevel = UnitThreatLevel
self.Players[PlayerName].ThreatType = UnitThreatType self.Players[PlayerName].ThreatType = UnitThreatType
-- TODO: DCS bug concerning Units with skill level client don't get destroyed in multi player. This logic is deactivated until this bug gets fixed. -- TODO: make fratricide switchable
--[[ if self.Players[PlayerName].Penalty > self.Fratricide * 0.50 and self.penaltyonfratricide then
if self.Players[PlayerName].Penalty > self.Fratricide * 0.50 then
if self.Players[PlayerName].PenaltyWarning < 1 then if self.Players[PlayerName].PenaltyWarning < 1 then
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "': WARNING! If you continue to commit FRATRICIDE and have a PENALTY score higher than " .. self.Fratricide .. ", you will be COURT MARTIALED and DISMISSED from this mission! \nYour total penalty is: " .. self.Players[PlayerName].Penalty, MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "': WARNING! If you continue to commit FRATRICIDE and have a PENALTY score higher than " .. self.Fratricide .. ", you will be COURT MARTIALED and DISMISSED from this mission! \nYour total penalty is: " .. self.Players[PlayerName].Penalty,
MESSAGE.Type.Information MESSAGE.Type.Information )
):ToAll() :ToAll()
self.Players[PlayerName].PenaltyWarning = self.Players[PlayerName].PenaltyWarning + 1 self.Players[PlayerName].PenaltyWarning = self.Players[PlayerName].PenaltyWarning + 1
end end
end end
if self.Players[PlayerName].Penalty > self.Fratricide then if self.Players[PlayerName].Penalty > self.Fratricide and self.penaltyonfratricide then
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' committed FRATRICIDE, he will be COURT MARTIALED and is DISMISSED from this mission!", MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' committed FRATRICIDE, he will be COURT MARTIALED and is DISMISSED from this mission!",
MESSAGE.Type.Information MESSAGE.Type.Information )
):ToAll() :ToAll()
UnitData:GetGroup():Destroy() UnitData:GetGroup():Destroy()
end end
--]]
end end
end end
--- Add a goal score for a player. --- Add a goal score for a player.
-- The method takes the Player name for which the Goal score needs to be set. -- The method takes the Player name for which the Goal score needs to be set.
-- The GoalTag is a string or identifier that is taken into the CSV file scoring log to identify the goal. -- The GoalTag is a string or identifier that is taken into the CSV file scoring log to identify the goal.
@@ -712,14 +713,14 @@ function SCORING:AddGoalScorePlayer( PlayerName, GoalTag, Text, Score )
PlayerData.Goals[GoalTag].Score = PlayerData.Goals[GoalTag].Score + Score PlayerData.Goals[GoalTag].Score = PlayerData.Goals[GoalTag].Score + Score
PlayerData.Score = PlayerData.Score + Score PlayerData.Score = PlayerData.Score + Score
MESSAGE:NewType( self.DisplayMessagePrefix .. Text, MESSAGE.Type.Information ):ToAll() MESSAGE:NewType( self.DisplayMessagePrefix .. Text,
MESSAGE.Type.Information )
:ToAll()
self:ScoreCSV( PlayerName, "", "GOAL_" .. string.upper( GoalTag ), 1, Score, nil ) self:ScoreCSV( PlayerName, "", "GOAL_" .. string.upper( GoalTag ), 1, Score, nil )
end end
end end
--- Add a goal score for a player. --- Add a goal score for a player.
-- The method takes the PlayerUnit for which the Goal score needs to be set. -- The method takes the PlayerUnit for which the Goal score needs to be set.
-- The GoalTag is a string or identifier that is taken into the CSV file scoring log to identify the goal. -- The GoalTag is a string or identifier that is taken into the CSV file scoring log to identify the goal.
@@ -744,13 +745,14 @@ function SCORING:AddGoalScore( PlayerUnit, GoalTag, Text, Score )
PlayerData.Goals[GoalTag].Score = PlayerData.Goals[GoalTag].Score + Score PlayerData.Goals[GoalTag].Score = PlayerData.Goals[GoalTag].Score + Score
PlayerData.Score = PlayerData.Score + Score PlayerData.Score = PlayerData.Score + Score
MESSAGE:NewType( self.DisplayMessagePrefix .. Text, MESSAGE.Type.Information ):ToAll() MESSAGE:NewType( self.DisplayMessagePrefix .. Text,
MESSAGE.Type.Information )
:ToAll()
self:ScoreCSV( PlayerName, "", "GOAL_" .. string.upper( GoalTag ), 1, Score, PlayerUnit:GetName() ) self:ScoreCSV( PlayerName, "", "GOAL_" .. string.upper( GoalTag ), 1, Score, PlayerUnit:GetName() )
end end
end end
--- Registers Scores the players completing a Mission Task. --- Registers Scores the players completing a Mission Task.
-- @param #SCORING self -- @param #SCORING self
-- @param Tasking.Mission#MISSION Mission -- @param Tasking.Mission#MISSION Mission
@@ -780,7 +782,9 @@ function SCORING:_AddMissionTaskScore( Mission, PlayerUnit, Text, Score )
PlayerData.Score = self.Players[PlayerName].Score + Score PlayerData.Score = self.Players[PlayerName].Score + Score
PlayerData.Mission[MissionName].ScoreTask = self.Players[PlayerName].Mission[MissionName].ScoreTask + Score PlayerData.Mission[MissionName].ScoreTask = self.Players[PlayerName].Mission[MissionName].ScoreTask + Score
MESSAGE:NewType( self.DisplayMessagePrefix .. Mission:GetText() .. " : " .. Text .. " Score: " .. Score, MESSAGE.Type.Information ):ToAll() MESSAGE:NewType( self.DisplayMessagePrefix .. Mission:GetText() .. " : " .. Text .. " Score: " .. Score,
MESSAGE.Type.Information )
:ToAll()
self:ScoreCSV( PlayerName, "", "TASK_" .. MissionName:gsub( ' ', '_' ), 1, Score, PlayerUnit:GetName() ) self:ScoreCSV( PlayerName, "", "TASK_" .. MissionName:gsub( ' ', '_' ), 1, Score, PlayerUnit:GetName() )
end end
@@ -841,26 +845,24 @@ function SCORING:_AddMissionScore( Mission, Text, Score )
PlayerData.Score = PlayerData.Score + Score PlayerData.Score = PlayerData.Score + Score
PlayerData.Mission[MissionName].ScoreMission = PlayerData.Mission[MissionName].ScoreMission + Score PlayerData.Mission[MissionName].ScoreMission = PlayerData.Mission[MissionName].ScoreMission + Score
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' has " .. Text .. " in " .. Mission:GetText() .. ". " .. MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' has " .. Text .. " in " .. Mission:GetText() .. ". " .. Score .. " mission score!",
Score .. " mission score!", MESSAGE.Type.Information )
MESSAGE.Type.Information ):ToAll() :ToAll()
self:ScoreCSV( PlayerName, "", "MISSION_" .. MissionName:gsub( ' ', '_' ), 1, Score ) self:ScoreCSV( PlayerName, "", "MISSION_" .. MissionName:gsub( ' ', '_' ), 1, Score )
end end
end end
end end
--- Handles the OnPlayerEnterUnit event for the scoring. --- Handles the OnPlayerEnterUnit event for the scoring.
-- @param #SCORING self -- @param #SCORING self
-- @param Core.Event#EVENTDATA Event -- @param Core.Event#EVENTDATA Event
--function SCORING:OnEventPlayerEnterUnit( Event ) -- function SCORING:OnEventPlayerEnterUnit( Event )
-- if Event.IniUnit then -- if Event.IniUnit then
-- self:_AddPlayerFromUnit( Event.IniUnit ) -- self:_AddPlayerFromUnit( Event.IniUnit )
-- self:SetScoringMenu( Event.IniGroup ) -- self:SetScoringMenu( Event.IniGroup )
-- end -- end
--end -- end
--- Handles the OnBirth event for the scoring. --- Handles the OnBirth event for the scoring.
-- @param #SCORING self -- @param #SCORING self
@@ -886,12 +888,11 @@ function SCORING:OnEventPlayerLeaveUnit( Event )
local Menu = self:GetState( Event.IniUnit:GetGroup(), "ScoringMenu" ) -- Core.Menu#MENU_GROUP local Menu = self:GetState( Event.IniUnit:GetGroup(), "ScoringMenu" ) -- Core.Menu#MENU_GROUP
if Menu then if Menu then
-- TODO: Check if this fixes #281. -- TODO: Check if this fixes #281.
--Menu:Remove() -- Menu:Remove()
end end
end end
end end
--- Handles the OnHit event for the scoring. --- Handles the OnHit event for the scoring.
-- @param #SCORING self -- @param #SCORING self
-- @param Core.Event#EVENTDATA Event -- @param Core.Event#EVENTDATA Event
@@ -936,9 +937,9 @@ function SCORING:_EventOnHit( Event )
InitPlayerName = Event.IniPlayerName InitPlayerName = Event.IniPlayerName
InitCoalition = Event.IniCoalition InitCoalition = Event.IniCoalition
--TODO: Workaround Client DCS Bug -- TODO: Workaround Client DCS Bug
--InitCategory = InitUnit:getCategory() -- InitCategory = InitUnit:getCategory()
--InitCategory = InitUnit:getDesc().category -- InitCategory = InitUnit:getDesc().category
InitCategory = Event.IniCategory InitCategory = Event.IniCategory
InitType = Event.IniTypeName InitType = Event.IniTypeName
@@ -946,10 +947,9 @@ function SCORING:_EventOnHit( Event )
InitUnitCategory = _SCORINGCategory[InitCategory] InitUnitCategory = _SCORINGCategory[InitCategory]
InitUnitType = InitType InitUnitType = InitType
self:T( { InitUnitName, InitGroupName, InitPlayerName, InitCoalition, InitCategory, InitType , InitUnitCoalition, InitUnitCategory, InitUnitType } ) self:T( { InitUnitName, InitGroupName, InitPlayerName, InitCoalition, InitCategory, InitType, InitUnitCoalition, InitUnitCategory, InitUnitType } )
end end
if Event.TgtDCSUnit then if Event.TgtDCSUnit then
TargetUnit = Event.TgtDCSUnit TargetUnit = Event.TgtDCSUnit
@@ -960,9 +960,9 @@ function SCORING:_EventOnHit( Event )
TargetPlayerName = Event.TgtPlayerName TargetPlayerName = Event.TgtPlayerName
TargetCoalition = Event.TgtCoalition TargetCoalition = Event.TgtCoalition
--TODO: Workaround Client DCS Bug -- TODO: Workaround Client DCS Bug
--TargetCategory = TargetUnit:getCategory() -- TargetCategory = TargetUnit:getCategory()
--TargetCategory = TargetUnit:getDesc().category -- TargetCategory = TargetUnit:getDesc().category
TargetCategory = Event.TgtCategory TargetCategory = Event.TgtCategory
TargetType = Event.TgtTypeName TargetType = Event.TgtTypeName
@@ -1023,23 +1023,17 @@ function SCORING:_EventOnHit( Event )
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1 PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1
if TargetPlayerName ~= nil then -- It is a player hitting another player ... if TargetPlayerName ~= nil then -- It is a player hitting another player ...
MESSAGE MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit friendly player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " ..
:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit friendly player '" .. TargetPlayerName .. "' " .. "Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty,
TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " .. MESSAGE.Type.Update )
"Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty, :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
MESSAGE.Type.Update :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
)
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
else else
MESSAGE MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit friendly target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " ..
:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit friendly target " .. "Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty,
TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " .. MESSAGE.Type.Update )
"Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty, :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
MESSAGE.Type.Update :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
)
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
end end
self:ScoreCSV( InitPlayerName, TargetPlayerName, "HIT_PENALTY", 1, -10, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) self:ScoreCSV( InitPlayerName, TargetPlayerName, "HIT_PENALTY", 1, -10, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
else else
@@ -1047,33 +1041,26 @@ function SCORING:_EventOnHit( Event )
PlayerHit.Score = PlayerHit.Score + 1 PlayerHit.Score = PlayerHit.Score + 1
PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1 PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1
if TargetPlayerName ~= nil then -- It is a player hitting another player ... if TargetPlayerName ~= nil then -- It is a player hitting another player ...
MESSAGE MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit enemy player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " ..
:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit enemy player '" .. TargetPlayerName .. "' " .. "Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty,
TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " .. MESSAGE.Type.Update )
"Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty, :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
MESSAGE.Type.Update :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
)
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
else else
MESSAGE MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit enemy target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " ..
:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit enemy target " .. "Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty,
TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " .. MESSAGE.Type.Update )
"Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty, :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
MESSAGE.Type.Update :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
)
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
end end
self:ScoreCSV( InitPlayerName, TargetPlayerName, "HIT_SCORE", 1, 1, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) self:ScoreCSV( InitPlayerName, TargetPlayerName, "HIT_SCORE", 1, 1, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
end end
else -- A scenery object was hit. else -- A scenery object was hit.
MESSAGE MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit scenery object.",
:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit scenery object.", MESSAGE.Type.Update )
MESSAGE.Type.Update :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
) :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
self:ScoreCSV( InitPlayerName, "", "HIT_SCORE", 1, 0, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, "", "Scenery", TargetUnitType ) self:ScoreCSV( InitPlayerName, "", "HIT_SCORE", 1, 0, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, "", "Scenery", TargetUnitType )
end end
end end
@@ -1125,40 +1112,35 @@ function SCORING:_EventOnHit( Event )
if InitCoalition then -- A coalition object was hit, probably a static. if InitCoalition then -- A coalition object was hit, probably a static.
if InitCoalition == TargetCoalition then if InitCoalition == TargetCoalition then
-- TODO: Penalty according scale -- TODO: Penalty according scale
Player.Penalty = Player.Penalty + 10 Player.Penalty = Player.Penalty + 10 -- * self.ScaleDestroyPenalty
PlayerHit.Penalty = PlayerHit.Penalty + 10 PlayerHit.Penalty = PlayerHit.Penalty + 10 -- * self.ScaleDestroyPenalty
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1 PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1 * self.ScaleDestroyPenalty
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit friendly target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
"Penalty: -" .. PlayerHit.Penalty .. " = " .. Player.Score - Player.Penalty,
MESSAGE.Type.Update )
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
MESSAGE
:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit friendly target " ..
TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
"Penalty: -" .. PlayerHit.Penalty .. " = " .. Player.Score - Player.Penalty,
MESSAGE.Type.Update
)
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
self:ScoreCSV( Event.WeaponPlayerName, TargetPlayerName, "HIT_PENALTY", 1, -10, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) self:ScoreCSV( Event.WeaponPlayerName, TargetPlayerName, "HIT_PENALTY", 1, -10, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
else else
Player.Score = Player.Score + 1 Player.Score = Player.Score + 1
PlayerHit.Score = PlayerHit.Score + 1 PlayerHit.Score = PlayerHit.Score + 1
PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1 PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1
MESSAGE MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit enemy target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit enemy target " .. "Score: +" .. PlayerHit.Score .. " = " .. Player.Score - Player.Penalty,
TargetUnitCategory .. " ( " .. TargetType .. " ) " .. MESSAGE.Type.Update )
"Score: +" .. PlayerHit.Score .. " = " .. Player.Score - Player.Penalty, :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
MESSAGE.Type.Update :ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
)
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
self:ScoreCSV( Event.WeaponPlayerName, TargetPlayerName, "HIT_SCORE", 1, 1, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) self:ScoreCSV( Event.WeaponPlayerName, TargetPlayerName, "HIT_SCORE", 1, 1, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
end end
else -- A scenery object was hit. else -- A scenery object was hit.
MESSAGE MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit scenery object.",
:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit scenery object.", MESSAGE.Type.Update )
MESSAGE.Type.Update :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
) :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
self:ScoreCSV( Event.WeaponPlayerName, "", "HIT_SCORE", 1, 0, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, "", "Scenery", TargetUnitType ) self:ScoreCSV( Event.WeaponPlayerName, "", "HIT_SCORE", 1, 0, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, "", "Scenery", TargetUnitType )
end end
end end
@@ -1194,8 +1176,8 @@ function SCORING:_EventOnDeadOrCrash( Event )
TargetPlayerName = Event.IniPlayerName TargetPlayerName = Event.IniPlayerName
TargetCoalition = Event.IniCoalition TargetCoalition = Event.IniCoalition
--TargetCategory = TargetUnit:getCategory() -- TargetCategory = TargetUnit:getCategory()
--TargetCategory = TargetUnit:getDesc().category -- Workaround -- TargetCategory = TargetUnit:getDesc().category -- Workaround
TargetCategory = Event.IniCategory TargetCategory = Event.IniCategory
TargetType = Event.IniTypeName TargetType = Event.IniTypeName
@@ -1236,7 +1218,7 @@ function SCORING:_EventOnDeadOrCrash( Event )
local TargetDestroy = Player.Destroy[TargetCategory][TargetType] local TargetDestroy = Player.Destroy[TargetCategory][TargetType]
TargetDestroy.Score = TargetDestroy.Score or 0 TargetDestroy.Score = TargetDestroy.Score or 0
TargetDestroy.ScoreDestroy = TargetDestroy.ScoreDestroy or 0 TargetDestroy.ScoreDestroy = TargetDestroy.ScoreDestroy or 0
TargetDestroy.Penalty = TargetDestroy.Penalty or 0 TargetDestroy.Penalty = TargetDestroy.Penalty or 0
TargetDestroy.PenaltyDestroy = TargetDestroy.PenaltyDestroy or 0 TargetDestroy.PenaltyDestroy = TargetDestroy.PenaltyDestroy or 0
if TargetCoalition then if TargetCoalition then
@@ -1244,81 +1226,69 @@ function SCORING:_EventOnDeadOrCrash( Event )
local ThreatLevelTarget = TargetThreatLevel local ThreatLevelTarget = TargetThreatLevel
local ThreatTypeTarget = TargetThreatType local ThreatTypeTarget = TargetThreatType
local ThreatLevelPlayer = Player.ThreatLevel / 10 + 1 local ThreatLevelPlayer = Player.ThreatLevel / 10 + 1
local ThreatPenalty = math.ceil( ( ThreatLevelTarget / ThreatLevelPlayer ) * self.ScaleDestroyPenalty / 10 ) local ThreatPenalty = math.ceil( (ThreatLevelTarget / ThreatLevelPlayer) * self.ScaleDestroyPenalty / 10 )
self:F( { ThreatLevel = ThreatPenalty, ThreatLevelTarget = ThreatLevelTarget, ThreatTypeTarget = ThreatTypeTarget, ThreatLevelPlayer = ThreatLevelPlayer } ) self:F( { ThreatLevel = ThreatPenalty, ThreatLevelTarget = ThreatLevelTarget, ThreatTypeTarget = ThreatTypeTarget, ThreatLevelPlayer = ThreatLevelPlayer } )
Player.Penalty = Player.Penalty + ThreatPenalty Player.Penalty = Player.Penalty + ThreatPenalty
TargetDestroy.Penalty = TargetDestroy.Penalty + ThreatPenalty TargetDestroy.Penalty = TargetDestroy.Penalty + ThreatPenalty
TargetDestroy.PenaltyDestroy = TargetDestroy.PenaltyDestroy + 1 TargetDestroy.PenaltyDestroy = TargetDestroy.PenaltyDestroy + 1
if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player
MESSAGE MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly player '" .. TargetPlayerName .. "' " .. "Penalty: -" .. TargetDestroy.Penalty .. " = " .. Player.Score - Player.Penalty,
TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. MESSAGE.Type.Information )
"Penalty: -" .. TargetDestroy.Penalty .. " = " .. Player.Score - Player.Penalty, :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
MESSAGE.Type.Information :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
)
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
else else
MESSAGE MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly target " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly target " .. "Penalty: -" .. TargetDestroy.Penalty .. " = " .. Player.Score - Player.Penalty,
TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. MESSAGE.Type.Information )
"Penalty: -" .. TargetDestroy.Penalty .. " = " .. Player.Score - Player.Penalty, :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
MESSAGE.Type.Information :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
)
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
end end
Destroyed = true
self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_PENALTY", 1, ThreatPenalty, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_PENALTY", 1, ThreatPenalty, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
Destroyed = true
else else
local ThreatLevelTarget = TargetThreatLevel local ThreatLevelTarget = TargetThreatLevel
local ThreatTypeTarget = TargetThreatType local ThreatTypeTarget = TargetThreatType
local ThreatLevelPlayer = Player.ThreatLevel / 10 + 1 local ThreatLevelPlayer = Player.ThreatLevel / 10 + 1
local ThreatScore = math.ceil( ( ThreatLevelTarget / ThreatLevelPlayer ) * self.ScaleDestroyScore / 10 ) local ThreatScore = math.ceil( (ThreatLevelTarget / ThreatLevelPlayer) * self.ScaleDestroyScore / 10 )
self:F( { ThreatLevel = ThreatScore, ThreatLevelTarget = ThreatLevelTarget, ThreatTypeTarget = ThreatTypeTarget, ThreatLevelPlayer = ThreatLevelPlayer } ) self:F( { ThreatLevel = ThreatScore, ThreatLevelTarget = ThreatLevelTarget, ThreatTypeTarget = ThreatTypeTarget, ThreatLevelPlayer = ThreatLevelPlayer } )
Player.Score = Player.Score + ThreatScore Player.Score = Player.Score + ThreatScore
TargetDestroy.Score = TargetDestroy.Score + ThreatScore TargetDestroy.Score = TargetDestroy.Score + ThreatScore
TargetDestroy.ScoreDestroy = TargetDestroy.ScoreDestroy + 1 TargetDestroy.ScoreDestroy = TargetDestroy.ScoreDestroy + 1
if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player
MESSAGE MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy player '" .. TargetPlayerName .. "' " .. "Score: +" .. TargetDestroy.Score .. " = " .. Player.Score - Player.Penalty,
TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. MESSAGE.Type.Information )
"Score: +" .. TargetDestroy.Score .. " = " .. Player.Score - Player.Penalty, :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
MESSAGE.Type.Information :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
)
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
else else
MESSAGE MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy " .. "Score: +" .. TargetDestroy.Score .. " = " .. Player.Score - Player.Penalty,
TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. MESSAGE.Type.Information )
"Score: +" .. TargetDestroy.Score .. " = " .. Player.Score - Player.Penalty, :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
MESSAGE.Type.Information :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
)
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
end end
Destroyed = true
self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_SCORE", 1, ThreatScore, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_SCORE", 1, ThreatScore, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
Destroyed = true
local UnitName = TargetUnit:GetName() local UnitName = TargetUnit:GetName()
local Score = self.ScoringObjects[UnitName] local Score = self.ScoringObjects[UnitName]
if Score then if Score then
Player.Score = Player.Score + Score Player.Score = Player.Score + Score
TargetDestroy.Score = TargetDestroy.Score + Score TargetDestroy.Score = TargetDestroy.Score + Score
MESSAGE MESSAGE:NewType( self.DisplayMessagePrefix .. "Special target '" .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. " destroyed! " ..
:NewType( self.DisplayMessagePrefix .. "Special target '" .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. " destroyed! " .. "Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! Total: " .. Player.Score - Player.Penalty,
"Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! Total: " .. Player.Score - Player.Penalty, MESSAGE.Type.Information )
MESSAGE.Type.Information :ToAllIf( self:IfMessagesScore() and self:IfMessagesToAll() )
) :ToCoalitionIf( InitCoalition, self:IfMessagesScore() and self:IfMessagesToCoalition() )
:ToAllIf( self:IfMessagesScore() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesScore() and self:IfMessagesToCoalition() )
self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
Destroyed = true Destroyed = true
end end
@@ -1331,13 +1301,12 @@ function SCORING:_EventOnDeadOrCrash( Event )
if ScoreZone:IsVec2InZone( TargetUnit:GetVec2() ) then if ScoreZone:IsVec2InZone( TargetUnit:GetVec2() ) then
Player.Score = Player.Score + Score Player.Score = Player.Score + Score
TargetDestroy.Score = TargetDestroy.Score + Score TargetDestroy.Score = TargetDestroy.Score + Score
MESSAGE MESSAGE:NewType( self.DisplayMessagePrefix .. "Target destroyed in zone '" .. ScoreZone:GetName() .. "'." ..
:NewType( self.DisplayMessagePrefix .. "Target destroyed in zone '" .. ScoreZone:GetName() .. "'." .. "Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! " .. "Total: " .. Player.Score - Player.Penalty,
"Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! " .. MESSAGE.Type.Information )
"Total: " .. Player.Score - Player.Penalty, :ToAllIf( self:IfMessagesZone() and self:IfMessagesToAll() )
MESSAGE.Type.Information ) :ToCoalitionIf( InitCoalition, self:IfMessagesZone() and self:IfMessagesToCoalition() )
:ToAllIf( self:IfMessagesZone() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesZone() and self:IfMessagesToCoalition() )
self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
Destroyed = true Destroyed = true
end end
@@ -1347,22 +1316,20 @@ function SCORING:_EventOnDeadOrCrash( Event )
else else
-- Check if there are Zones where the destruction happened. -- Check if there are Zones where the destruction happened.
for ZoneName, ScoreZoneData in pairs( self.ScoringZones ) do for ZoneName, ScoreZoneData in pairs( self.ScoringZones ) do
self:F( { ScoringZone = ScoreZoneData } ) self:F( { ScoringZone = ScoreZoneData } )
local ScoreZone = ScoreZoneData.ScoreZone -- Core.Zone#ZONE_BASE local ScoreZone = ScoreZoneData.ScoreZone -- Core.Zone#ZONE_BASE
local Score = ScoreZoneData.Score local Score = ScoreZoneData.Score
if ScoreZone:IsVec2InZone( TargetUnit:GetVec2() ) then if ScoreZone:IsVec2InZone( TargetUnit:GetVec2() ) then
Player.Score = Player.Score + Score Player.Score = Player.Score + Score
TargetDestroy.Score = TargetDestroy.Score + Score TargetDestroy.Score = TargetDestroy.Score + Score
MESSAGE MESSAGE:NewType( self.DisplayMessagePrefix .. "Scenery destroyed in zone '" .. ScoreZone:GetName() .. "'." ..
:NewType( self.DisplayMessagePrefix .. "Scenery destroyed in zone '" .. ScoreZone:GetName() .. "'." .. "Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! " .. "Total: " .. Player.Score - Player.Penalty,
"Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! " .. MESSAGE.Type.Information )
"Total: " .. Player.Score - Player.Penalty, :ToAllIf( self:IfMessagesZone() and self:IfMessagesToAll() )
MESSAGE.Type.Information :ToCoalitionIf( InitCoalition, self:IfMessagesZone() and self:IfMessagesToCoalition() )
)
:ToAllIf( self:IfMessagesZone() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesZone() and self:IfMessagesToCoalition() )
Destroyed = true
self:ScoreCSV( PlayerName, "", "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, "", "Scenery", TargetUnitType ) self:ScoreCSV( PlayerName, "", "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, "", "Scenery", TargetUnitType )
Destroyed = true
end end
end end
end end
@@ -1378,7 +1345,6 @@ function SCORING:_EventOnDeadOrCrash( Event )
end end
end end
--- Produce detailed report of player hit scores. --- Produce detailed report of player hit scores.
-- @param #SCORING self -- @param #SCORING self
-- @param #string PlayerName The name of the player. -- @param #string PlayerName The name of the player.
@@ -1420,7 +1386,7 @@ function SCORING:ReportDetailedPlayerHits( PlayerName )
PlayerScore = PlayerScore + Score PlayerScore = PlayerScore + Score
PlayerPenalty = PlayerPenalty + Penalty PlayerPenalty = PlayerPenalty + Penalty
else else
--ScoreMessageHits = ScoreMessageHits .. string.format( "%s:%d ", string.format(CategoryName, 1, 1), 0 ) -- ScoreMessageHits = ScoreMessageHits .. string.format( "%s:%d ", string.format(CategoryName, 1, 1), 0 )
end end
end end
if ScoreMessageHits ~= "" then if ScoreMessageHits ~= "" then
@@ -1431,7 +1397,6 @@ function SCORING:ReportDetailedPlayerHits( PlayerName )
return ScoreMessage, PlayerScore, PlayerPenalty return ScoreMessage, PlayerScore, PlayerPenalty
end end
--- Produce detailed report of player destroy scores. --- Produce detailed report of player destroy scores.
-- @param #SCORING self -- @param #SCORING self
-- @param #string PlayerName The name of the player. -- @param #string PlayerName The name of the player.
@@ -1478,7 +1443,7 @@ function SCORING:ReportDetailedPlayerDestroys( PlayerName )
PlayerScore = PlayerScore + Score PlayerScore = PlayerScore + Score
PlayerPenalty = PlayerPenalty + Penalty PlayerPenalty = PlayerPenalty + Penalty
else else
--ScoreMessageDestroys = ScoreMessageDestroys .. string.format( "%s:%d ", string.format(CategoryName, 1, 1), 0 ) -- ScoreMessageDestroys = ScoreMessageDestroys .. string.format( "%s:%d ", string.format(CategoryName, 1, 1), 0 )
end end
end end
if ScoreMessageDestroys ~= "" then if ScoreMessageDestroys ~= "" then
@@ -1597,7 +1562,6 @@ function SCORING:ReportDetailedPlayerMissions( PlayerName )
return ScoreMessage, PlayerScore, PlayerPenalty return ScoreMessage, PlayerScore, PlayerPenalty
end end
--- Report Group Score Summary --- Report Group Score Summary
-- @param #SCORING self -- @param #SCORING self
-- @param Wrapper.Group#GROUP PlayerGroup The player group. -- @param Wrapper.Group#GROUP PlayerGroup The player group.
@@ -1637,13 +1601,12 @@ function SCORING:ReportScoreGroupSummary( PlayerGroup )
local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions
local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + PenaltyGoals + PenaltyMissions local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + PenaltyGoals + PenaltyMissions
PlayerMessage = PlayerMessage = string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )",
string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )", PlayerName,
PlayerName, PlayerScore - PlayerPenalty,
PlayerScore - PlayerPenalty, PlayerScore,
PlayerScore, PlayerPenalty
PlayerPenalty )
)
MESSAGE:NewType( PlayerMessage, MESSAGE.Type.Detailed ):ToGroup( PlayerGroup ) MESSAGE:NewType( PlayerMessage, MESSAGE.Type.Detailed ):ToGroup( PlayerGroup )
end end
end end
@@ -1687,20 +1650,19 @@ function SCORING:ReportScoreGroupDetailed( PlayerGroup )
self:F( { ReportMissions, ScoreMissions, PenaltyMissions } ) self:F( { ReportMissions, ScoreMissions, PenaltyMissions } )
local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions
local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + ScoreGoals + PenaltyMissions local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + PenaltyGoals + PenaltyMissions
PlayerMessage = PlayerMessage = string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )%s%s%s%s%s",
string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )%s%s%s%s%s", PlayerName,
PlayerName, PlayerScore - PlayerPenalty,
PlayerScore - PlayerPenalty, PlayerScore,
PlayerScore, PlayerPenalty,
PlayerPenalty, ReportHits,
ReportHits, ReportDestroys,
ReportDestroys, ReportCoalitionChanges,
ReportCoalitionChanges, ReportGoals,
ReportGoals, ReportMissions
ReportMissions )
)
MESSAGE:NewType( PlayerMessage, MESSAGE.Type.Detailed ):ToGroup( PlayerGroup ) MESSAGE:NewType( PlayerMessage, MESSAGE.Type.Detailed ):ToGroup( PlayerGroup )
end end
end end
@@ -1743,32 +1705,30 @@ function SCORING:ReportScoreAllSummary( PlayerGroup )
self:F( { ReportMissions, ScoreMissions, PenaltyMissions } ) self:F( { ReportMissions, ScoreMissions, PenaltyMissions } )
local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions
local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + ScoreGoals + PenaltyMissions local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + PenaltyGoals + PenaltyMissions
PlayerMessage = PlayerMessage = string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )",
string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )", PlayerName,
PlayerName, PlayerScore - PlayerPenalty,
PlayerScore - PlayerPenalty, PlayerScore,
PlayerScore, PlayerPenalty
PlayerPenalty )
)
MESSAGE:NewType( PlayerMessage, MESSAGE.Type.Overview ):ToGroup( PlayerGroup ) MESSAGE:NewType( PlayerMessage, MESSAGE.Type.Overview ):ToGroup( PlayerGroup )
end end
end end
end end
function SCORING:SecondsToClock( sSeconds )
function SCORING:SecondsToClock(sSeconds)
local nSeconds = sSeconds local nSeconds = sSeconds
if nSeconds == 0 then if nSeconds == 0 then
--return nil; -- return nil;
return "00:00:00"; return "00:00:00";
else else
nHours = string.format("%02.f", math.floor(nSeconds/3600)); nHours = string.format( "%02.f", math.floor( nSeconds / 3600 ) );
nMins = string.format("%02.f", math.floor(nSeconds/60 - (nHours*60))); nMins = string.format( "%02.f", math.floor( nSeconds / 60 - (nHours * 60) ) );
nSecs = string.format("%02.f", math.floor(nSeconds - nHours*3600 - nMins *60)); nSecs = string.format( "%02.f", math.floor( nSeconds - nHours * 3600 - nMins * 60 ) );
return nHours..":"..nMins..":"..nSecs return nHours .. ":" .. nMins .. ":" .. nSecs
end end
end end
@@ -1793,9 +1753,9 @@ function SCORING:OpenCSV( ScoringCSV )
error( "Error: Cannot open CSV file in " .. lfs.writedir() ) error( "Error: Cannot open CSV file in " .. lfs.writedir() )
end end
self.CSVFile:write( '"GameName","RunTime","Time","PlayerName","TargetPlayerName","ScoreType","PlayerUnitCoaltion","PlayerUnitCategory","PlayerUnitType","PlayerUnitName","TargetUnitCoalition","TargetUnitCategory","TargetUnitType","TargetUnitName","Times","Score"\n' ) self.CSVFile:write( '"GameName","RunTime","Time","PlayerName","TargetPlayerName","ScoreType","PlayerUnitCoalition","PlayerUnitCategory","PlayerUnitType","PlayerUnitName","TargetUnitCoalition","TargetUnitCategory","TargetUnitType","TargetUnitName","Times","Score"\n' )
self.RunTime = os.date("%y-%m-%d_%H-%M-%S") self.RunTime = os.date( "%y-%m-%d_%H-%M-%S" )
else else
error( "A string containing the CSV file name must be given." ) error( "A string containing the CSV file name must be given." )
end end
@@ -1805,7 +1765,6 @@ function SCORING:OpenCSV( ScoringCSV )
return self return self
end end
--- Registers a score for a player. --- Registers a score for a player.
-- @param #SCORING self -- @param #SCORING self
-- @param #string PlayerName The name of the player. -- @param #string PlayerName The name of the player.
@@ -1823,7 +1782,7 @@ end
-- @param #string TargetUnitType The type of the target unit. -- @param #string TargetUnitType The type of the target unit.
-- @return #SCORING self -- @return #SCORING self
function SCORING:ScoreCSV( PlayerName, TargetPlayerName, ScoreType, ScoreTimes, ScoreAmount, PlayerUnitName, PlayerUnitCoalition, PlayerUnitCategory, PlayerUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) function SCORING:ScoreCSV( PlayerName, TargetPlayerName, ScoreType, ScoreTimes, ScoreAmount, PlayerUnitName, PlayerUnitCoalition, PlayerUnitCategory, PlayerUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
--write statistic information to file -- write statistic information to file
local ScoreTime = self:SecondsToClock( timer.getTime() ) local ScoreTime = self:SecondsToClock( timer.getTime() )
PlayerName = PlayerName:gsub( '"', '_' ) PlayerName = PlayerName:gsub( '"', '_' )
@@ -1835,7 +1794,7 @@ function SCORING:ScoreCSV( PlayerName, TargetPlayerName, ScoreType, ScoreTimes,
if PlayerUnit then if PlayerUnit then
if not PlayerUnitCategory then if not PlayerUnitCategory then
--PlayerUnitCategory = SCORINGCategory[PlayerUnit:getCategory()] -- PlayerUnitCategory = SCORINGCategory[PlayerUnit:getCategory()]
PlayerUnitCategory = _SCORINGCategory[PlayerUnit:getDesc().category] PlayerUnitCategory = _SCORINGCategory[PlayerUnit:getDesc().category]
end end
@@ -1888,7 +1847,6 @@ function SCORING:ScoreCSV( PlayerName, TargetPlayerName, ScoreType, ScoreTimes,
end end
end end
function SCORING:CloseCSV() function SCORING:CloseCSV()
if lfs and io and os then if lfs and io and os then
self.CSVFile:close() self.CSVFile:close()
+238 -104
View File
@@ -19,7 +19,7 @@
-- --
-- ### Authors: **FlightControl**, **applevangelist** -- ### Authors: **FlightControl**, **applevangelist**
-- --
-- Last Update: Nov 2021 -- Last Update: Feb 2022
-- --
-- === -- ===
-- --
@@ -59,6 +59,7 @@ SEAD = {
Padding = 10, Padding = 10,
CallBack = nil, CallBack = nil,
UseCallBack = false, UseCallBack = false,
debug = false,
} }
--- Missile enumerators --- Missile enumerators
@@ -76,6 +77,8 @@ SEAD = {
["X_25"] = "X_25", ["X_25"] = "X_25",
["X_31"] = "X_31", ["X_31"] = "X_31",
["Kh25"] = "Kh25", ["Kh25"] = "Kh25",
["BGM_109"] = "BGM_109",
["AGM_154"] = "AGM_154",
} }
--- Missile enumerators - from DCS ME and Wikipedia --- Missile enumerators - from DCS ME and Wikipedia
@@ -85,7 +88,7 @@ SEAD = {
["AGM_88"] = { 150, 3}, ["AGM_88"] = { 150, 3},
["AGM_45"] = { 12, 2}, ["AGM_45"] = { 12, 2},
["AGM_122"] = { 16.5, 2.3}, ["AGM_122"] = { 16.5, 2.3},
["AGM_84"] = { 280, 0.85}, ["AGM_84"] = { 280, 0.8},
["ALARM"] = { 45, 2}, ["ALARM"] = { 45, 2},
["LD-10"] = { 60, 4}, ["LD-10"] = { 60, 4},
["X_58"] = { 70, 4}, ["X_58"] = { 70, 4},
@@ -93,6 +96,8 @@ SEAD = {
["X_25"] = { 25, 0.76}, ["X_25"] = { 25, 0.76},
["X_31"] = {150, 3}, ["X_31"] = {150, 3},
["Kh25"] = {25, 0.8}, ["Kh25"] = {25, 0.8},
["BGM_109"] = {460, 0.705}, --in-game ~465kn
["AGM_154"] = {130, 0.61},
} }
--- Creates the main object which is handling defensive actions for SA sites or moving SA vehicles. --- Creates the main object which is handling defensive actions for SA sites or moving SA vehicles.
@@ -108,8 +113,8 @@ SEAD = {
-- SEAD_RU_SAM_Defenses = SEAD:New( { 'RU SA-6 Kub', 'RU SA-6 Defenses', 'RU MI-26 Troops', 'RU Attack Gori' } ) -- SEAD_RU_SAM_Defenses = SEAD:New( { 'RU SA-6 Kub', 'RU SA-6 Defenses', 'RU MI-26 Troops', 'RU Attack Gori' } )
function SEAD:New( SEADGroupPrefixes, Padding ) function SEAD:New( SEADGroupPrefixes, Padding )
local self = BASE:Inherit( self, BASE:New() ) local self = BASE:Inherit( self, FSM:New() )
self:F( SEADGroupPrefixes ) self:T( SEADGroupPrefixes )
if type( SEADGroupPrefixes ) == 'table' then if type( SEADGroupPrefixes ) == 'table' then
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
@@ -122,14 +127,21 @@ function SEAD:New( SEADGroupPrefixes, Padding )
local padding = Padding or 10 local padding = Padding or 10
if padding < 10 then padding = 10 end if padding < 10 then padding = 10 end
self.Padding = padding self.Padding = padding
self.UseEmissionsOnOff = false self.UseEmissionsOnOff = true
self.debug = false
self.CallBack = nil self.CallBack = nil
self.UseCallBack = false self.UseCallBack = false
self:HandleEvent( EVENTS.Shot, self.HandleEventShot ) self:HandleEvent( EVENTS.Shot, self.HandleEventShot )
self:I("*** SEAD - Started Version 0.3.3") -- Start State.
self:SetStartState("Running")
self:AddTransition("*", "ManageEvasion", "*")
self:AddTransition("*", "CalculateHitZone", "*")
self:I("*** SEAD - Started Version 0.4.3")
return self return self
end end
@@ -213,7 +225,7 @@ function SEAD:_CheckHarms(WeaponName)
local hit = false local hit = false
local name = "" local name = ""
for _,_name in pairs (SEAD.Harms) do for _,_name in pairs (SEAD.Harms) do
if string.find(WeaponName,_name,1) then if string.find(WeaponName,_name,1,true) then
hit = true hit = true
name = _name name = _name
break break
@@ -249,6 +261,186 @@ function SEAD:_GetDistance(_point1, _point2)
end end
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. --- (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 #SEAD self
-- @param Core.Event#EVENTDATA EventData -- @param Core.Event#EVENTDATA EventData
@@ -256,6 +448,7 @@ end
function SEAD:HandleEventShot( EventData ) function SEAD:HandleEventShot( EventData )
self:T( { EventData.id } ) self:T( { EventData.id } )
local SEADPlane = EventData.IniUnit -- Wrapper.Unit#UNIT local SEADPlane = EventData.IniUnit -- Wrapper.Unit#UNIT
local SEADGroup = EventData.IniGroup -- Wrapper.Group#GROUP
local SEADPlanePos = SEADPlane:GetCoordinate() -- Core.Point#COORDINATE local SEADPlanePos = SEADPlane:GetCoordinate() -- Core.Point#COORDINATE
local SEADUnit = EventData.IniDCSUnit local SEADUnit = EventData.IniDCSUnit
local SEADUnitName = EventData.IniDCSUnitName local SEADUnitName = EventData.IniDCSUnitName
@@ -270,114 +463,55 @@ function SEAD:HandleEventShot( EventData )
local _targetskill = "Random" local _targetskill = "Random"
local _targetgroupname = "none" local _targetgroupname = "none"
local _target = EventData.Weapon:getTarget() -- Identify target local _target = EventData.Weapon:getTarget() -- Identify target
local _targetUnit = UNIT:Find(_target) -- Wrapper.Unit#UNIT 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 local _targetgroup = nil -- Wrapper.Group#GROUP
if _targetUnit and _targetUnit:IsAlive() then self:T(string.format("*** Targetcat = %d",targetcat))
_targetgroup = _targetUnit:GetGroup() if targetcat == Object.Category.UNIT then -- UNIT
_targetgroupname = _targetgroup:GetName() -- group name self:T("*** Target Category UNIT")
local _targetUnitName = _targetUnit:GetName() _targetUnit = UNIT:Find(_target) -- Wrapper.Unit#UNIT
_targetUnit:GetSkill() if _targetUnit and _targetUnit:IsAlive() then
_targetskill = _targetUnit:GetSkill() _targetgroup = _targetUnit:GetGroup()
_targetgroupname = _targetgroup:GetName() -- group name
local _targetUnitName = _targetUnit:GetName()
_targetUnit:GetSkill()
_targetskill = _targetUnit:GetSkill()
end
elseif targetcat == Object.Category.STATIC then
self:T("*** Target Category STATIC")
local seadset = SET_GROUP:New():FilterPrefixes(self.SEADGroupPrefixes):FilterOnce()
local targetpoint = _target:getPoint() or {x=0,y=0,z=0}
local tgtcoord = COORDINATE:NewFromVec3(targetpoint)
local tgtgrp = seadset:FindNearestGroupFromPointVec2(tgtcoord)
if tgtgrp and tgtgrp:IsAlive() then
_targetgroup = tgtgrp
_targetgroupname = tgtgrp:GetName() -- group name
_targetskill = tgtgrp:GetUnit(1):GetSkill()
self:T("*** Found Target = ".. _targetgroupname)
end
end end
-- see if we are shot at -- see if we are shot at
local SEADGroupFound = false local SEADGroupFound = false
for SEADGroupPrefixID, SEADGroupPrefix in pairs( self.SEADGroupPrefixes ) do for SEADGroupPrefixID, SEADGroupPrefix in pairs( self.SEADGroupPrefixes ) do
self:T( _targetgroupname, SEADGroupPrefix ) self:T("Target = ".. _targetgroupname .. " | Prefix = " .. SEADGroupPrefix )
if string.find( _targetgroupname, SEADGroupPrefix, 1, true ) then if string.find( _targetgroupname, SEADGroupPrefix,1,true ) then
SEADGroupFound = true SEADGroupFound = true
self:T( '*** SEAD - Group Match Found' ) self:T( '*** SEAD - Group Match Found' )
break break
end end
end end
if SEADGroupFound == true then -- yes we are being attacked if SEADGroupFound == true then -- yes we are being attacked
if _targetskill == "Random" then -- when skill is random, choose a skill self:ManageEvasion(_targetskill,_targetgroup,SEADPlanePos,SEADWeaponName,SEADGroup)
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) -- 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
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)
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:OptionAlarmStateAuto()
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 > (3*delay) then delay = (_tti / 2) * 0.9 end -- shot from afar
local SuppressionStartTime = timer.getTime() + delay
local SuppressionEndTime = timer.getTime() + _tti + self.Padding
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},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)
end
end
end
end
end
end end
end end
return self return self
@@ -50,7 +50,7 @@
-- @field #boolean Report If true, send status messages to coalition. -- @field #boolean Report If true, send status messages to coalition.
-- @field Wrapper.Static#STATIC warehouse The phyical warehouse structure. -- @field Wrapper.Static#STATIC warehouse The phyical warehouse structure.
-- @field #string alias Alias of the warehouse. Name its called when sending messages. -- @field #string alias Alias of the warehouse. Name its called when sending messages.
-- @field Core.Zone#ZONE zone Zone around the warehouse. If this zone is captured, the warehouse and all its assets goes to the capturing coaliton. -- @field Core.Zone#ZONE zone Zone around the warehouse. If this zone is captured, the warehouse and all its assets goes to the capturing coalition.
-- @field Wrapper.Airbase#AIRBASE airbase Airbase the warehouse belongs to. -- @field Wrapper.Airbase#AIRBASE airbase Airbase the warehouse belongs to.
-- @field #string airbasename Name of the airbase associated to the warehouse. -- @field #string airbasename Name of the airbase associated to the warehouse.
-- @field Core.Point#COORDINATE road Closest point to warehouse on road. -- @field Core.Point#COORDINATE road Closest point to warehouse on road.
@@ -764,7 +764,7 @@
-- warehouseBatumi:Load("D:\\My Warehouse Data\\") -- warehouseBatumi:Load("D:\\My Warehouse Data\\")
-- warehouseBatumi:Start() -- warehouseBatumi:Start()
-- --
-- This sequence loads all assets from file. If a warehouse was captured in the last mission, it also respawns the static warehouse structure with the right coaliton. -- This sequence loads all assets from file. If a warehouse was captured in the last mission, it also respawns the static warehouse structure with the right coalition.
-- However, it due to DCS limitations it is not possible to set the airbase coalition. This has to be done manually in the mission editor. Or alternatively, one could -- However, it due to DCS limitations it is not possible to set the airbase coalition. This has to be done manually in the mission editor. Or alternatively, one could
-- spawn some ground units via a self request and let them capture the airbase. -- spawn some ground units via a self request and let them capture the airbase.
-- --
@@ -1811,7 +1811,7 @@ WAREHOUSE.version="1.0.2"
-- DONE: Add shipping lanes between warehouses. -- DONE: Add shipping lanes between warehouses.
-- DONE: Handle cases with immobile units <== should be handled by dispatcher classes. -- DONE: Handle cases with immobile units <== should be handled by dispatcher classes.
-- DONE: Handle cases for aircraft carriers and other ships. Place warehouse on carrier possible? On others probably not - exclude them? -- DONE: Handle cases for aircraft carriers and other ships. Place warehouse on carrier possible? On others probably not - exclude them?
-- DONE: Add general message function for sending to coaliton or debug. -- DONE: Add general message function for sending to coalition or debug.
-- DONE: Fine tune event handlers. -- DONE: Fine tune event handlers.
-- DONE: Improve generalized attributes. -- DONE: Improve generalized attributes.
-- DONE: If warehouse is destroyed, all asssets are gone. -- DONE: If warehouse is destroyed, all asssets are gone.
@@ -3155,7 +3155,7 @@ end
-- @param MinAssets (Optional) Minimum number of assets the warehouse should have. Default 0. -- @param MinAssets (Optional) Minimum number of assets the warehouse should have. Default 0.
-- @param #string Descriptor (Optional) Descriptor describing the selected assets which should be in stock. See @{#WAREHOUSE.Descriptor} for possible values. -- @param #string Descriptor (Optional) Descriptor describing the selected assets which should be in stock. See @{#WAREHOUSE.Descriptor} for possible values.
-- @param DescriptorValue (Optional) Descriptor value selecting the type of assets which should be in stock. -- @param DescriptorValue (Optional) Descriptor value selecting the type of assets which should be in stock.
-- @param DCS#Coalition.side Coalition (Optional) Coalition side of the warehouse. Default is the same coaliton as the present warehouse. Set to false for any coalition. -- @param DCS#Coalition.side Coalition (Optional) Coalition side of the warehouse. Default is the same coalition as the present warehouse. Set to false for any coalition.
-- @param Core.Point#COORDINATE RefCoordinate (Optional) Coordinate to which the closest warehouse is searched. Default is the warehouse calling this function. -- @param Core.Point#COORDINATE RefCoordinate (Optional) Coordinate to which the closest warehouse is searched. Default is the warehouse calling this function.
-- @return #WAREHOUSE The the nearest warehouse object. Or nil if no warehouse is found. -- @return #WAREHOUSE The the nearest warehouse object. Or nil if no warehouse is found.
-- @return #number The distance to the nearest warehouse in meters. Or nil if no warehouse is found. -- @return #number The distance to the nearest warehouse in meters. Or nil if no warehouse is found.
@@ -3267,7 +3267,7 @@ function WAREHOUSE:onafterStart(From, Event, To)
-- Short info. -- Short info.
local text=string.format("Starting warehouse %s alias %s:\n",self.warehouse:GetName(), self.alias) local text=string.format("Starting warehouse %s alias %s:\n",self.warehouse:GetName(), self.alias)
text=text..string.format("Coaliton = %s\n", self:GetCoalitionName()) text=text..string.format("Coalition = %s\n", self:GetCoalitionName())
text=text..string.format("Country = %s\n", self:GetCountryName()) text=text..string.format("Country = %s\n", self:GetCountryName())
text=text..string.format("Airbase = %s (category=%d)\n", self:GetAirbaseName(), self:GetAirbaseCategory()) text=text..string.format("Airbase = %s (category=%d)\n", self:GetAirbaseName(), self:GetAirbaseCategory())
env.info(text) env.info(text)
@@ -8460,7 +8460,7 @@ function WAREHOUSE:_GetStockAssetsText(messagetoall)
end end
--- Create or update mark text at warehouse, which is displayed in F10 map showing how many assets of each type are in stock. --- Create or update mark text at warehouse, which is displayed in F10 map showing how many assets of each type are in stock.
-- Only the coaliton of the warehouse owner is able to see it. -- Only the coalition of the warehouse owner is able to see it.
-- @param #WAREHOUSE self -- @param #WAREHOUSE self
-- @return #string Text about warehouse stock -- @return #string Text about warehouse stock
function WAREHOUSE:_UpdateWarehouseMarkText() function WAREHOUSE:_UpdateWarehouseMarkText()
@@ -804,7 +804,7 @@ do -- ZONE_CAPTURE_COALITION
return IsEmpty return IsEmpty
end end
--- Check if zone is "Guarded", i.e. only one (the defending) coaliton is present inside the zone. --- Check if zone is "Guarded", i.e. only one (the defending) coalition is present inside the zone.
-- @param #ZONE_CAPTURE_COALITION self -- @param #ZONE_CAPTURE_COALITION self
-- @return #boolean self:IsAllInZoneOfCoalition( self.Coalition ) -- @return #boolean self:IsAllInZoneOfCoalition( self.Coalition )
function ZONE_CAPTURE_COALITION:IsGuarded() function ZONE_CAPTURE_COALITION:IsGuarded()
@@ -826,7 +826,7 @@ do -- ZONE_CAPTURE_COALITION
return IsCaptured return IsCaptured
end end
--- Check if zone is "Attacked", i.e. another coaliton entered the zone. --- Check if zone is "Attacked", i.e. another coalition entered the zone.
-- @param #ZONE_CAPTURE_COALITION self -- @param #ZONE_CAPTURE_COALITION self
-- @return #boolean self:IsSomeInZoneOfCoalition( self.Coalition ) -- @return #boolean self:IsSomeInZoneOfCoalition( self.Coalition )
function ZONE_CAPTURE_COALITION:IsAttacked() function ZONE_CAPTURE_COALITION:IsAttacked()
@@ -906,7 +906,6 @@ do -- ZONE_CAPTURE_COALITION
-- Misc Functions -- Misc Functions
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Update Mark on F10 map. --- Update Mark on F10 map.
-- @param #ZONE_CAPTURE_COALITION self -- @param #ZONE_CAPTURE_COALITION self
function ZONE_CAPTURE_COALITION:Mark() function ZONE_CAPTURE_COALITION:Mark()
@@ -925,7 +924,7 @@ do -- ZONE_CAPTURE_COALITION
Coord:RemoveMark(self.MarkBlue) Coord:RemoveMark(self.MarkBlue)
end end
-- Create new marks for each coaliton. -- Create new marks for each coalition.
if self.Coalition == coalition.side.BLUE then if self.Coalition == coalition.side.BLUE then
self.MarkBlue = Coord:MarkToCoalitionBlue( "Coalition: Blue\nGuard Zone: " .. ZoneName .. "\nStatus: " .. State ) self.MarkBlue = Coord:MarkToCoalitionBlue( "Coalition: Blue\nGuard Zone: " .. ZoneName .. "\nStatus: " .. State )
self.MarkRed = Coord:MarkToCoalitionRed( "Coalition: Blue\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State ) self.MarkRed = Coord:MarkToCoalitionRed( "Coalition: Blue\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State )
@@ -24,7 +24,6 @@ do -- ZoneGoal
-- @field #table ObjectCategories Table of object categories that are able to hold a zone. Default is UNITS and STATICS. -- @field #table ObjectCategories Table of object categories that are able to hold a zone. Default is UNITS and STATICS.
-- @extends Functional.ZoneGoal#ZONE_GOAL -- @extends Functional.ZoneGoal#ZONE_GOAL
--- ZONE_GOAL_COALITION models processes that have a Goal with a defined achievement involving a Zone for a Coalition. --- ZONE_GOAL_COALITION models processes that have a Goal with a defined achievement involving a Zone for a Coalition.
-- Derived classes implement the ways how the achievements can be realized. -- Derived classes implement the ways how the achievements can be realized.
-- --
@@ -42,10 +41,10 @@ do -- ZoneGoal
-- --
-- @field #ZONE_GOAL_COALITION -- @field #ZONE_GOAL_COALITION
ZONE_GOAL_COALITION = { ZONE_GOAL_COALITION = {
ClassName = "ZONE_GOAL_COALITION", ClassName = "ZONE_GOAL_COALITION",
Coalition = nil, Coalition = nil,
PreviousCoaliton = nil, PreviousCoalition = nil,
UnitCategories = nil, UnitCategories = nil,
ObjectCategories = nil, ObjectCategories = nil,
} }
@@ -61,31 +60,30 @@ do -- ZoneGoal
function ZONE_GOAL_COALITION:New( Zone, Coalition, UnitCategories ) function ZONE_GOAL_COALITION:New( Zone, Coalition, UnitCategories )
if not Zone then if not Zone then
BASE:E("ERROR: No Zone specified in ZONE_GOAL_COALITON!") BASE:E( "ERROR: No Zone specified in ZONE_GOAL_COALITION!" )
return nil return nil
end end
-- Inherit ZONE_GOAL. -- Inherit ZONE_GOAL.
local self = BASE:Inherit( self, ZONE_GOAL:New( Zone ) ) -- #ZONE_GOAL_COALITION local self = BASE:Inherit( self, ZONE_GOAL:New( Zone ) ) -- #ZONE_GOAL_COALITION
self:F( { Zone = Zone, Coalition = Coalition } ) self:F( { Zone = Zone, Coalition = Coalition } )
-- Set initial owner. -- Set initial owner.
self:SetCoalition( Coalition or coalition.side.NEUTRAL) self:SetCoalition( Coalition or coalition.side.NEUTRAL )
-- Set default unit and object categories for the zone scan. -- Set default unit and object categories for the zone scan.
self:SetUnitCategories(UnitCategories) self:SetUnitCategories( UnitCategories )
self:SetObjectCategories() self:SetObjectCategories()
return self return self
end end
--- Set the owning coalition of the zone. --- Set the owning coalition of the zone.
-- @param #ZONE_GOAL_COALITION self -- @param #ZONE_GOAL_COALITION self
-- @param DCSCoalition.DCSCoalition#coalition Coalition The coalition ID, e.g. *coalition.side.RED*. -- @param DCSCoalition.DCSCoalition#coalition Coalition The coalition ID, e.g. *coalition.side.RED*.
-- @return #ZONE_GOAL_COALITION -- @return #ZONE_GOAL_COALITION
function ZONE_GOAL_COALITION:SetCoalition( Coalition ) function ZONE_GOAL_COALITION:SetCoalition( Coalition )
self.PreviousCoalition=self.Coalition or Coalition self.PreviousCoalition = self.Coalition or Coalition
self.Coalition = Coalition self.Coalition = Coalition
return self return self
end end
@@ -96,11 +94,11 @@ do -- ZoneGoal
-- @return #ZONE_GOAL_COALITION -- @return #ZONE_GOAL_COALITION
function ZONE_GOAL_COALITION:SetUnitCategories( UnitCategories ) function ZONE_GOAL_COALITION:SetUnitCategories( UnitCategories )
if UnitCategories and type(UnitCategories)~="table" then if UnitCategories and type( UnitCategories ) ~= "table" then
UnitCategories={UnitCategories} UnitCategories = { UnitCategories }
end end
self.UnitCategories=UnitCategories or {Unit.Category.GROUND_UNIT} self.UnitCategories = UnitCategories or { Unit.Category.GROUND_UNIT }
return self return self
end end
@@ -111,11 +109,11 @@ do -- ZoneGoal
-- @return #ZONE_GOAL_COALITION -- @return #ZONE_GOAL_COALITION
function ZONE_GOAL_COALITION:SetObjectCategories( ObjectCategories ) function ZONE_GOAL_COALITION:SetObjectCategories( ObjectCategories )
if ObjectCategories and type(ObjectCategories)~="table" then if ObjectCategories and type( ObjectCategories ) ~= "table" then
ObjectCategories={ObjectCategories} ObjectCategories = { ObjectCategories }
end end
self.ObjectCategories=ObjectCategories or {Object.Category.UNIT, Object.Category.STATIC} self.ObjectCategories = ObjectCategories or { Object.Category.UNIT, Object.Category.STATIC }
return self return self
end end
@@ -127,22 +125,20 @@ do -- ZoneGoal
return self.Coalition return self.Coalition
end end
--- Get the previous coaliton, i.e. the one owning the zone before the current one. --- Get the previous coalition, i.e. the one owning the zone before the current one.
-- @param #ZONE_GOAL_COALITION self -- @param #ZONE_GOAL_COALITION self
-- @return DCSCoalition.DCSCoalition#coalition Coalition. -- @return DCSCoalition.DCSCoalition#coalition Coalition.
function ZONE_GOAL_COALITION:GetPreviousCoalition() function ZONE_GOAL_COALITION:GetPreviousCoalition()
return self.PreviousCoalition return self.PreviousCoalition
end end
--- Get the owning coalition name of the zone. --- Get the owning coalition name of the zone.
-- @param #ZONE_GOAL_COALITION self -- @param #ZONE_GOAL_COALITION self
-- @return #string Coalition name. -- @return #string Coalition name.
function ZONE_GOAL_COALITION:GetCoalitionName() function ZONE_GOAL_COALITION:GetCoalitionName()
return UTILS.GetCoalitionName(self.Coalition) return UTILS.GetCoalitionName( self.Coalition )
end end
--- Check status Coalition ownership. --- Check status Coalition ownership.
-- @param #ZONE_GOAL_COALITION self -- @param #ZONE_GOAL_COALITION self
-- @return #ZONE_GOAL_COALITION -- @return #ZONE_GOAL_COALITION
@@ -152,8 +148,8 @@ do -- ZoneGoal
local State = self:GetState() local State = self:GetState()
-- Debug text. -- Debug text.
local text=string.format("Zone state=%s, Owner=%s, Scanning...", State, self:GetCoalitionName()) local text = string.format( "Zone state=%s, Owner=%s, Scanning...", State, self:GetCoalitionName() )
self:F(text) self:F( text )
-- Scan zone. -- Scan zone.
self:Scan( self.ObjectCategories, self.UnitCategories ) self:Scan( self.ObjectCategories, self.UnitCategories )
+1
View File
@@ -4,6 +4,7 @@ __Moose.Include( 'Scripts/Moose/Utilities/Utils.lua' )
__Moose.Include( 'Scripts/Moose/Utilities/Profiler.lua' ) __Moose.Include( 'Scripts/Moose/Utilities/Profiler.lua' )
__Moose.Include( 'Scripts/Moose/Utilities/Templates.lua' ) __Moose.Include( 'Scripts/Moose/Utilities/Templates.lua' )
__Moose.Include( 'Scripts/Moose/Utilities/STTS.lua' ) __Moose.Include( 'Scripts/Moose/Utilities/STTS.lua' )
__Moose.Include( 'Scripts/Moose/Utilities/FiFo.lua' )
__Moose.Include( 'Scripts/Moose/Core/Base.lua' ) __Moose.Include( 'Scripts/Moose/Core/Base.lua' )
__Moose.Include( 'Scripts/Moose/Core/Beacon.lua' ) __Moose.Include( 'Scripts/Moose/Core/Beacon.lua' )
+5 -1
View File
@@ -1740,6 +1740,10 @@ function ATIS:onafterBroadcast( From, Event, To )
end end
-- Wind -- Wind
-- Adding a space after each digit of WINDFROM to convert this to aviation-speak for TTS via SRS
if self.useSRS then
WINDFROM = string.gsub(WINDFROM,".", "%1 ")
end
if self.metric then if self.metric then
subtitle = string.format( "Wind from %s at %s m/s", WINDFROM, WINDSPEED ) subtitle = string.format( "Wind from %s at %s m/s", WINDFROM, WINDSPEED )
else else
@@ -2197,7 +2201,7 @@ function ATIS:onafterBroadcast( From, Event, To )
-- TACAN -- TACAN
if self.tacan then if self.tacan then
subtitle = string.format( "TACAN channel %dX", self.tacan ) subtitle = string.format( "TACAN channel %dX Ray", self.tacan )
if not self.useSRS then if not self.useSRS then
self:Transmission( ATIS.Sound.TACANChannel, 1.0, subtitle ) self:Transmission( ATIS.Sound.TACANChannel, 1.0, subtitle )
self.radioqueue:Number2Transmission( tostring( self.tacan ), nil, 0.2 ) self.radioqueue:Number2Transmission( tostring( self.tacan ), nil, 0.2 )
+279 -137
View File
@@ -32,9 +32,12 @@
-- * [USS George Washington](https://en.wikipedia.org/wiki/USS_George_Washington_(CVN-73)) (CVN-73) [Super Carrier Module] -- * [USS George Washington](https://en.wikipedia.org/wiki/USS_George_Washington_(CVN-73)) (CVN-73) [Super Carrier Module]
-- * [USS Harry S. Truman](https://en.wikipedia.org/wiki/USS_Harry_S._Truman) (CVN-75) [Super Carrier Module] -- * [USS Harry S. Truman](https://en.wikipedia.org/wiki/USS_Harry_S._Truman) (CVN-75) [Super Carrier Module]
-- * [USS Forrestal](https://en.wikipedia.org/wiki/USS_Forrestal_(CV-59)) (CV-59) [Heatblur Carrier Module] -- * [USS Forrestal](https://en.wikipedia.org/wiki/USS_Forrestal_(CV-59)) (CV-59) [Heatblur Carrier Module]
-- * [HMS Hermes](https://en.wikipedia.org/wiki/HMS_Hermes_(R12)) (R12) [**WIP**]
-- * [HMS Invincible](https://en.wikipedia.org/wiki/HMS_Invincible_(R05) (R05) [**WIP**]
-- * [USS Tarawa](https://en.wikipedia.org/wiki/USS_Tarawa_(LHA-1)) (LHA-1) [**WIP**] -- * [USS Tarawa](https://en.wikipedia.org/wiki/USS_Tarawa_(LHA-1)) (LHA-1) [**WIP**]
-- * [USS America](https://en.wikipedia.org/wiki/USS_America_(LHA-6)) (LHA-6) [**WIP**] -- * [USS America](https://en.wikipedia.org/wiki/USS_America_(LHA-6)) (LHA-6) [**WIP**]
-- * [Juan Carlos I](https://en.wikipedia.org/wiki/Spanish_amphibious_assault_ship_Juan_Carlos_I) (L61) [**WIP**] -- * [Juan Carlos I](https://en.wikipedia.org/wiki/Spanish_amphibious_assault_ship_Juan_Carlos_I) (L61) [**WIP**]
-- * [HMAS Canberra](https://en.wikipedia.org/wiki/HMAS_Canberra_(L02)) (L02) [**WIP**]
-- --
-- **Supported Aircraft:** -- **Supported Aircraft:**
-- --
@@ -51,9 +54,9 @@
-- --
-- At the moment, optimized parameters are available for the F/A-18C Hornet (Lot 20) and A-4E community mod as aircraft and the USS John C. Stennis as carrier. -- At the moment, optimized parameters are available for the F/A-18C Hornet (Lot 20) and A-4E community mod as aircraft and the USS John C. Stennis as carrier.
-- --
-- The AV-8B Harrier, the USS Tarawa, USS America, HMAS Canberra and Juan Carlos I are WIP. The AV-8B harrier and the LHA's and LHD can only be used together, i.e. these ships are the only carriers the harrier is supposed to land on and -- The AV-8B Harrier, HMS Hermes, HMS Invincible, the USS Tarawa, USS America, HMAS Canberra, and Juan Carlos I are WIP. The AV-8B harrier and the LHA's and LHD can only be used together, i.e. these ships are the only carriers the harrier is supposed to land on and
-- no other fixed wing aircraft (human or AI controlled) are supposed to land on these ships. Currently only Case I is supported. Case II/III take slightly different steps from the CVN carrier. -- no other fixed wing aircraft (human or AI controlled) are supposed to land on these ships. Currently only Case I is supported. Case II/III take slightly different steps from the CVN carrier.
-- However, the two Case II/III pattern are very similar so this is not a big drawback. -- However, if no offset is used for the holding radial this provides a very close representation of the V/STOL Case III, allowing for an approach to over the deck and a vertical landing.
-- --
-- Heatblur's mighty F-14B Tomcat has been added (March 13th 2019) as well. Same goes for the A version. -- Heatblur's mighty F-14B Tomcat has been added (March 13th 2019) as well. Same goes for the A version.
-- --
@@ -113,10 +116,11 @@
-- * [Harrier Ship Landing Mission with Auto LSO!](https://www.youtube.com/watch?v=lqmVvpunk2c) -- * [Harrier Ship Landing Mission with Auto LSO!](https://www.youtube.com/watch?v=lqmVvpunk2c)
-- * [Updated Airboss V/STOL Features USS Tarawa](https://youtu.be/K7I4pU6j718) -- * [Updated Airboss V/STOL Features USS Tarawa](https://youtu.be/K7I4pU6j718)
-- * [Harrier Practice pattern USS America](https://youtu.be/99NigITYmcI) -- * [Harrier Practice pattern USS America](https://youtu.be/99NigITYmcI)
-- * [Harrier CASE III TACAN Approach USS Tarawa](https://www.youtube.com/watch?v=bTgJXZ9Mhdc&t=1s)
-- --
-- === -- ===
-- --
-- ### Author: **funkyfranky** -- ### Author: **funkyfranky** LHA and LHD V/STOL additions by **Pene**
-- ### Special Thanks To **Bankler** -- ### Special Thanks To **Bankler**
-- For his great [Recovery Trainer](https://forums.eagle.ru/showthread.php?t=221412) mission and script! -- For his great [Recovery Trainer](https://forums.eagle.ru/showthread.php?t=221412) mission and script!
-- His work was the initial inspiration for this class. Also note that this implementation uses some routines for determining the player position in Case I recoveries he developed. -- His work was the initial inspiration for this class. Also note that this implementation uses some routines for determining the player position in Case I recoveries he developed.
@@ -1260,7 +1264,7 @@ AIRBOSS = {
--- Aircraft types capable of landing on carrier (human+AI). --- Aircraft types capable of landing on carrier (human+AI).
-- @type AIRBOSS.AircraftCarrier -- @type AIRBOSS.AircraftCarrier
-- @field #string AV8B AV-8B Night Harrier. Works only with the USS Tarawa, USS America and Juan Carlos I. -- @field #string AV8B AV-8B Night Harrier. Works only with the HMS Hermes, HMS Invincible, USS Tarawa, USS America, and Juan Carlos I.
-- @field #string A4EC A-4E Community mod. -- @field #string A4EC A-4E Community mod.
-- @field #string HORNET F/A-18C Lot 20 Hornet by Eagle Dynamics. -- @field #string HORNET F/A-18C Lot 20 Hornet by Eagle Dynamics.
-- @field #string F14A F-14A by Heatblur. -- @field #string F14A F-14A by Heatblur.
@@ -1296,6 +1300,8 @@ AIRBOSS.AircraftCarrier={
-- @field #string TRUMAN USS Harry S. Truman (CVN-75) [Super Carrier Module] -- @field #string TRUMAN USS Harry S. Truman (CVN-75) [Super Carrier Module]
-- @field #string FORRESTAL USS Forrestal (CV-59) [Heatblur Carrier Module] -- @field #string FORRESTAL USS Forrestal (CV-59) [Heatblur Carrier Module]
-- @field #string VINSON USS Carl Vinson (CVN-70) [Obsolete] -- @field #string VINSON USS Carl Vinson (CVN-70) [Obsolete]
-- @field #string HERMES HMS Hermes (R12) [V/STOL Carrier]
-- @field #string INVINCIBLE HMS Invincible (R05) [V/STOL Carrier]
-- @field #string TARAWA USS Tarawa (LHA-1) [V/STOL Carrier] -- @field #string TARAWA USS Tarawa (LHA-1) [V/STOL Carrier]
-- @field #string AMERICA USS America (LHA-6) [V/STOL Carrier] -- @field #string AMERICA USS America (LHA-6) [V/STOL Carrier]
-- @field #string JCARLOS Juan Carlos I (L61) [V/STOL Carrier] -- @field #string JCARLOS Juan Carlos I (L61) [V/STOL Carrier]
@@ -1309,6 +1315,8 @@ AIRBOSS.CarrierType = {
STENNIS = "Stennis", STENNIS = "Stennis",
FORRESTAL = "Forrestal", FORRESTAL = "Forrestal",
VINSON = "VINSON", VINSON = "VINSON",
HERMES = "HERMES81",
INVINCIBLE = "hms_invincible",
TARAWA = "LHA_Tarawa", TARAWA = "LHA_Tarawa",
AMERICA = "USS America LHA-6", AMERICA = "USS America LHA-6",
JCARLOS = "L61", JCARLOS = "L61",
@@ -1980,6 +1988,12 @@ function AIRBOSS:New( carriername, alias )
elseif self.carriertype == AIRBOSS.CarrierType.VINSON then elseif self.carriertype == AIRBOSS.CarrierType.VINSON then
-- TODO: Carl Vinson parameters. -- TODO: Carl Vinson parameters.
self:_InitStennis() self:_InitStennis()
elseif self.carriertype == AIRBOSS.CarrierType.HERMES then
-- Hermes parameters.
self:_InitHermes()
elseif self.carriertype == AIRBOSS.CarrierType.INVINCIBLE then
-- Invincible parameters.
self:_InitInvincible()
elseif self.carriertype == AIRBOSS.CarrierType.TARAWA then elseif self.carriertype == AIRBOSS.CarrierType.TARAWA then
-- Tarawa parameters. -- Tarawa parameters.
self:_InitTarawa() self:_InitTarawa()
@@ -2082,7 +2096,7 @@ function AIRBOSS:New( carriername, alias )
-- cL:FlareYellow() -- cL:FlareYellow()
-- Carrier specific. -- Carrier specific.
if self.carrier:GetTypeName() ~= AIRBOSS.CarrierType.TARAWA or self.carrier:GetTypeName() ~= AIRBOSS.CarrierType.AMERICA or self.carrier:GetTypeName() ~= AIRBOSS.CarrierType.JCARLOS or self.carrier:GetTypeName() ~= AIRBOSS.CarrierType.CANBERRA then if self.carrier:GetTypeName() ~= AIRBOSS.CarrierType.INVINCIBLE or self.carrier:GetTypeName() ~= AIRBOSS.CarrierType.HERMES or self.carrier:GetTypeName() ~= AIRBOSS.CarrierType.TARAWA or self.carrier:GetTypeName() ~= AIRBOSS.CarrierType.AMERICA or self.carrier:GetTypeName() ~= AIRBOSS.CarrierType.JCARLOS or self.carrier:GetTypeName() ~= AIRBOSS.CarrierType.CANBERRA then
-- Flare wires. -- Flare wires.
local w1 = stern:Translate( self.carrierparam.wire1, FB, true ) local w1 = stern:Translate( self.carrierparam.wire1, FB, true )
@@ -2811,13 +2825,29 @@ end
-- @param #number Low -- @param #number Low
-- @param #number LOW -- @param #number LOW
-- @return #AIRBOSS self -- @return #AIRBOSS self
function AIRBOSS:SetGlideslopeErrorThresholds( _max, _min, High, HIGH, Low, LOW )
self.gle._max = _max or 0.4 function AIRBOSS:SetGlideslopeErrorThresholds(_max,_min, High, HIGH, Low, LOW)
self.gle.High = High or 0.8
self.gle.HIGH = HIGH or 1.5 --Check if V/STOL Carrier
self.gle._min = _min or -0.3 if self.carriertype == AIRBOSS.CarrierType.INVINCIBLE or self.carriertype == AIRBOSS.CarrierType.HERMES or self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then
self.gle.Low = Low or -0.6
self.gle.LOW = LOW or -0.9 -- allow a larger GSE for V/STOL operations --Pene Testing
self.gle._max=_max or 0.7
self.gle.High=High or 1.4
self.gle.HIGH=HIGH or 1.9
self.gle._min=_min or -0.5
self.gle.Low=Low or -1.2
self.gle.LOW=LOW or -1.5
-- CVN values
else
self.gle._max=_max or 0.4
self.gle.High=High or 0.8
self.gle.HIGH=HIGH or 1.5
self.gle._min=_min or -0.3
self.gle.Low=Low or -0.6
self.gle.LOW=LOW or -0.9
end
return self return self
end end
@@ -2832,15 +2862,33 @@ end
-- @param #number RightMed -- @param #number RightMed
-- @param #number RIGHT -- @param #number RIGHT
-- @return #AIRBOSS self -- @return #AIRBOSS self
function AIRBOSS:SetLineupErrorThresholds( _max, _min, Left, LeftMed, LEFT, Right, RightMed, RIGHT )
self.lue._max = _max or 0.5 function AIRBOSS:SetLineupErrorThresholds(_max,_min, Left, LeftMed, LEFT, Right, RightMed, RIGHT)
self.lue._min = _min or -0.5
self.lue.Left = Left or -1.0 --Check if V/STOL Carrier -- Pene testing
self.lue.LeftMed = LeftMed or -2.0 if self.carriertype == AIRBOSS.CarrierType.INVINCIBLE or self.carriertype == AIRBOSS.CarrierType.HERMES or self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then
self.lue.LEFT = LEFT or -3.0
self.lue.Right = Right or 1.0 -- V/STOL Values -- allow a larger LUE for V/STOL operations
self.lue.RightMed = RightMed or 2.0 self.lue._max=_max or 1.8
self.lue.RIGHT = RIGHT or 3.0 self.lue._min=_min or -1.8
self.lue.Left=Left or -2.8
self.lue.LeftMed=LeftMed or -3.8
self.lue.LEFT=LEFT or -4.5
self.lue.Right=Right or 2.8
self.lue.RightMed=RightMed or 3.8
self.lue.RIGHT=RIGHT or 4.5
-- CVN Values
else
self.lue._max=_max or 0.5
self.lue._min=_min or -0.5
self.lue.Left=Left or -1.0
self.lue.LeftMed=LeftMed or -2.0
self.lue.LEFT=LEFT or -3.0
self.lue.Right=Right or 1.0
self.lue.RightMed=RightMed or 2.0
self.lue.RIGHT=RIGHT or 3.0
end
return self return self
end end
@@ -4384,6 +4432,86 @@ function AIRBOSS:_InitForrestal()
end end
--- Init parameters for R12 HMS Hermes carrier.
-- @param #AIRBOSS self
function AIRBOSS:_InitHermes()
-- Init Stennis as default.
self:_InitStennis()
-- Carrier Parameters.
self.carrierparam.sterndist = -105
self.carrierparam.deckheight = 12 -- From model viewer WL0.
-- Total size of the carrier (approx as rectangle).
self.carrierparam.totlength = 228.19
self.carrierparam.totwidthport = 20.5
self.carrierparam.totwidthstarboard = 24.5
-- Landing runway.
self.carrierparam.rwyangle = 0
self.carrierparam.rwylength = 215
self.carrierparam.rwywidth = 13
-- Wires.
self.carrierparam.wire1 = nil
self.carrierparam.wire2 = nil
self.carrierparam.wire3 = nil
self.carrierparam.wire4 = nil
-- Late break.
self.BreakLate.name = "Late Break"
self.BreakLate.Xmin = -UTILS.NMToMeters( 1 ) -- Not more than 1 NM behind the boat. Last check was at 0.
self.BreakLate.Xmax = UTILS.NMToMeters( 5 ) -- Not more than 5 NM in front of the boat. Enough for late breaks?
self.BreakLate.Zmin = -UTILS.NMToMeters( 1.6 ) -- Not more than 1.6 NM port.
self.BreakLate.Zmax = UTILS.NMToMeters( 1 ) -- Not more than 1 NM starboard.
self.BreakLate.LimitXmin = 0 -- Check and next step 0.8 NM port and in front of boat.
self.BreakLate.LimitXmax = nil
self.BreakLate.LimitZmin = -UTILS.NMToMeters( 0.5 ) -- 926 m port, closer than the stennis as abeam is 0.8-1.0 rather than 1.2
self.BreakLate.LimitZmax = nil
end
--- Init parameters for R05 HMS Invincible carrier.
-- @param #AIRBOSS self
function AIRBOSS:_InitInvincible()
-- Init Stennis as default.
self:_InitStennis()
-- Carrier Parameters.
self.carrierparam.sterndist = -105
self.carrierparam.deckheight = 12 -- From model viewer WL0.
-- Total size of the carrier (approx as rectangle).
self.carrierparam.totlength = 228.19
self.carrierparam.totwidthport = 20.5
self.carrierparam.totwidthstarboard = 24.5
-- Landing runway.
self.carrierparam.rwyangle = 0
self.carrierparam.rwylength = 215
self.carrierparam.rwywidth = 13
-- Wires.
self.carrierparam.wire1 = nil
self.carrierparam.wire2 = nil
self.carrierparam.wire3 = nil
self.carrierparam.wire4 = nil
-- Late break.
self.BreakLate.name = "Late Break"
self.BreakLate.Xmin = -UTILS.NMToMeters( 1 ) -- Not more than 1 NM behind the boat. Last check was at 0.
self.BreakLate.Xmax = UTILS.NMToMeters( 5 ) -- Not more than 5 NM in front of the boat. Enough for late breaks?
self.BreakLate.Zmin = -UTILS.NMToMeters( 1.6 ) -- Not more than 1.6 NM port.
self.BreakLate.Zmax = UTILS.NMToMeters( 1 ) -- Not more than 1 NM starboard.
self.BreakLate.LimitXmin = 0 -- Check and next step 0.8 NM port and in front of boat.
self.BreakLate.LimitXmax = nil
self.BreakLate.LimitZmin = -UTILS.NMToMeters( 0.5 ) -- 926 m port, closer than the stennis as abeam is 0.8-1.0 rather than 1.2
self.BreakLate.LimitZmax = nil
end
--- Init parameters for LHA-1 Tarawa carrier. --- Init parameters for LHA-1 Tarawa carrier.
-- @param #AIRBOSS self -- @param #AIRBOSS self
function AIRBOSS:_InitTarawa() function AIRBOSS:_InitTarawa()
@@ -5007,14 +5135,16 @@ function AIRBOSS:_GetAircraftAoA( playerData )
aoa.Fast = 8.25 -- =17.5/2 aoa.Fast = 8.25 -- =17.5/2
aoa.FAST = 8.00 -- =16.5/2 aoa.FAST = 8.00 -- =16.5/2
elseif harrier then elseif harrier then
-- AV-8B Harrier parameters. Tuning done on the Fast AoA to allow for abeam and ninety at Nozzles 60 - 73.
aoa.SLOW = 14.0 -- AV-8B Harrier parameters. Tuning done on the Fast AoA to allow for abeam and ninety at Nozzles 55. Pene testing
aoa.Slow = 13.0 aoa.SLOW = 16.0
aoa.OnSpeedMax = 12.0 aoa.Slow = 13.5
aoa.OnSpeed = 11.0 aoa.OnSpeedMax = 12.5
aoa.OnSpeedMin = 10.0 aoa.OnSpeed = 10.0
aoa.Fast = 8.0 aoa.OnSpeedMin = 9.5
aoa.FAST = 7.5 aoa.Fast = 8.0
aoa.FAST = 7.5
end end
return aoa return aoa
@@ -5273,8 +5403,8 @@ function AIRBOSS:_GetAircraftParameters( playerData, step )
elseif skyhawk then elseif skyhawk then
alt = UTILS.FeetToMeters( 300 ) -- ? alt = UTILS.FeetToMeters( 300 ) -- ?
elseif harrier then elseif harrier then
-- 300-325 ft alt=UTILS.FeetToMeters(312)-- 300-325 ft
alt = UTILS.FeetToMeters( 300 ) -- Need to verify
end end
aoa = aoaac.OnSpeed aoa = aoaac.OnSpeed
@@ -6058,7 +6188,7 @@ function AIRBOSS:_RefuelAI( flight )
-- Guide AI to divert field -- -- Guide AI to divert field --
------------------------------ ------------------------------
-- Closest Airfield of the coaliton. -- Closest Airfield of the coalition.
local divertfield = self:GetCoordinate():GetClosestAirbase( Airbase.Category.AIRDROME, self:GetCoalition() ) local divertfield = self:GetCoordinate():GetClosestAirbase( Airbase.Category.AIRDROME, self:GetCoalition() )
-- Handle case where there is no divert field of the own coalition and try neutral instead. -- Handle case where there is no divert field of the own coalition and try neutral instead.
@@ -6209,7 +6339,7 @@ function AIRBOSS:_GetMarshalAltitude( stack, case )
p2 = Carrier:Translate( UTILS.NMToMeters( 1.5 ), hdg ) p2 = Carrier:Translate( UTILS.NMToMeters( 1.5 ), hdg )
-- Tarawa,LHA,LHD Delta patterns. -- Tarawa,LHA,LHD Delta patterns.
if self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then if self.carriertype == AIRBOSS.CarrierType.INVINCIBLE or self.carriertype == AIRBOSS.CarrierType.HERMES or self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then
-- Pattern is directly overhead the carrier. -- Pattern is directly overhead the carrier.
p1 = Carrier:Translate( UTILS.NMToMeters( 1.0 ), hdg + 90 ) p1 = Carrier:Translate( UTILS.NMToMeters( 1.0 ), hdg + 90 )
@@ -8048,7 +8178,7 @@ function AIRBOSS:OnEventLand( EventData )
self:T( self.lid .. text ) self:T( self.lid .. text )
-- Check carrier type. -- Check carrier type.
if self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then if self.carriertype == AIRBOSS.CarrierType.INVINCIBLE or self.carriertype == AIRBOSS.CarrierType.HERMES or self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then
-- Power "Idle". -- Power "Idle".
self:RadioTransmission( self.LSORadio, self.LSOCall.IDLE, false, 1, nil, true ) self:RadioTransmission( self.LSORadio, self.LSOCall.IDLE, false, 1, nil, true )
@@ -8083,7 +8213,7 @@ function AIRBOSS:OnEventLand( EventData )
-- AI unit landed -- -- AI unit landed --
-------------------- --------------------
if self.carriertype ~= AIRBOSS.CarrierType.TARAWA or self.carriertype ~= AIRBOSS.CarrierType.AMERICA or self.carriertype ~= AIRBOSS.CarrierType.JCARLOS or self.carriertype ~= AIRBOSS.CarrierType.CANBERRA then if self.carriertype ~= AIRBOSS.CarrierType.INVINCIBLE or self.carriertype ~= AIRBOSS.CarrierType.HERMES or self.carriertype ~= AIRBOSS.CarrierType.TARAWA or self.carriertype ~= AIRBOSS.CarrierType.AMERICA or self.carriertype ~= AIRBOSS.CarrierType.JCARLOS or self.carriertype ~= AIRBOSS.CarrierType.CANBERRA then
-- Coordinate at landing event -- Coordinate at landing event
local coord = EventData.IniUnit:GetCoordinate() local coord = EventData.IniUnit:GetCoordinate()
@@ -9121,7 +9251,7 @@ function AIRBOSS:_CheckForLongDownwind( playerData )
local limit = UTILS.NMToMeters( -1.6 ) local limit = UTILS.NMToMeters( -1.6 )
-- For the tarawa, other LHA and LHD we give a bit more space. -- For the tarawa, other LHA and LHD we give a bit more space.
if self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then if self.carriertype == AIRBOSS.CarrierType.INVINCIBLE or self.carriertype == AIRBOSS.CarrierType.HERMES or self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then
limit = UTILS.NMToMeters( -2.0 ) limit = UTILS.NMToMeters( -2.0 )
end end
@@ -9208,7 +9338,7 @@ function AIRBOSS:_Ninety( playerData )
self:_PlayerHint( playerData ) self:_PlayerHint( playerData )
-- Next step: wake. -- Next step: wake.
if self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then if self.carriertype == AIRBOSS.CarrierType.INVINCIBLE or self.carriertype == AIRBOSS.CarrierType.HERMES or self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then
-- Harrier has no wake stop. It stays port of the boat. -- Harrier has no wake stop. It stays port of the boat.
self:_SetPlayerStep( playerData, AIRBOSS.PatternStep.FINAL ) self:_SetPlayerStep( playerData, AIRBOSS.PatternStep.FINAL )
else else
@@ -9464,8 +9594,9 @@ function AIRBOSS:_Groove( playerData )
-- Speed difference. -- Speed difference.
local dv = math.abs( vplayer - vcarrier ) local dv = math.abs( vplayer - vcarrier )
-- Stable when speed difference < 20 km/h.
local stable = dv < 20 -- Stable when speed difference < 30 km/h.(16 Kts)Pene Testing
local stable=dv<30
-- Check if player is inside the zone. -- Check if player is inside the zone.
if playerData.unit:IsInZone( ZoneALS ) and stable then if playerData.unit:IsInZone( ZoneALS ) and stable then
@@ -9497,8 +9628,8 @@ function AIRBOSS:_Groove( playerData )
-- Speed difference. -- Speed difference.
local dv = math.abs( vplayer - vcarrier ) local dv = math.abs( vplayer - vcarrier )
-- Stable when v<10 km/h. -- Stable when v<15 km/h.
local stable = dv < 10 local stable=dv<15
-- Radio Transmission "Stabilized" once the aircraft has been cleared to cross and is over the Landing Spot and stable. -- Radio Transmission "Stabilized" once the aircraft has been cleared to cross and is over the Landing Spot and stable.
if playerData.unit:IsInZone( ZoneLS ) and stable and playerData.stable == true then if playerData.unit:IsInZone( ZoneLS ) and stable and playerData.stable == true then
@@ -9537,25 +9668,26 @@ function AIRBOSS:_Groove( playerData )
-- Nothing else necessary. -- Nothing else necessary.
return return
end end
end
-- Long V/STOL groove time Wave Off over 75 seconds to IC - TOPGUN level Only. --pene testing (WIP) end
-- if rho>=RAR and rho<=RIC and not playerData.waveoff and playerData.difficulty==AIRBOSS.Difficulty.HARD and playerData.actype== AIRBOSS.AircraftCarrier.AV8B then -- Long V/STOL groove time Wave Off over 75 seconds to IC - TOPGUN level Only. --pene testing (WIP)--- Need to think more about this.
-- Get groove time
-- local vSlow=groovedata.time
-- If too slow wave off.
-- if vSlow >75 then
-- LSO Wave off! --if rho>=RAR and rho<=RIC and not playerData.waveoff and playerData.difficulty==AIRBOSS.Difficulty.HARD and playerData.actype== AIRBOSS.AircraftCarrier.AV8B then
-- self:RadioTransmission(self.LSORadio, self.LSOCall.WAVEOFF, nil, nil, nil, true) -- Get groove time
-- playerData.Tlso=timer.getTime() --local vSlow=groovedata.time
-- If too slow wave off.
--if vSlow >75 then
-- Player was waved Off -- LSO Wave off!
-- playerData.waveoff=true --self:RadioTransmission(self.LSORadio, self.LSOCall.WAVEOFF, nil, nil, nil, true)
-- return --playerData.Tlso=timer.getTime()
-- end
-- end -- Player was waved Off
--playerData.waveoff=true
--return
--end
--end
-- Groovedata step. -- Groovedata step.
groovedata.Step = playerData.step groovedata.Step = playerData.step
@@ -9726,8 +9858,8 @@ function AIRBOSS:_CheckWaveOff( glideslopeError, lineupError, AoA, playerData )
-- For the harrier, we allow a bit more room. -- For the harrier, we allow a bit more room.
if playerData.actype == AIRBOSS.AircraftCarrier.AV8B then if playerData.actype == AIRBOSS.AircraftCarrier.AV8B then
glMax = 2.6 glMax = 2.6
glMin = -2.0 glMin = -2.2 -- Testing, @Engines may be just dragging it in on Hermes, or the carrier parameters need adjusting.
luAbs = 4.1 -- Testing Pene (WIP) needs feedback to tighten up tolerences. luAbs = 4.1 -- Testing Pene.
end end
@@ -9893,17 +10025,23 @@ function AIRBOSS:_GetSternCoord()
local hdg = self.carrier:GetHeading() local hdg = self.carrier:GetHeading()
-- Final bearing (true). -- Final bearing (true).
local FB = self:GetFinalBearing() local FB=self:GetFinalBearing()
local case=self.case
-- Stern coordinate (sterndist<0). Also translate 10 meters starboard wrt Final bearing. -- Stern coordinate (sterndist<0). Also translate 10 meters starboard wrt Final bearing.
self.sterncoord:UpdateFromCoordinate( self:GetCoordinate() ) self.sterncoord:UpdateFromCoordinate( self:GetCoordinate() )
-- local stern=self:GetCoordinate() -- local stern=self:GetCoordinate()
-- Stern coordinate (sterndist<0). -- Stern coordinate (sterndist<0). --Pene testing Case III
if self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then if self.carriertype==AIRBOSS.CarrierType.INVINCIBLE or self.carriertype==AIRBOSS.CarrierType.HERMES or self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS or self.carriertype==AIRBOSS.CarrierType.CANBERRA then
-- Tarawa: Translate 8 meters port. if case==3 then
self.sterncoord:Translate( self.carrierparam.sterndist, hdg, true, true ):Translate( 8, FB - 90, true, true ) -- CASE III V/STOL translation Due over deck approach if needed.
elseif self.carriertype == AIRBOSS.CarrierType.STENNIS then self.sterncoord:Translate(self.carrierparam.sterndist, hdg, true, true):Translate(8, FB-90, true, true)
elseif case==2 or case==1 then
-- V/Stol: Translate 8 meters port.
self.sterncoord:Translate(self.carrierparam.sterndist, hdg, true, true):Translate(8, FB-90, true, true)
end
elseif self.carriertype==AIRBOSS.CarrierType.STENNIS then
-- Stennis: translate 7 meters starboard wrt Final bearing. -- Stennis: translate 7 meters starboard wrt Final bearing.
self.sterncoord:Translate( self.carrierparam.sterndist, hdg, true, true ):Translate( 7, FB + 90, true, true ) self.sterncoord:Translate( self.carrierparam.sterndist, hdg, true, true ):Translate( 7, FB + 90, true, true )
elseif self.carriertype == AIRBOSS.CarrierType.FORRESTAL then elseif self.carriertype == AIRBOSS.CarrierType.FORRESTAL then
@@ -10536,7 +10674,8 @@ function AIRBOSS:_GetZoneRunwayBox()
return self.zoneRunwaybox return self.zoneRunwaybox
end end
--- Get zone of primary abeam landing position of USS Tarawa, USS America and Juan Carlos. Box length 50 meters and width 30 meters. --- Get zone of primary abeam landing position of HMS Hermes, HMS Invincible, USS Tarawa, USS America and Juan Carlos. Box length 50 meters and width 30 meters.
--- Allow for Clear to land call from LSO approaching abeam the landing spot if stable as per NATOPS 00-80T --- Allow for Clear to land call from LSO approaching abeam the landing spot if stable as per NATOPS 00-80T
-- @param #AIRBOSS self -- @param #AIRBOSS self
-- @return Core.Zone#ZONE_POLYGON Zone surrounding landing runway. -- @return Core.Zone#ZONE_POLYGON Zone surrounding landing runway.
@@ -10548,8 +10687,8 @@ function AIRBOSS:_GetZoneAbeamLandingSpot()
-- Current carrier heading. -- Current carrier heading.
local FB = self:GetFinalBearing( false ) local FB = self:GetFinalBearing( false )
-- Coordinate array. -- Coordinate array. Pene Testing extended Abeam landing spot V/STOL.
local p = {} local p={}
-- Points. -- Points.
p[1] = S:Translate( 15, FB ):Translate( 15, FB + 90 ) -- Top-Right p[1] = S:Translate( 15, FB ):Translate( 15, FB + 90 ) -- Top-Right
@@ -10640,7 +10779,7 @@ function AIRBOSS:_GetZoneHolding( case, stack )
self.zoneHolding = ZONE_RADIUS:New( "CASE I Holding Zone", Post:GetVec2(), self.marshalradius ) self.zoneHolding = ZONE_RADIUS:New( "CASE I Holding Zone", Post:GetVec2(), self.marshalradius )
-- Delta pattern. -- Delta pattern.
if self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then if self.carriertype == AIRBOSS.CarrierType.INVINCIBLE or self.carriertype == AIRBOSS.CarrierType.HERMES or self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then
self.zoneHolding = ZONE_RADIUS:New( "CASE I Holding Zone", self.carrier:GetVec2(), UTILS.NMToMeters( 5 ) ) self.zoneHolding = ZONE_RADIUS:New( "CASE I Holding Zone", self.carrier:GetVec2(), UTILS.NMToMeters( 5 ) )
end end
@@ -10692,7 +10831,7 @@ function AIRBOSS:_GetZoneCommence( case, stack )
-- Three position -- Three position
local Three = self:GetCoordinate():Translate( D, hdg + 275 ) local Three = self:GetCoordinate():Translate( D, hdg + 275 )
if self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then if self.carriertype == AIRBOSS.CarrierType.INVINCIBLE or self.carriertype == AIRBOSS.CarrierType.HERMES or self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then
local Dx = UTILS.NMToMeters( 2.25 ) local Dx = UTILS.NMToMeters( 2.25 )
@@ -10983,7 +11122,7 @@ function AIRBOSS:_GetAltCarrier( unit )
return h return h
end end
--- Get optimal landing position of the aircraft. Usually between second and third wire. In case of Tarawa and America we take the abeam landing spot 120 ft abeam the 7.5 position, for the Juan Carlos I it is 120 ft and abeam the 5 position. --- Get optimal landing position of the aircraft. Usually between second and third wire. In case of Tarawa, Canberrra, Juan Carlos and America we take the abeam landing spot 120 ft above and 21 ft abeam the 7.5 position, for the Juan Carlos I, HMS Invincible, and HMS Hermes and Invincible it is 120 ft above and 21 ft abeam the 5 position. For CASE III it is 120ft directly above the landing spot.
-- @param #AIRBOSS self -- @param #AIRBOSS self
-- @return Core.Point#COORDINATE Optimal landing coordinate. -- @return Core.Point#COORDINATE Optimal landing coordinate.
function AIRBOSS:_GetOptLandingCoordinate() function AIRBOSS:_GetOptLandingCoordinate()
@@ -10993,44 +11132,27 @@ function AIRBOSS:_GetOptLandingCoordinate()
-- Stern coordinate. -- Stern coordinate.
-- local stern=self:_GetSternCoord() -- local stern=self:_GetSternCoord()
-- Final bearing. -- Final bearing.
local FB = self:GetFinalBearing( false )
if self.carriertype == AIRBOSS.CarrierType.TARAWA then local FB=self:GetFinalBearing(false)
local case=self.case
-- set Case III V/STOL abeam landing spot over deck -- Pene Testing
if self.carriertype==AIRBOSS.CarrierType.INVINCIBLE or self.carriertype==AIRBOSS.CarrierType.HERMES or self.carriertype==AIRBOSS.CarrierType.TARAWA or self.carriertype==AIRBOSS.CarrierType.AMERICA or self.carriertype==AIRBOSS.CarrierType.JCARLOS or self.carriertype==AIRBOSS.CarrierType.CANBERRA then
if case==3 then
self.landingcoord:UpdateFromCoordinate(self:_GetLandingSpotCoordinate())
-- Altitude 120ft -- is this corect for Case III?
self.landingcoord:SetAltitude(UTILS.FeetToMeters(120))
elseif case==2 or case==1 then
-- Landing 100 ft abeam, 120 ft alt. -- Landing 100 ft abeam, 120 ft alt.
self.landingcoord:UpdateFromCoordinate( self:_GetLandingSpotCoordinate() ):Translate( 35, FB - 90, true, true ) self.landingcoord:UpdateFromCoordinate(self:_GetLandingSpotCoordinate()):Translate(35, FB-90, true, true)
-- stern=self:_GetLandingSpotCoordinate():Translate(35, FB-90) --stern=self:_GetLandingSpotCoordinate():Translate(35, FB-90)
-- Alitude 120 ft. -- Atlitude 120 ft.
self.landingcoord:SetAltitude( UTILS.FeetToMeters( 120 ) ) self.landingcoord:SetAltitude(UTILS.FeetToMeters(120))
elseif self.carriertype == AIRBOSS.CarrierType.AMERICA then end
-- Landing 100 ft abeam, 120 ft alt. To allow adjustments to match different deck configurations.
self.landingcoord:UpdateFromCoordinate( self:_GetLandingSpotCoordinate() ):Translate( 35, FB - 90, true, true )
-- stern=self:_GetLandingSpotCoordinate():Translate(35, FB-90)
-- Alitude 120 ft.
self.landingcoord:SetAltitude( UTILS.FeetToMeters( 120 ) )
elseif self.carriertype == AIRBOSS.CarrierType.JCARLOS then
-- Landing 100 ft abeam, 120 ft alt.
self.landingcoord:UpdateFromCoordinate( self:_GetLandingSpotCoordinate() ):Translate( 35, FB - 90, true, true )
-- stern=self:_GetLandingSpotCoordinate():Translate(35, FB-90)
-- Alitude 120 ft.
self.landingcoord:SetAltitude( UTILS.FeetToMeters( 120 ) )
elseif self.carriertype == AIRBOSS.CarrierType.CANBERRA then
-- Landing 100 ft abeam, 120 ft alt.
self.landingcoord:UpdateFromCoordinate( self:_GetLandingSpotCoordinate() ):Translate( 35, FB - 90, true, true )
-- stern=self:_GetLandingSpotCoordinate():Translate(35, FB-90)
-- Alitude 120 ft.
self.landingcoord:SetAltitude( UTILS.FeetToMeters( 120 ) )
else else
@@ -11059,7 +11181,21 @@ function AIRBOSS:_GetLandingSpotCoordinate()
-- Stern coordinate. -- Stern coordinate.
-- local stern=self:_GetSternCoord() -- local stern=self:_GetSternCoord()
if self.carriertype == AIRBOSS.CarrierType.TARAWA then if self.carriertype==AIRBOSS.CarrierType.HERMES then
-- Landing 100 ft abeam, 100 alt.
local hdg = self:GetHeading()
-- Primary landing spot 5
self.landingspotcoord:Translate( 69, hdg, true, true ):SetAltitude( self.carrierparam.deckheight )
elseif self.carriertype == AIRBOSS.CarrierType.INVINCIBLE then
-- Using spot 3 as the default
local hdg = self:GetHeading()
self.landingspotcoord:Translate( 69, hdg, true, true ):SetAltitude( self.carrierparam.deckheight )
-- This location looks good.
elseif self.carriertype == AIRBOSS.CarrierType.TARAWA then
-- Landing 100 ft abeam, 120 alt. -- Landing 100 ft abeam, 120 alt.
local hdg = self:GetHeading() local hdg = self:GetHeading()
@@ -11127,7 +11263,7 @@ end
--- Get wind direction and speed at carrier position. --- Get wind direction and speed at carrier position.
-- @param #AIRBOSS self -- @param #AIRBOSS self
-- @param #number alt Altitude ASL in meters. Default 50 m. -- @param #number alt Altitude ASL in meters. Default 15 m.
-- @param #boolean magnetic Direction including magnetic declination. -- @param #boolean magnetic Direction including magnetic declination.
-- @param Core.Point#COORDINATE coord (Optional) Coordinate at which to get the wind. Default is current carrier position. -- @param Core.Point#COORDINATE coord (Optional) Coordinate at which to get the wind. Default is current carrier position.
-- @return #number Direction the wind is blowing **from** in degrees. -- @return #number Direction the wind is blowing **from** in degrees.
@@ -11137,8 +11273,8 @@ function AIRBOSS:GetWind( alt, magnetic, coord )
-- Current position of the carrier or input. -- Current position of the carrier or input.
local cv = coord or self:GetCoordinate() local cv = coord or self:GetCoordinate()
-- Wind direction and speed. By default at 50 meters ASL. -- Wind direction and speed. By default at 15 meters ASL.
local Wdir, Wspeed = cv:GetWind( alt or 50 ) local Wdir, Wspeed = cv:GetWind( alt or 15 )
-- Include magnetic declination. -- Include magnetic declination.
if magnetic then if magnetic then
@@ -11649,15 +11785,17 @@ function AIRBOSS:_LSOgrade( playerData )
local G = GXX .. " " .. GIM .. " " .. " " .. GIC .. " " .. GAR local G = GXX .. " " .. GIM .. " " .. " " .. GIC .. " " .. GAR
-- Count number of minor, normal and major deviations. -- Count number of minor, normal and major deviations.
local N = nXX + nIM + nIC + nAR local N=nXX+nIM+nIC+nAR
local nL = count( G, '_' ) / 2 local Nv=nXX+nIM
local nS = count( G, '%(' ) local nL=count(G, '_')/2
local nN = N - nS - nL local nS=count(G, '%(')
local nN=N-nS-nL
local nNv=Nv-nS-nL
-- Groove time 15-18.99 sec for a unicorn. Or 65-70 for V/STOL unicorn. -- Groove time 15-18.99 sec for a unicorn. Or 60-65 for V/STOL unicorn.
local Tgroove = playerData.Tgroove local Tgroove=playerData.Tgroove
local TgrooveUnicorn = Tgroove and (Tgroove >= 15.0 and Tgroove <= 18.99) or false local TgrooveUnicorn=Tgroove and (Tgroove>=15.0 and Tgroove<=18.99) or false
local TgrooveVstolUnicorn = Tgroove and (Tgroove >= 60.0 and Tgroove <= 65.0) and playerData.actype == AIRBOSS.AircraftCarrier.AV8B or false local TgrooveVstolUnicorn=Tgroove and (Tgroove>=60.0 and Tgroove<=65.0)and playerData.actype==AIRBOSS.AircraftCarrier.AV8B or false
local grade local grade
local points local points
@@ -11668,29 +11806,33 @@ function AIRBOSS:_LSOgrade( playerData )
G = "Unicorn" G = "Unicorn"
else else
-- Add AV-8B Harrier devation allowances due to lower groundspeed and 3x conventional groove time, this allows to maintain LSO tolerances while respecting the deviations are not unsafe. (WIP requires feedback) -- Add AV-8B Harrier devation allowances due to lower groundspeed and 3x conventional groove time, this allows to maintain LSO tolerances while respecting the deviations are not unsafe.--Pene testing
-- Large devaitions still result in a No Grade, A Unicorn still requires a clean pass with no deviation. -- Large devaitions still result in a No Grade, A Unicorn still requires a clean pass with no deviation.
if nL > 3 and playerData.actype == AIRBOSS.AircraftCarrier.AV8B then if nL > 1 and playerData.actype==AIRBOSS.AircraftCarrier.AV8B then
-- Larger deviations ==> "No grade" 2.0 points. -- Larger deviations ==> "No grade" 2.0 points.
grade = "--" grade="--"
points = 2.0 points=2.0
elseif nN > 2 and playerData.actype == AIRBOSS.AircraftCarrier.AV8B then elseif nNv >= 1 and playerData.actype==AIRBOSS.AircraftCarrier.AV8B then
-- Only average deviations ==> "Fair Pass" Pass with average deviations and corrections. -- Only average deviations ==> "Fair Pass" Pass with average deviations and corrections.
grade = "(OK)" grade="(OK)"
points = 3.0 points=3.0
elseif nL > 0 then elseif nNv < 1 and playerData.actype==AIRBOSS.AircraftCarrier.AV8B then
-- Only minor average deviations ==> "OK" Pass with minor deviations and corrections. (test nNv<=1 and)
grade="OK"
points=4.0
elseif nL > 0 then
-- Larger deviations ==> "No grade" 2.0 points. -- Larger deviations ==> "No grade" 2.0 points.
grade = "--" grade="--"
points = 2.0 points=2.0
elseif nN > 0 then elseif nN> 0 then
-- No larger but average deviations ==> "Fair Pass" Pass with average deviations and corrections. -- No larger but average deviations ==> "Fair Pass" Pass with average deviations and corrections.
grade = "(OK)" grade="(OK)"
points = 3.0 points=3.0
else else
-- Only minor corrections -- Only minor corrections
grade = "OK" grade="OK"
points = 4.0 points=4.0
end end
end end
@@ -11994,7 +12136,7 @@ function AIRBOSS:_GS( step, n )
if n == -1 then if n == -1 then
gp = AIRBOSS.GroovePos.IC gp = AIRBOSS.GroovePos.IC
elseif n == 1 then elseif n == 1 then
if self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then if self.carriertype == AIRBOSS.CarrierType.INVINCIBLE or self.carriertype == AIRBOSS.CarrierType.HERMES or self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then
gp = AIRBOSS.GroovePos.AL gp = AIRBOSS.GroovePos.AL
else else
gp = AIRBOSS.GroovePos.IW gp = AIRBOSS.GroovePos.IW
@@ -13873,7 +14015,7 @@ function AIRBOSS:_IsCarrierAircraft( unit )
-- Special case for Harrier which can only land on Tarawa, LHA and LHD. -- Special case for Harrier which can only land on Tarawa, LHA and LHD.
if aircrafttype == AIRBOSS.AircraftCarrier.AV8B then if aircrafttype == AIRBOSS.AircraftCarrier.AV8B then
if self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then if self.carriertype == AIRBOSS.CarrierType.INVINCIBLE or self.carriertype == AIRBOSS.CarrierType.HERMES or self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then
return true return true
else else
return false return false
@@ -13881,7 +14023,7 @@ function AIRBOSS:_IsCarrierAircraft( unit )
end end
-- Also only Harriers can land on the Tarawa, LHA and LHD. -- Also only Harriers can land on the Tarawa, LHA and LHD.
if self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then if self.carriertype == AIRBOSS.CarrierType.INVINCIBLE or self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then
if aircrafttype ~= AIRBOSS.AircraftCarrier.AV8B then if aircrafttype ~= AIRBOSS.AircraftCarrier.AV8B then
return false return false
end end
@@ -17238,7 +17380,7 @@ function AIRBOSS:_MarkCaseZones( _unitName, flare )
end end
-- Tarawa, LHA and LHD landing spots. -- Tarawa, LHA and LHD landing spots.
if self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then if self.carriertype == AIRBOSS.CarrierType.INVINCIBLE or self.carriertype == AIRBOSS.CarrierType.HERMES or self.carriertype == AIRBOSS.CarrierType.TARAWA or self.carriertype == AIRBOSS.CarrierType.AMERICA or self.carriertype == AIRBOSS.CarrierType.JCARLOS or self.carriertype == AIRBOSS.CarrierType.CANBERRA then
text = text .. "\n* abeam landing stop with RED flares" text = text .. "\n* abeam landing stop with RED flares"
-- Abeam landing spot zone. -- Abeam landing spot zone.
local ALSPT = self:_GetZoneAbeamLandingSpot() local ALSPT = self:_GetZoneAbeamLandingSpot()
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+455 -56
View File
@@ -45,14 +45,13 @@
-- @field Core.Point#COORDINATE coordinate Coordinate from where the transmission is send. -- @field Core.Point#COORDINATE coordinate Coordinate from where the transmission is send.
-- @field #string path Path to the SRS exe. This includes the final slash "/". -- @field #string path Path to the SRS exe. This includes the final slash "/".
-- @field #string google Full path google credentials JSON file, e.g. "C:\Users\username\Downloads\service-account-file.json". -- @field #string google Full path google credentials JSON file, e.g. "C:\Users\username\Downloads\service-account-file.json".
-- @field #string Label Label showing up on the SRS radio overlay. Default is "ROBOT". No spaces allowed.
-- @extends Core.Base#BASE -- @extends Core.Base#BASE
--- *It is a very sad thing that nowadays there is so little useless information.* - Oscar Wilde --- *It is a very sad thing that nowadays there is so little useless information.* - Oscar Wilde
-- --
-- === -- ===
-- --
-- ![Banner Image](..\Presentations\ATIS\ATIS_Main.png)
--
-- # The MSRS Concept -- # The MSRS Concept
-- --
-- This class allows to broadcast sound files or text via Simple Radio Standalone (SRS). -- This class allows to broadcast sound files or text via Simple Radio Standalone (SRS).
@@ -95,6 +94,14 @@
-- For more information on setting up a cloud account, visit: https://cloud.google.com/text-to-speech -- For more information on setting up a cloud account, visit: https://cloud.google.com/text-to-speech
-- Google's supported SSML reference: https://cloud.google.com/text-to-speech/docs/ssml -- Google's supported SSML reference: https://cloud.google.com/text-to-speech/docs/ssml
-- --
-- **NOTE on using GOOGLE TTS with SRS:** You need to have the C# library installed in your SRS folder for Google to work.
-- You can obtain it e.g. here: [NuGet](https://www.nuget.org/packages/Grpc.Core)
--
-- **Pro-Tipp** - use the command line with power shell to call DCS-SR-ExternalAudio.exe - it will tell you what is missing.
-- and also the Google Console error, in case you have missed a step in setting up your Google TTS.
-- E.g. `.\DCS-SR-ExternalAudio.exe -t "Text Message" -f 255 -m AM -c 2 -s 2 -z -G "Path_To_You_Google.Json"`
-- Plays a message on 255AM for the blue coalition in-game.
--
-- ## Set Voice -- ## Set Voice
-- --
-- Use a specifc voice with the @{#MSRS.SetVoice} function, e.g, `:SetVoice("Microsoft Hedda Desktop")`. -- Use a specifc voice with the @{#MSRS.SetVoice} function, e.g, `:SetVoice("Microsoft Hedda Desktop")`.
@@ -106,6 +113,14 @@
-- --
-- Use @{#MSRS.SetCoordinate} to define the origin from where the transmission is broadcasted. -- Use @{#MSRS.SetCoordinate} to define the origin from where the transmission is broadcasted.
-- --
-- ## Set SRS Port
--
-- Use @{#MSRS.SetPort} to define the SRS port. Defaults to 5002.
--
-- ## Set SRS Volume
--
-- Use @{#MSRS.SetVolume} to define the SRS volume. Defaults to 1.0. Allowed values are between 0.0 and 1.0, from silent to loudest.
--
-- @field #MSRS -- @field #MSRS
MSRS = { MSRS = {
ClassName = "MSRS", ClassName = "MSRS",
@@ -121,11 +136,12 @@ MSRS = {
volume = 1, volume = 1,
speed = 1, speed = 1,
coordinate = nil, coordinate = nil,
Label = "ROBOT",
} }
--- MSRS class version. --- MSRS class version.
-- @field #string version -- @field #string version
MSRS.version="0.0.3" MSRS.version="0.1.0"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
@@ -144,8 +160,9 @@ MSRS.version="0.0.3"
-- @param #string PathToSRS Path to the directory, where SRS is located. -- @param #string PathToSRS Path to the directory, where SRS is located.
-- @param #number Frequency Radio frequency in MHz. Default 143.00 MHz. Can also be given as a #table of multiple frequencies. -- @param #number Frequency Radio frequency in MHz. Default 143.00 MHz. Can also be given as a #table of multiple frequencies.
-- @param #number Modulation Radio modulation: 0=AM (default), 1=FM. See `radio.modulation.AM` and `radio.modulation.FM` enumerators. Can also be given as a #table of multiple modulations. -- @param #number Modulation Radio modulation: 0=AM (default), 1=FM. See `radio.modulation.AM` and `radio.modulation.FM` enumerators. Can also be given as a #table of multiple modulations.
-- @param #number Volume Volume - 1.0 is max, 0.0 is silence
-- @return #MSRS self -- @return #MSRS self
function MSRS:New(PathToSRS, Frequency, Modulation) function MSRS:New(PathToSRS, Frequency, Modulation, Volume)
-- Defaults. -- Defaults.
Frequency =Frequency or 143 Frequency =Frequency or 143
@@ -160,6 +177,13 @@ function MSRS:New(PathToSRS, Frequency, Modulation)
self:SetModulations(Modulation) self:SetModulations(Modulation)
self:SetGender() self:SetGender()
self:SetCoalition() self:SetCoalition()
self:SetLabel()
self:SetVolume()
self.lid = string.format("%s-%s | ", self.name, self.version)
if not io or not os then
self:E(self.lid.."***** ERROR - io or os NOT desanitized! MSRS will not work!")
end
return self return self
end end
@@ -202,12 +226,47 @@ function MSRS:GetPath()
return self.path return self.path
end end
--- Set SRS volume.
-- @param #MSRS self
-- @param #number Volume Volume - 1.0 is max, 0.0 is silence
-- @return #MSRS self
function MSRS:SetVolume(Volume)
local volume = Volume or 1
if volume > 1 then volume = 1 elseif volume < 0 then volume = 0 end
self.volume = volume
return self
end
--- Get SRS volume.
-- @param #MSRS self
-- @return #number Volume Volume - 1.0 is max, 0.0 is silence
function MSRS:GetVolume()
return self.volume
end
--- Set label.
-- @param #MSRS self
-- @param #number Label. Default "ROBOT"
-- @return #MSRS self
function MSRS:SetLabel(Label)
self.Label=Label or "ROBOT"
return self
end
--- Get label.
-- @param #MSRS self
-- @return #number Label.
function MSRS:GetLabel()
return self.Label
end
--- Set port. --- Set port.
-- @param #MSRS self -- @param #MSRS self
-- @param #number Port Port. Default 5002. -- @param #number Port Port. Default 5002.
-- @return #MSRS self -- @return #MSRS self
function MSRS:SetPort(Port) function MSRS:SetPort(Port)
self.port=Port or 5002 self.port=Port or 5002
return self
end end
--- Get port. --- Get port.
@@ -223,6 +282,7 @@ end
-- @return #MSRS self -- @return #MSRS self
function MSRS:SetCoalition(Coalition) function MSRS:SetCoalition(Coalition)
self.coalition=Coalition or 0 self.coalition=Coalition or 0
return self
end end
--- Get coalition. --- Get coalition.
@@ -391,22 +451,11 @@ function MSRS:PlaySoundFile(Soundfile, Delay)
local command=self:_GetCommand() local command=self:_GetCommand()
-- Append file. -- Append file.
command=command.." --file="..tostring(soundfile) command=command..' --file="'..tostring(soundfile)..'"'
-- Execute command.
self:_ExecCommand(command) self:_ExecCommand(command)
--[[
command=command.." > bla.txt"
-- Debug output.
self:I(string.format("MSRS PlaySoundfile command=%s", command))
-- Execute SRS command.
local x=os.execute(command)
]]
end end
return self return self
@@ -432,16 +481,6 @@ function MSRS:PlaySoundText(SoundText, Delay)
-- Execute command. -- Execute command.
self:_ExecCommand(command) self:_ExecCommand(command)
--[[
command=command.." > bla.txt"
-- Debug putput.
self:I(string.format("MSRS PlaySoundfile command=%s", command))
-- Execute SRS command.
local x=os.execute(command)
]]
end end
return self return self
@@ -467,37 +506,48 @@ function MSRS:PlayText(Text, Delay)
-- Execute command. -- Execute command.
self:_ExecCommand(command) self:_ExecCommand(command)
--[[ end
-- Check that length of command is max 255 chars or os.execute() will not work! return self
if string.len(command)>255 then end
-- Create a tmp file. --- Play text message via STTS with explicitly specified options.
local filename = os.getenv('TMP') .. "\\MSRS-"..STTS.uuid()..".bat" -- @param #MSRS self
-- @param #string Text Text message.
-- @param #number Delay Delay in seconds, before the message is played.
-- @param #table Frequencies Radio frequencies.
-- @param #table Modulations Radio modulations.
-- @param #string Gender Gender.
-- @param #string Culture Culture.
-- @param #string Voice Voice.
-- @param #number Volume Volume.
-- @param #string Label Label.
-- @return #MSRS self
function MSRS:PlayTextExt(Text, Delay, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label)
local script = io.open(filename, "w+") if Delay and Delay>0 then
script:write(command.." && exit") self:ScheduleOnce(Delay, MSRS.PlayTextExt, self, Text, 0, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label)
script:close() else
-- Play command.
command=string.format("\"%s\"", filename)
-- Play file in 0.05 seconds
timer.scheduleFunction(os.execute, command, timer.getTime()+0.05)
-- Remove file in 1 second.
timer.scheduleFunction(os.remove, filename, timer.getTime()+1)
else
-- Debug output.
self:I(string.format("MSRS Text command=%s", command))
-- Execute SRS command.
local x=os.execute(command)
-- Ensure table.
if Frequencies and type(Frequencies)~="table" then
Frequencies={Frequencies}
end end
]] -- Ensure table.
if Modulations and type(Modulations)~="table" then
Modulations={Modulations}
end
-- Get command line.
local command=self:_GetCommand(Frequencies, Modulations, nil, Gender, Voice, Culture, Volume, nil, nil, Label)
-- Append text.
command=command..string.format(" --text=\"%s\"", tostring(Text))
-- Execute command.
self:_ExecCommand(command)
end end
return self return self
@@ -634,8 +684,9 @@ end
-- @param #number volume Volume. -- @param #number volume Volume.
-- @param #number speed Speed. -- @param #number speed Speed.
-- @param #number port Port. -- @param #number port Port.
-- @param #string label Label, defaults to "ROBOT" (displayed sender name in the radio overlay of SRS) - No spaces allowed!
-- @return #string Command. -- @return #string Command.
function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, speed, port) function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, speed, port,label)
local path=self:GetPath() or STTS.DIRECTORY local path=self:GetPath() or STTS.DIRECTORY
local exe=STTS.EXECUTABLE or "DCS-SR-ExternalAudio.exe" local exe=STTS.EXECUTABLE or "DCS-SR-ExternalAudio.exe"
@@ -648,6 +699,7 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp
volume=volume or self.volume volume=volume or self.volume
speed=speed or self.speed speed=speed or self.speed
port=port or self.port port=port or self.port
label=label or self.Label
-- Replace modulation -- Replace modulation
modus=modus:gsub("0", "AM") modus=modus:gsub("0", "AM")
@@ -657,12 +709,12 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp
--local command=string.format("%s --freqs=%s --modulations=%s --coalition=%d --port=%d --volume=%.2f --speed=%d", exe, freqs, modus, coal, port, volume, speed) --local command=string.format("%s --freqs=%s --modulations=%s --coalition=%d --port=%d --volume=%.2f --speed=%d", exe, freqs, modus, coal, port, volume, speed)
-- Command from orig STTS script. Works better for some unknown reason! -- Command from orig STTS script. Works better for some unknown reason!
local command=string.format("start /min \"\" /d \"%s\" /b \"%s\" -f %s -m %s -c %s -p %s -n \"%s\" -h", path, exe, freqs, modus, coal, port, "ROBOT") --local command=string.format("start /min \"\" /d \"%s\" /b \"%s\" -f %s -m %s -c %s -p %s -n \"%s\" -h", path, exe, freqs, modus, coal, port, "ROBOT")
--local command=string.format('start /b "" /d "%s" "%s" -f %s -m %s -c %s -p %s -n "%s" > bla.txt', path, exe, freqs, modus, coal, port, "ROBOT") --local command=string.format('start /b "" /d "%s" "%s" -f %s -m %s -c %s -p %s -n "%s" > bla.txt', path, exe, freqs, modus, coal, port, "ROBOT")
-- Command. -- Command.
local command=string.format('%s/%s -f %s -m %s -c %s -p %s -n "%s"', path, exe, freqs, modus, coal, port, "ROBOT") local command=string.format('"%s\\%s" -f %s -m %s -c %s -p %s -n "%s" -v "%.1f"', path, exe, freqs, modus, coal, port, label,volume)
-- Set voice or gender/culture. -- Set voice or gender/culture.
if voice then if voice then
@@ -671,7 +723,7 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp
else else
-- Add gender. -- Add gender.
if gender and gender~="female" then if gender and gender~="female" then
command=command..string.format(" --gender=%s", tostring(gender)) command=command..string.format(" -g %s", tostring(gender))
end end
-- Add culture. -- Add culture.
if culture and culture~="en-GB" then if culture and culture~="en-GB" then
@@ -696,6 +748,353 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp
return command return command
end end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Manages radio transmissions.
--
-- The purpose of the MSRSQUEUE class is to manage SRS text-to-speech (TTS) messages using the MSRS class.
-- This can be used to submit multiple TTS messages and the class takes care that they are transmitted one after the other (and not overlapping).
--
-- @type MSRSQUEUE
-- @field #string ClassName Name of the class "MSRSQUEUE".
-- @field #string lid ID for dcs.log.
-- @field #table queue The queue of transmissions.
-- @field #string alias Name of the radio queue.
-- @field #number dt Time interval in seconds for checking the radio queue.
-- @field #number Tlast Time (abs) when the last transmission finished.
-- @field #boolean checking If `true`, the queue update function is scheduled to be called again.
-- @extends Core.Base#BASE
MSRSQUEUE = {
ClassName = "MSRSQUEUE",
Debugmode = nil,
lid = nil,
queue = {},
alias = nil,
dt = nil,
Tlast = nil,
checking = nil,
}
--- Radio queue transmission data.
-- @type MSRSQUEUE.Transmission
-- @field #string text Text to be transmitted.
-- @field Sound.SRS#MSRS msrs MOOSE SRS object.
-- @field #number duration Duration in seconds.
-- @field #table subgroups Groups to send subtitle to.
-- @field #string subtitle Subtitle of the transmission.
-- @field #number subduration Duration of the subtitle being displayed.
-- @field #number frequency Frequency.
-- @field #number modulation Modulation.
-- @field #number Tstarted Mission time (abs) in seconds when the transmission started.
-- @field #boolean isplaying If true, transmission is currently playing.
-- @field #number Tplay Mission time (abs) in seconds when the transmission should be played.
-- @field #number interval Interval in seconds before next transmission.
--- Create a new MSRSQUEUE object for a given radio frequency/modulation.
-- @param #MSRSQUEUE self
-- @param #string alias (Optional) Name of the radio queue.
-- @return #MSRSQUEUE self The MSRSQUEUE object.
function MSRSQUEUE:New(alias)
-- Inherit base
local self=BASE:Inherit(self, BASE:New()) --#MSRSQUEUE
self.alias=alias or "My Radio"
self.dt=1.0
self.lid=string.format("MSRSQUEUE %s | ", self.alias)
return self
end
--- Clear the radio queue.
-- @param #MSRSQUEUE self
-- @return #MSRSQUEUE self The MSRSQUEUE object.
function MSRSQUEUE:Clear()
self:I(self.lid.."Clearning MSRSQUEUE")
self.queue={}
return self
end
--- Add a transmission to the radio queue.
-- @param #MSRSQUEUE self
-- @param #MSRSQUEUE.Transmission transmission The transmission data table.
-- @return #MSRSQUEUE self
function MSRSQUEUE:AddTransmission(transmission)
-- Init.
transmission.isplaying=false
transmission.Tstarted=nil
-- Add to queue.
table.insert(self.queue, transmission)
-- Start checking.
if not self.checking then
self:_CheckRadioQueue()
end
return self
end
--- Create a new transmission and add it to the radio queue.
-- @param #MSRSQUEUE self
-- @param #string text Text to play.
-- @param #number duration Duration in seconds the file lasts. Default is determined by number of characters of the text message.
-- @param Sound.SRS#MSRS msrs MOOSE SRS object.
-- @param #number tstart Start time (abs) seconds. Default now.
-- @param #number interval Interval in seconds after the last transmission finished.
-- @param #table subgroups Groups that should receive the subtiltle.
-- @param #string subtitle Subtitle displayed when the message is played.
-- @param #number subduration Duration [sec] of the subtitle being displayed. Default 5 sec.
-- @param #number frequency Radio frequency if other than MSRS default.
-- @param #number modulation Radio modulation if other then MSRS default.
-- @return #MSRSQUEUE.Transmission Radio transmission table.
function MSRSQUEUE:NewTransmission(text, duration, msrs, tstart, interval, subgroups, subtitle, subduration, frequency, modulation)
-- Sanity checks.
if not text then
self:E(self.lid.."ERROR: No text specified.")
return nil
end
if type(text)~="string" then
self:E(self.lid.."ERROR: Text specified is NOT a string.")
return nil
end
-- Create a new transmission object.
local transmission={} --#MSRSQUEUE.Transmission
transmission.text=text
transmission.duration=duration or STTS.getSpeechTime(text)
transmission.msrs=msrs
transmission.Tplay=tstart or timer.getAbsTime()
transmission.subtitle=subtitle
transmission.interval=interval or 0
transmission.frequency=frequency
transmission.modulation=modulation
transmission.subgroups=subgroups
if transmission.subtitle then
transmission.subduration=subduration or transmission.duration
else
transmission.subduration=0 --nil
end
-- Add transmission to queue.
self:AddTransmission(transmission)
return transmission
end
--- Broadcast radio message.
-- @param #MSRSQUEUE self
-- @param #MSRSQUEUE.Transmission transmission The transmission.
function MSRSQUEUE:Broadcast(transmission)
if transmission.frequency then
transmission.msrs:PlayTextExt(transmission.text, nil, transmission.frequency, transmission.modulation, Gender, Culture, Voice, Volume, Label)
else
transmission.msrs:PlayText(transmission.text)
end
local function texttogroup(gid)
-- Text to group.
trigger.action.outTextForGroup(gid, transmission.subtitle, transmission.subduration, true)
end
if transmission.subgroups and #transmission.subgroups>0 then
for _,_group in pairs(transmission.subgroups) do
local group=_group --Wrapper.Group#GROUP
if group and group:IsAlive() then
local gid=group:GetID()
self:ScheduleOnce(4, texttogroup, gid)
end
end
end
end
--- Calculate total transmission duration of all transmission in the queue.
-- @param #MSRSQUEUE self
-- @return #number Total transmission duration.
function MSRSQUEUE:CalcTransmisstionDuration()
local Tnow=timer.getAbsTime()
local T=0
for _,_transmission in pairs(self.queue) do
local transmission=_transmission --#MSRSQUEUE.Transmission
if transmission.isplaying then
-- Playing for dt seconds.
local dt=Tnow-transmission.Tstarted
T=T+transmission.duration-dt
else
T=T+transmission.duration
end
end
return T
end
--- Check radio queue for transmissions to be broadcasted.
-- @param #MSRSQUEUE self
-- @param #number delay Delay in seconds before checking.
function MSRSQUEUE:_CheckRadioQueue(delay)
-- Transmissions in queue.
local N=#self.queue
-- Debug info.
self:T2(self.lid..string.format("Check radio queue %s: delay=%.3f sec, N=%d, checking=%s", self.alias, delay or 0, N, tostring(self.checking)))
if delay and delay>0 then
-- Delayed call.
self:ScheduleOnce(delay, MSRSQUEUE._CheckRadioQueue, self)
-- Checking on.
self.checking=true
else
-- Check if queue is empty.
if N==0 then
-- Debug info.
self:T(self.lid..string.format("Check radio queue %s empty ==> disable checking", self.alias))
-- Queue is now empty. Nothing to else to do. We start checking again, if a transmission is added.
self.checking=false
return
end
-- Get current abs time.
local time=timer.getAbsTime()
-- Checking on.
self.checking=true
-- Set dt.
local dt=self.dt
local playing=false
local next=nil --#MSRSQUEUE.Transmission
local remove=nil
for i,_transmission in ipairs(self.queue) do
local transmission=_transmission --#MSRSQUEUE.Transmission
-- Check if transmission time has passed.
if time>=transmission.Tplay then
-- Check if transmission is currently playing.
if transmission.isplaying then
-- Check if transmission is finished.
if time>=transmission.Tstarted+transmission.duration then
-- Transmission over.
transmission.isplaying=false
-- Remove ith element in queue.
remove=i
-- Store time last transmission finished.
self.Tlast=time
else -- still playing
-- Transmission is still playing.
playing=true
dt=transmission.duration-(time-transmission.Tstarted)
end
else -- not playing yet
local Tlast=self.Tlast
if transmission.interval==nil then
-- Not playing ==> this will be next.
if next==nil then
next=transmission
end
else
if Tlast==nil or time-Tlast>=transmission.interval then
next=transmission
else
end
end
-- We got a transmission or one with an interval that is not due yet. No need for anything else.
if next or Tlast then
break
end
end
else
-- Transmission not due yet.
end
end
-- Found a new transmission.
if next~=nil and not playing then
-- Debug info.
self:T(self.lid..string.format("Broadcasting text=\"%s\" at T=%.3f", next.text, time))
-- Call SRS.
self:Broadcast(next)
next.isplaying=true
next.Tstarted=time
dt=next.duration
end
-- Remove completed call from queue.
if remove then
-- Remove from queue.
table.remove(self.queue, remove)
N=N-1
-- Check if queue is empty.
if #self.queue==0 then
-- Debug info.
self:T(self.lid..string.format("Check radio queue %s empty ==> disable checking", self.alias))
self.checking=false
return
end
end
-- Check queue.
self:_CheckRadioQueue(dt)
end
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -183,13 +183,17 @@ do -- Sound File
--- Set path, where the sound file is located. --- Set path, where the sound file is located.
-- @param #SOUNDFILE self -- @param #SOUNDFILE self
-- @param #string Path Path to the directory, where the sound file is located. -- @param #string Path Path to the directory, where the sound file is located. In case this is nil, it defaults to the DCS mission temp directory.
-- @return #SOUNDFILE self -- @return #SOUNDFILE self
function SOUNDFILE:SetPath(Path) function SOUNDFILE:SetPath(Path)
-- Init path. -- Init path.
self.path=Path or "l10n/DEFAULT/" self.path=Path or "l10n/DEFAULT/"
if not Path and self.useSRS then -- use path to mission temp dir
self.path = os.getenv('TMP') .. "\\DCS\\Mission\\l10n\\DEFAULT"
end
-- Remove (back)slashes. -- Remove (back)slashes.
local nmax=1000 ; local n=1 local nmax=1000 ; local n=1
while (self.path:sub(-1)=="/" or self.path:sub(-1)==[[\]]) and n<=nmax do while (self.path:sub(-1)=="/" or self.path:sub(-1)==[[\]]) and n<=nmax do
+29 -7
View File
@@ -40,7 +40,7 @@ do -- UserSound
-- @param #USERSOUND self -- @param #USERSOUND self
-- @param #string UserSoundFileName The filename of the usersound. -- @param #string UserSoundFileName The filename of the usersound.
-- @return #USERSOUND -- @return #USERSOUND
function USERSOUND:New( UserSoundFileName ) --R2.3 function USERSOUND:New( UserSoundFileName )
local self = BASE:Inherit( self, BASE:New() ) -- #USERSOUND local self = BASE:Inherit( self, BASE:New() ) -- #USERSOUND
@@ -58,7 +58,7 @@ do -- UserSound
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" ) -- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
-- BlueVictory:SetFileName( "BlueVictoryLoud.ogg" ) -- Set the BlueVictory to change the file name to play a louder sound. -- BlueVictory:SetFileName( "BlueVictoryLoud.ogg" ) -- Set the BlueVictory to change the file name to play a louder sound.
-- --
function USERSOUND:SetFileName( UserSoundFileName ) --R2.3 function USERSOUND:SetFileName( UserSoundFileName )
self.UserSoundFileName = UserSoundFileName self.UserSoundFileName = UserSoundFileName
@@ -75,7 +75,7 @@ do -- UserSound
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" ) -- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
-- BlueVictory:ToAll() -- Play the sound that Blue has won. -- BlueVictory:ToAll() -- Play the sound that Blue has won.
-- --
function USERSOUND:ToAll() --R2.3 function USERSOUND:ToAll()
trigger.action.outSound( self.UserSoundFileName ) trigger.action.outSound( self.UserSoundFileName )
@@ -91,7 +91,7 @@ do -- UserSound
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" ) -- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
-- BlueVictory:ToCoalition( coalition.side.BLUE ) -- Play the sound that Blue has won to the blue coalition. -- BlueVictory:ToCoalition( coalition.side.BLUE ) -- Play the sound that Blue has won to the blue coalition.
-- --
function USERSOUND:ToCoalition( Coalition ) --R2.3 function USERSOUND:ToCoalition( Coalition )
trigger.action.outSoundForCoalition(Coalition, self.UserSoundFileName ) trigger.action.outSoundForCoalition(Coalition, self.UserSoundFileName )
@@ -107,7 +107,7 @@ do -- UserSound
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" ) -- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
-- BlueVictory:ToCountry( country.id.USA ) -- Play the sound that Blue has won to the USA country. -- BlueVictory:ToCountry( country.id.USA ) -- Play the sound that Blue has won to the USA country.
-- --
function USERSOUND:ToCountry( Country ) --R2.3 function USERSOUND:ToCountry( Country )
trigger.action.outSoundForCountry( Country, self.UserSoundFileName ) trigger.action.outSoundForCountry( Country, self.UserSoundFileName )
@@ -123,9 +123,9 @@ do -- UserSound
-- @usage -- @usage
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" ) -- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
-- local PlayerGroup = GROUP:FindByName( "PlayerGroup" ) -- Search for the active group named "PlayerGroup", that contains a human player. -- local PlayerGroup = GROUP:FindByName( "PlayerGroup" ) -- Search for the active group named "PlayerGroup", that contains a human player.
-- BlueVictory:ToGroup( PlayerGroup ) -- Play the sound that Blue has won to the player group. -- BlueVictory:ToGroup( PlayerGroup ) -- Play the victory sound to the player group.
-- --
function USERSOUND:ToGroup( Group, Delay ) --R2.3 function USERSOUND:ToGroup( Group, Delay )
Delay=Delay or 0 Delay=Delay or 0
if Delay>0 then if Delay>0 then
@@ -137,4 +137,26 @@ do -- UserSound
return self return self
end end
--- Play the usersound to the given @{Wrapper.Unit}.
-- @param #USERSOUND self
-- @param Wrapper.Unit#UNIT Unit The @{Wrapper.Unit} to play the usersound to.
-- @param #number Delay (Optional) Delay in seconds, before the sound is played. Default 0.
-- @return #USERSOUND The usersound instance.
-- @usage
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
-- local PlayerUnit = UNIT:FindByName( "PlayerUnit" ) -- Search for the active group named "PlayerUnit", a human player.
-- BlueVictory:ToUnit( PlayerUnit ) -- Play the victory sound to the player unit.
--
function USERSOUND:ToUnit( Unit, Delay )
Delay=Delay or 0
if Delay>0 then
SCHEDULER:New(nil, USERSOUND.ToUnit,{self, Unit}, Delay)
else
trigger.action.outSoundForUnit( Unit:GetID(), self.UserSoundFileName )
end
return self
end
end end
+1 -1
View File
@@ -132,7 +132,7 @@ MISSION = {
-- @param #string MissionName Name of the mission. This name will be used to reference the status of each mission by the players. -- @param #string MissionName Name of the mission. This name will be used to reference the status of each mission by the players.
-- @param #string MissionPriority String indicating the "priority" of the Mission. e.g. "Primary", "Secondary". It is free format and up to the Mission designer to choose. There are no rules behind this field. -- @param #string MissionPriority String indicating the "priority" of the Mission. e.g. "Primary", "Secondary". It is free format and up to the Mission designer to choose. There are no rules behind this field.
-- @param #string MissionBriefing String indicating the mission briefing to be shown when a player joins a @{CLIENT}. -- @param #string MissionBriefing String indicating the mission briefing to be shown when a player joins a @{CLIENT}.
-- @param DCS#coaliton.side MissionCoalition Side of the coalition, i.e. and enumerator @{#DCS.coalition.side} corresponding to RED, BLUE or NEUTRAL. -- @param DCS#coalition.side MissionCoalition Side of the coalition, i.e. and enumerator @{#DCS.coalition.side} corresponding to RED, BLUE or NEUTRAL.
-- @return #MISSION self -- @return #MISSION self
function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefing, MissionCoalition ) function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefing, MissionCoalition )
+1 -1
View File
@@ -82,7 +82,7 @@
-- --
-- ![Mission](../Tasking/Report_Statistics_Progress.JPG) -- ![Mission](../Tasking/Report_Statistics_Progress.JPG)
-- --
-- A statistic report on the progress of the mission. Each task achievement will increase the %-tage to 100% as a goal to complete the task. -- A statistic report on the progress of the mission. Each task achievement will increase the % to 100% as a goal to complete the task.
-- --
-- ## 1.3) Join a Task. -- ## 1.3) Join a Task.
-- --
+244 -45
View File
@@ -30,16 +30,16 @@ ENUMS = {}
-- @type ENUMS.ROE -- @type ENUMS.ROE
-- @field #number WeaponFree AI will engage any enemy group it detects. Target prioritization is based based on the threat of the target. -- @field #number WeaponFree AI will engage any enemy group it detects. Target prioritization is based based on the threat of the target.
-- @field #number OpenFireWeaponFree AI will engage any enemy group it detects, but will prioritize targets specified in the groups tasking. -- @field #number OpenFireWeaponFree AI will engage any enemy group it detects, but will prioritize targets specified in the groups tasking.
-- @field #number OpenFire AI will engage only targets specified in its tasking. -- @field #number OpenFire AI will engage only targets specified in its taskings.
-- @field #number ReturnFire AI will only engage threats that shoot first. -- @field #number ReturnFire AI will only engage threats that shoot first.
-- @field #number WeaponHold AI will hold fire under all circumstances. -- @field #number WeaponHold AI will hold fire under all circumstances.
ENUMS.ROE = { ENUMS.ROE = {
WeaponFree = 0, WeaponFree=0,
OpenFireWeaponFree = 1, OpenFireWeaponFree=1,
OpenFire = 2, OpenFire=2,
ReturnFire = 3, ReturnFire=3,
WeaponHold = 4, WeaponHold=4,
} }
--- Reaction On Threat. --- Reaction On Threat.
-- @type ENUMS.ROT -- @type ENUMS.ROT
@@ -49,11 +49,11 @@ ENUMS.ROE = {
-- @field #number BypassAndEscape AI will attempt to avoid enemy threat zones all together. This includes attempting to fly above or around threats. -- @field #number BypassAndEscape AI will attempt to avoid enemy threat zones all together. This includes attempting to fly above or around threats.
-- @field #number AllowAbortMission If a threat is deemed severe enough the AI will abort its mission and return to base. -- @field #number AllowAbortMission If a threat is deemed severe enough the AI will abort its mission and return to base.
ENUMS.ROT = { ENUMS.ROT = {
NoReaction = 0, NoReaction=0,
PassiveDefense = 1, PassiveDefense=1,
EvadeFire = 2, EvadeFire=2,
BypassAndEscape = 3, BypassAndEscape=3,
AllowAbortMission = 4, AllowAbortMission=4,
} }
--- Alarm state. --- Alarm state.
@@ -62,12 +62,12 @@ ENUMS.ROT = {
-- @field #number Green Group is not combat ready. Sensors are stowed if possible. -- @field #number Green Group is not combat ready. Sensors are stowed if possible.
-- @field #number Red Group is combat ready and actively searching for targets. Some groups like infantry will not move in this state. -- @field #number Red Group is combat ready and actively searching for targets. Some groups like infantry will not move in this state.
ENUMS.AlarmState = { ENUMS.AlarmState = {
Auto = 0, Auto=0,
Green = 1, Green=1,
Red = 2, Red=2,
} }
--- Weapon types. See the [Weapon Flag](https://wiki.hoggitworld.com/view/DCS_enum_weapon_flag) enumerator on Hoggit wiki. --- Weapon types. See the [Weapon Flag](https://wiki.hoggitworld.com/view/DCS_enum_weapon_flag) enumerotor on hoggit wiki.
-- @type ENUMS.WeaponFlag -- @type ENUMS.WeaponFlag
ENUMS.WeaponFlag={ ENUMS.WeaponFlag={
-- Bombs -- Bombs
@@ -111,7 +111,7 @@ ENUMS.WeaponFlag={
-- --
-- Bombs -- Bombs
GuidedBomb = 14, -- (LGB + TvGB + SNSGB) GuidedBomb = 14, -- (LGB + TvGB + SNSGB)
AnyUnguidedBomb = 2147485680, -- (HeBomb + Penetrator + NapalmBomb + FAEBomb + ClusterBomb + Dispenser + CandleBomb + ParachuteBomb) AnyUnguidedBomb = 2147485680, -- (HeBomb + Penetrator + NapalmBomb + FAEBomb + ClusterBomb + Dispencer + CandleBomb + ParachuteBomb)
AnyBomb = 2147485694, -- (GuidedBomb + AnyUnguidedBomb) AnyBomb = 2147485694, -- (GuidedBomb + AnyUnguidedBomb)
--- Rockets --- Rockets
AnyRocket = 30720, -- LightRocket + MarkerRocket + CandleRocket + HeavyRocket AnyRocket = 30720, -- LightRocket + MarkerRocket + CandleRocket + HeavyRocket
@@ -126,6 +126,8 @@ ENUMS.WeaponFlag={
AnyMissile = 268402688, -- AnyASM + AnyAAM AnyMissile = 268402688, -- AnyASM + AnyAAM
--- Guns --- Guns
Cannons = 805306368, -- GUN_POD + BuiltInCannon Cannons = 805306368, -- GUN_POD + BuiltInCannon
--- Torpedo
Torpedo = 4294967296,
--- ---
-- Even More Genral -- Even More Genral
Auto = 3221225470, -- Any Weapon (AnyBomb + AnyRocket + AnyMissile + Cannons) Auto = 3221225470, -- Any Weapon (AnyBomb + AnyRocket + AnyMissile + Cannons)
@@ -136,6 +138,93 @@ ENUMS.WeaponFlag={
AnyGuided = 268402702, -- Any Guided Weapon AnyGuided = 268402702, -- Any Guided Weapon
} }
--- Weapon types by category. See the [Weapon Flag](https://wiki.hoggitworld.com/view/DCS_enum_weapon_flag) enumerator on hoggit wiki.
-- @type ENUMS.WeaponType
-- @field #table Bomb Bombs.
-- @field #table Rocket Rocket.
-- @field #table Gun Guns.
-- @field #table Missile Missiles.
-- @field #table AAM Air-to-Air missiles.
-- @field #table Torpedo Torpedos.
-- @field #table Any Combinations.
ENUMS.WeaponType={}
ENUMS.WeaponType.Bomb={
-- Bombs
LGB = 2,
TvGB = 4,
SNSGB = 8,
HEBomb = 16,
Penetrator = 32,
NapalmBomb = 64,
FAEBomb = 128,
ClusterBomb = 256,
Dispencer = 512,
CandleBomb = 1024,
ParachuteBomb = 2147483648,
-- Combinations
GuidedBomb = 14, -- (LGB + TvGB + SNSGB)
AnyUnguidedBomb = 2147485680, -- (HeBomb + Penetrator + NapalmBomb + FAEBomb + ClusterBomb + Dispencer + CandleBomb + ParachuteBomb)
AnyBomb = 2147485694, -- (GuidedBomb + AnyUnguidedBomb)
}
ENUMS.WeaponType.Rocket={
-- Rockets
LightRocket = 2048,
MarkerRocket = 4096,
CandleRocket = 8192,
HeavyRocket = 16384,
-- Combinations
AnyRocket = 30720, -- LightRocket + MarkerRocket + CandleRocket + HeavyRocket
}
ENUMS.WeaponType.Gun={
-- Guns
GunPod = 268435456,
BuiltInCannon = 536870912,
-- Combinations
Cannons = 805306368, -- GUN_POD + BuiltInCannon
}
ENUMS.WeaponType.Missile={
-- Missiles
AntiRadarMissile = 32768,
AntiShipMissile = 65536,
AntiTankMissile = 131072,
FireAndForgetASM = 262144,
LaserASM = 524288,
TeleASM = 1048576,
CruiseMissile = 2097152,
AntiRadarMissile2 = 1073741824,
-- Combinations
GuidedASM = 1572864, -- (LaserASM + TeleASM)
TacticalASM = 1835008, -- (GuidedASM + FireAndForgetASM)
AnyASM = 4161536, -- (AntiRadarMissile + AntiShipMissile + AntiTankMissile + FireAndForgetASM + GuidedASM + CruiseMissile)
AnyASM2 = 1077903360, -- 4161536+1073741824,
AnyAutonomousMissile = 36012032, -- IR_AAM + AntiRadarMissile + AntiShipMissile + FireAndForgetASM + CruiseMissile
AnyMissile = 268402688, -- AnyASM + AnyAAM
}
ENUMS.WeaponType.AAM={
-- Air-To-Air Missiles
SRAM = 4194304,
MRAAM = 8388608,
LRAAM = 16777216,
IR_AAM = 33554432,
SAR_AAM = 67108864,
AR_AAM = 134217728,
-- Combinations
AnyAAM = 264241152, -- IR_AAM + SAR_AAM + AR_AAM + SRAAM + MRAAM + LRAAM
}
ENUMS.WeaponType.Torpedo={
-- Torpedo
Torpedo = 4294967296,
}
ENUMS.WeaponType.Any={
-- General combinations
Weapon = 3221225470, -- Any Weapon (AnyBomb + AnyRocket + AnyMissile + Cannons)
AG = 2956984318, -- Any Air-To-Ground Weapon
AA = 264241152, -- Any Air-To-Air Weapon
Unguided = 2952822768, -- Any Unguided Weapon
Guided = 268402702, -- Any Guided Weapon
}
--- Mission tasks. --- Mission tasks.
-- @type ENUMS.MissionTask -- @type ENUMS.MissionTask
-- @field #string NOTHING No special task. Group can perform the minimal tasks: Orbit, Refuelling, Follow and Aerobatics. -- @field #string NOTHING No special task. Group can perform the minimal tasks: Orbit, Refuelling, Follow and Aerobatics.
@@ -173,7 +262,7 @@ ENUMS.MissionTask={
TRANSPORT="Transport", TRANSPORT="Transport",
} }
--- Formations (new). See the [Formations](https://wiki.hoggitworld.com/view/DCS_enum_formation) on Hoggit wiki. --- Formations (new). See the [Formations](https://wiki.hoggitworld.com/view/DCS_enum_formation) on hoggit wiki.
-- @type ENUMS.Formation -- @type ENUMS.Formation
ENUMS.Formation={} ENUMS.Formation={}
ENUMS.Formation.FixedWing={} ENUMS.Formation.FixedWing={}
@@ -216,23 +305,23 @@ ENUMS.Formation.FixedWing.FighterVic.Close = 917505
ENUMS.Formation.FixedWing.FighterVic.Open = 917506 ENUMS.Formation.FixedWing.FighterVic.Open = 917506
ENUMS.Formation.RotaryWing={} ENUMS.Formation.RotaryWing={}
ENUMS.Formation.RotaryWing.Column={} ENUMS.Formation.RotaryWing.Column={}
ENUMS.Formation.RotaryWing.Column.D70 = 720896 ENUMS.Formation.RotaryWing.Column.D70=720896
ENUMS.Formation.RotaryWing.Wedge={} ENUMS.Formation.RotaryWing.Wedge={}
ENUMS.Formation.RotaryWing.Wedge.D70 = 8 ENUMS.Formation.RotaryWing.Wedge.D70=8
ENUMS.Formation.RotaryWing.FrontRight={} ENUMS.Formation.RotaryWing.FrontRight={}
ENUMS.Formation.RotaryWing.FrontRight.D300 = 655361 ENUMS.Formation.RotaryWing.FrontRight.D300=655361
ENUMS.Formation.RotaryWing.FrontRight.D600 = 655362 ENUMS.Formation.RotaryWing.FrontRight.D600=655362
ENUMS.Formation.RotaryWing.FrontLeft={} ENUMS.Formation.RotaryWing.FrontLeft={}
ENUMS.Formation.RotaryWing.FrontLeft.D300 = 655617 ENUMS.Formation.RotaryWing.FrontLeft.D300=655617
ENUMS.Formation.RotaryWing.FrontLeft.D600 = 655618 ENUMS.Formation.RotaryWing.FrontLeft.D600=655618
ENUMS.Formation.RotaryWing.EchelonRight={} ENUMS.Formation.RotaryWing.EchelonRight={}
ENUMS.Formation.RotaryWing.EchelonRight.D70 = 589825 ENUMS.Formation.RotaryWing.EchelonRight.D70 =589825
ENUMS.Formation.RotaryWing.EchelonRight.D300 = 589826 ENUMS.Formation.RotaryWing.EchelonRight.D300=589826
ENUMS.Formation.RotaryWing.EchelonRight.D600 = 589827 ENUMS.Formation.RotaryWing.EchelonRight.D600=589827
ENUMS.Formation.RotaryWing.EchelonLeft={} ENUMS.Formation.RotaryWing.EchelonLeft={}
ENUMS.Formation.RotaryWing.EchelonLeft.D70 = 590081 ENUMS.Formation.RotaryWing.EchelonLeft.D70 =590081
ENUMS.Formation.RotaryWing.EchelonLeft.D300 = 590082 ENUMS.Formation.RotaryWing.EchelonLeft.D300=590082
ENUMS.Formation.RotaryWing.EchelonLeft.D600 = 590083 ENUMS.Formation.RotaryWing.EchelonLeft.D600=590083
ENUMS.Formation.Vehicle={} ENUMS.Formation.Vehicle={}
ENUMS.Formation.Vehicle.Vee="Vee" ENUMS.Formation.Vehicle.Vee="Vee"
ENUMS.Formation.Vehicle.EchelonRight="EchelonR" ENUMS.Formation.Vehicle.EchelonRight="EchelonR"
@@ -244,25 +333,25 @@ ENUMS.Formation.Vehicle.Cone="Cone"
ENUMS.Formation.Vehicle.Diamond="Diamond" ENUMS.Formation.Vehicle.Diamond="Diamond"
--- Formations (old). The old format is a simplified version of the new formation enums, which allow more sophisticated settings. --- Formations (old). The old format is a simplified version of the new formation enums, which allow more sophisticated settings.
-- See the [Formations](https://wiki.hoggitworld.com/view/DCS_enum_formation) on Hoggit wiki. -- See the [Formations](https://wiki.hoggitworld.com/view/DCS_enum_formation) on hoggit wiki.
-- @type ENUMS.FormationOld -- @type ENUMS.FormationOld
ENUMS.FormationOld={} ENUMS.FormationOld={}
ENUMS.FormationOld.FixedWing={} ENUMS.FormationOld.FixedWing={}
ENUMS.FormationOld.FixedWing.LineAbreast = 1 ENUMS.FormationOld.FixedWing.LineAbreast=1
ENUMS.FormationOld.FixedWing.Trail = 2 ENUMS.FormationOld.FixedWing.Trail=2
ENUMS.FormationOld.FixedWing.Wedge = 3 ENUMS.FormationOld.FixedWing.Wedge=3
ENUMS.FormationOld.FixedWing.EchelonRight = 4 ENUMS.FormationOld.FixedWing.EchelonRight=4
ENUMS.FormationOld.FixedWing.EchelonLeft = 5 ENUMS.FormationOld.FixedWing.EchelonLeft=5
ENUMS.FormationOld.FixedWing.FingerFour = 6 ENUMS.FormationOld.FixedWing.FingerFour=6
ENUMS.FormationOld.FixedWing.SpreadFour = 7 ENUMS.FormationOld.FixedWing.SpreadFour=7
ENUMS.FormationOld.FixedWing.BomberElement = 12 ENUMS.FormationOld.FixedWing.BomberElement=12
ENUMS.FormationOld.FixedWing.BomberElementHeight = 13 ENUMS.FormationOld.FixedWing.BomberElementHeight=13
ENUMS.FormationOld.FixedWing.FighterVic = 14 ENUMS.FormationOld.FixedWing.FighterVic=14
ENUMS.FormationOld.RotaryWing={} ENUMS.FormationOld.RotaryWing={}
ENUMS.FormationOld.RotaryWing.Wedge = 8 ENUMS.FormationOld.RotaryWing.Wedge=8
ENUMS.FormationOld.RotaryWing.Echelon = 9 ENUMS.FormationOld.RotaryWing.Echelon=9
ENUMS.FormationOld.RotaryWing.Front = 10 ENUMS.FormationOld.RotaryWing.Front=10
ENUMS.FormationOld.RotaryWing.Column = 11 ENUMS.FormationOld.RotaryWing.Column=11
--- Morse Code. See the [Wikipedia](https://en.wikipedia.org/wiki/Morse_code). --- Morse Code. See the [Wikipedia](https://en.wikipedia.org/wiki/Morse_code).
@@ -360,3 +449,113 @@ ENUMS.Phonetic =
Y = 'Yankee', Y = 'Yankee',
Z = 'Zulu', Z = 'Zulu',
} }
--- Reporting Names (NATO). See the [Wikipedia](https://en.wikipedia.org/wiki/List_of_NATO_reporting_names_for_fighter_aircraft).
-- DCS known aircraft types
--
-- @type ENUMS.ReportingName
ENUMS.ReportingName =
{
NATO = {
-- Fighters
Dragon = "JF-17", -- China, correctly Fierce Dragon, Thunder for PAC
Fagot = "MiG-15",
Farmer = "MiG-19", -- Shenyang J-6 and Mikoyan-Gurevich MiG-19
Felon = "Su-57",
Fencer = "Su-24",
Fishbed = "MiG-21",
Fitter = "Su-17", -- Sukhoi Su-7 and Su-17/Su-20/Su-22
Flogger = "MiG-23", --and MiG-27
Flogger_D = "MiG-27", --and MiG-23
Flagon = "Su-15",
Foxbat = "MiG-25",
Fulcrum = "MiG-29",
Foxhound = "MiG-31",
Flanker = "Su-27", -- Sukhoi Su-27/Su-30/Su-33/Su-35/Su-37 and Shenyang J-11/J-15/J-16
Flanker_C = "Su-30",
Flanker_E = "Su-35",
Flanker_F = "Su-37",
Flanker_L = "J-11A",
Firebird = "J-10",
Sea_Flanker = "Su-33",
Fullback = "Su-34", -- also Su-32
Frogfoot = "Su-25",
Tomcat = "F-14", -- Iran
Mirage = "Mirage", -- various non-NATO
Codling = "Yak-40",
Maya = "L-39",
-- Fighters US/NATO
Warthog = "A-10",
--Mosquito = "A-20",
Skyhawk = "A-4E",
Viggen = "AJS37",
Harrier = "AV-8B",
Spirit = "B-2",
Aviojet = "C-101",
Nighthawk = "F-117A",
Eagle = "F-15C",
Mudhen = "F-15E",
Viper = "F-16",
Phantom = "F-4E",
Tiger = "F-5", -- was thinking to name this MiG-25 ;)
Sabre = "F-86",
Hornet = "A-18", -- avoiding the slash
Hawk = "Hawk",
Albatros = "L-39",
Goshawk = "T-45",
Starfighter = "F-104",
Tornado = "Tornado",
-- Transport / Bomber / Others
Atlas = "A400",
Lancer = "B1-B",
Stratofortress = "B-52H",
Hercules = "C-130",
Super_Hercules = "Hercules",
Globemaster = "C-17",
Greyhound = "C-2A",
Galaxy = "C-5",
Hawkeye = "E-2D",
Sentry = "E-3A",
Stratotanker = "KC-135",
Extender = "KC-10",
Orion = "P-3C",
Viking = "S-3B",
Osprey = "V-22",
-- Bomber Rus
Badger = "H6-J",
Bear_J = "Tu-142", -- also Tu-95
Bear = "Tu-95", -- also Tu-142
Blinder = "Tu-22",
Blackjack = "Tu-160",
-- AIC / Transport / Other
Clank = "An-30",
Curl = "An-26",
Candid = "IL-76",
Midas = "IL-78",
Mainstay = "A-50",
Mainring = "KJ-2000", -- A-50 China
Yak = "Yak-52",
-- Helos
Helix = "Ka-27",
Shark = "Ka-50",
Hind = "Mi-24",
Halo = "Mi-26",
Hip = "Mi-8",
Havoc = "Mi-28",
Gazelle = "SA342",
-- Helos US
Huey = "UH-1H",
Cobra = "AH-1",
Apache = "AH-64",
Chinook = "CH-47",
Sea_Stallion = "CH-53",
Kiowa = "OH-58",
Seahawk = "SH-60",
Blackhawk = "UH-60",
Sea_King = "S-61",
-- Drones
UCAV = "WingLoong",
Reaper = "MQ-9",
Predator = "MQ-1A",
}
}
+773
View File
@@ -0,0 +1,773 @@
--- **UTILS** - ClassicFiFo Stack.
--
-- ===
--
-- ## Main Features:
--
-- * Build a simple multi-purpose FiFo (First-In, First-Out) stack for generic data.
-- * [Wikipedia](https://en.wikipedia.org/wiki/FIFO_(computing_and_electronics)
--
-- ===
--
-- ### Author: **applevangelist**
-- @module Utils.FiFo
-- @image MOOSE.JPG
-- Date: April 2022
do
--- FIFO class.
-- @type FIFO
-- @field #string ClassName Name of the class.
-- @field #string lid Class id string for output to DCS log file.
-- @field #string version Version of FiFo
-- @field #number counter
-- @field #number pointer
-- @field #table stackbypointer
-- @field #table stackbyid
-- @extends Core.Base#BASE
---
-- @type FIFO.IDEntry
-- @field #number pointer
-- @field #table data
-- @field #table uniqueID
---
-- @field #FIFO
FIFO = {
ClassName = "FIFO",
lid = "",
version = "0.0.5",
counter = 0,
pointer = 0,
stackbypointer = {},
stackbyid = {}
}
--- Instantiate a new FIFO Stack
-- @param #FIFO self
-- @return #FIFO self
function FIFO:New()
-- Inherit everything from BASE class.
local self=BASE:Inherit(self, BASE:New())
self.pointer = 0
self.counter = 0
self.stackbypointer = {}
self.stackbyid = {}
self.uniquecounter = 0
-- Set some string id for output to DCS.log file.
self.lid=string.format("%s (%s) | ", "FiFo", self.version)
self:T(self.lid .."Created.")
return self
end
--- Empty FIFO Stack
-- @param #FIFO self
-- @return #FIFO self
function FIFO:Clear()
self:T(self.lid.."Clear")
self.pointer = 0
self.counter = 0
self.stackbypointer = nil
self.stackbyid = nil
self.stackbypointer = {}
self.stackbyid = {}
self.uniquecounter = 0
return self
end
--- FIFO Push Object to Stack
-- @param #FIFO self
-- @param #table Object
-- @param #string UniqueID (optional) - will default to current pointer + 1. Note - if you intend to use `FIFO:GetIDStackSorted()` keep the UniqueID numerical!
-- @return #FIFO self
function FIFO:Push(Object,UniqueID)
self:T(self.lid.."Push")
self:T({Object,UniqueID})
self.pointer = self.pointer + 1
self.counter = self.counter + 1
local uniID = UniqueID
if not UniqueID then
self.uniquecounter = self.uniquecounter + 1
uniID = self.uniquecounter
end
self.stackbyid[uniID] = { pointer = self.pointer, data = Object, uniqueID = uniID }
self.stackbypointer[self.pointer] = { pointer = self.pointer, data = Object, uniqueID = uniID }
return self
end
--- FIFO Pull Object from Stack
-- @param #FIFO self
-- @return #table Object or nil if stack is empty
function FIFO:Pull()
self:T(self.lid.."Pull")
if self.counter == 0 then return nil end
--local object = self.stackbypointer[self.pointer].data
--self.stackbypointer[self.pointer] = nil
local object = self.stackbypointer[1].data
self.stackbypointer[1] = nil
self.counter = self.counter - 1
--self.pointer = self.pointer - 1
self:Flatten()
return object
end
--- FIFO Pull Object from Stack by Pointer
-- @param #FIFO self
-- @param #number Pointer
-- @return #table Object or nil if stack is empty
function FIFO:PullByPointer(Pointer)
self:T(self.lid.."PullByPointer " .. tostring(Pointer))
if self.counter == 0 then return nil end
local object = self.stackbypointer[Pointer] -- #FIFO.IDEntry
self.stackbypointer[Pointer] = nil
if object then self.stackbyid[object.uniqueID] = nil end
self.counter = self.counter - 1
self:Flatten()
if object then
return object.data
else
return nil
end
end
--- FIFO Read, not Pull, Object from Stack by Pointer
-- @param #FIFO self
-- @param #number Pointer
-- @return #table Object or nil if stack is empty or pointer does not exist
function FIFO:ReadByPointer(Pointer)
self:T(self.lid.."ReadByPointer " .. tostring(Pointer))
if self.counter == 0 or not Pointer or not self.stackbypointer[Pointer] then return nil end
local object = self.stackbypointer[Pointer] -- #FIFO.IDEntry
if object then
return object.data
else
return nil
end
end
--- FIFO Read, not Pull, Object from Stack by UniqueID
-- @param #FIFO self
-- @param #number UniqueID
-- @return #table Object data or nil if stack is empty or ID does not exist
function FIFO:ReadByID(UniqueID)
self:T(self.lid.."ReadByID " .. tostring(UniqueID))
if self.counter == 0 or not UniqueID or not self.stackbyid[UniqueID] then return nil end
local object = self.stackbyid[UniqueID] -- #FIFO.IDEntry
if object then
return object.data
else
return nil
end
end
--- FIFO Pull Object from Stack by UniqueID
-- @param #FIFO self
-- @param #tableUniqueID
-- @return #table Object or nil if stack is empty
function FIFO:PullByID(UniqueID)
self:T(self.lid.."PullByID " .. tostring(UniqueID))
if self.counter == 0 then return nil end
local object = self.stackbyid[UniqueID] -- #FIFO.IDEntry
--self.stackbyid[UniqueID] = nil
if object then
return self:PullByPointer(object.pointer)
else
return nil
end
end
--- FIFO Housekeeping
-- @param #FIFO self
-- @return #FIFO self
function FIFO:Flatten()
self:T(self.lid.."Flatten")
-- rebuild stacks
local pointerstack = {}
local idstack = {}
local counter = 0
for _ID,_entry in pairs(self.stackbypointer) do
counter = counter + 1
pointerstack[counter] = { pointer = counter, data = _entry.data, uniqueID = _entry.uniqueID}
end
for _ID,_entry in pairs(pointerstack) do
idstack[_entry.uniqueID] = { pointer = _entry.pointer , data = _entry.data, uniqueID = _entry.uniqueID}
end
self.stackbypointer = nil
self.stackbypointer = pointerstack
self.stackbyid = nil
self.stackbyid = idstack
self.counter = counter
self.pointer = counter
return self
end
--- FIFO Check Stack is empty
-- @param #FIFO self
-- @return #boolean empty
function FIFO:IsEmpty()
self:T(self.lid.."IsEmpty")
return self.counter == 0 and true or false
end
--- FIFO Get stack size
-- @param #FIFO self
-- @return #number size
function FIFO:GetSize()
self:T(self.lid.."GetSize")
return self.counter
end
--- FIFO Get stack size
-- @param #FIFO self
-- @return #number size
function FIFO:Count()
self:T(self.lid.."Count")
return self.counter
end
--- FIFO Check Stack is NOT empty
-- @param #FIFO self
-- @return #boolean notempty
function FIFO:IsNotEmpty()
self:T(self.lid.."IsNotEmpty")
return not self:IsEmpty()
end
--- FIFO Get the data stack by pointer
-- @param #FIFO self
-- @return #table Table of #FIFO.IDEntry entries
function FIFO:GetPointerStack()
self:T(self.lid.."GetPointerStack")
return self.stackbypointer
end
--- FIFO Check if a certain UniqeID exists
-- @param #FIFO self
-- @return #boolean exists
function FIFO:HasUniqueID(UniqueID)
self:T(self.lid.."HasUniqueID")
if self.stackbyid[UniqueID] ~= nil then
return true
else
return false
end
end
--- FIFO Get the data stack by UniqueID
-- @param #FIFO self
-- @return #table Table of #FIFO.IDEntry entries
function FIFO:GetIDStack()
self:T(self.lid.."GetIDStack")
return self.stackbyid
end
--- FIFO Get table of UniqueIDs sorted smallest to largest
-- @param #FIFO self
-- @return #table Table with index [1] to [n] of UniqueID entries
function FIFO:GetIDStackSorted()
self:T(self.lid.."GetIDStackSorted")
local stack = self:GetIDStack()
local idstack = {}
for _id,_entry in pairs(stack) do
idstack[#idstack+1] = _id
self:T({"pre",_id})
end
local function sortID(a, b)
return a < b
end
table.sort(idstack)
return idstack
end
--- FIFO Get table of data entries
-- @param #FIFO self
-- @return #table Raw table indexed [1] to [n] of object entries - might be empty!
function FIFO:GetDataTable()
self:T(self.lid.."GetDataTable")
local datatable = {}
for _,_entry in pairs(self.stackbypointer) do
datatable[#datatable+1] = _entry.data
end
return datatable
end
--- FIFO Get sorted table of data entries by UniqueIDs (must be numerical UniqueIDs only!)
-- @param #FIFO self
-- @return #table Table indexed [1] to [n] of sorted object entries - might be empty!
function FIFO:GetSortedDataTable()
self:T(self.lid.."GetSortedDataTable")
local datatable = {}
local idtablesorted = self:GetIDStackSorted()
for _,_entry in pairs(idtablesorted) do
datatable[#datatable+1] = self:ReadByID(_entry)
end
return datatable
end
--- Iterate the FIFO and call an iterator function for the given FIFO data, providing the object for each element of the stack and optional parameters.
-- @param #FIFO self
-- @param #function IteratorFunction The function that will be called.
-- @param #table Arg (Optional) Further Arguments of the IteratorFunction.
-- @param #function Function (Optional) A function returning a #boolean true/false. Only if true, the IteratorFunction is called.
-- @param #table FunctionArguments (Optional) Function arguments.
-- @return #FIFO self
function FIFO:ForEach( IteratorFunction, Arg, Function, FunctionArguments )
self:T(self.lid.."ForEach")
local Set = self:GetPointerStack() or {}
Arg = Arg or {}
local function CoRoutine()
local Count = 0
for ObjectID, ObjectData in pairs( Set ) do
local Object = ObjectData.data
self:T( {Object} )
if Function then
if Function( unpack( FunctionArguments or {} ), Object ) == true then
IteratorFunction( Object, unpack( Arg ) )
end
else
IteratorFunction( Object, unpack( Arg ) )
end
Count = Count + 1
end
return true
end
local co = CoRoutine
local function Schedule()
local status, res = co()
self:T( { status, res } )
if status == false then
error( res )
end
if res == false then
return true -- resume next time the loop
end
return false
end
Schedule()
return self
end
--- FIFO Print stacks to dcs.log
-- @param #FIFO self
-- @return #FIFO self
function FIFO:Flush()
self:T(self.lid.."FiFo Flush")
self:I("FIFO Flushing Stack by Pointer")
for _id,_data in pairs (self.stackbypointer) do
local data = _data -- #FIFO.IDEntry
self:I(string.format("Pointer: %s | Entry: Number = %s Data = %s UniqueID = %s",tostring(_id),tostring(data.pointer),tostring(data.data),tostring(data.uniqueID)))
end
self:I("FIFO Flushing Stack by ID")
for _id,_data in pairs (self.stackbyid) do
local data = _data -- #FIFO.IDEntry
self:I(string.format("ID: %s | Entry: Number = %s Data = %s UniqueID = %s",tostring(_id),tostring(data.pointer),tostring(data.data),tostring(data.uniqueID)))
end
self:I("Counter = " .. self.counter)
self:I("Pointer = ".. self.pointer)
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- End FIFO
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- LIFO
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
do
--- **UTILS** - LiFo Stack.
--
-- **Main Features:**
--
-- * Build a simple multi-purpose LiFo (Last-In, First-Out) stack for generic data.
--
-- ===
--
-- ### Author: **applevangelist**
--- LIFO class.
-- @type LIFO
-- @field #string ClassName Name of the class.
-- @field #string lid Class id string for output to DCS log file.
-- @field #string version Version of LiFo
-- @field #number counter
-- @field #number pointer
-- @field #table stackbypointer
-- @field #table stackbyid
-- @extends Core.Base#BASE
---
-- @type LIFO.IDEntry
-- @field #number pointer
-- @field #table data
-- @field #table uniqueID
---
-- @field #LIFO
LIFO = {
ClassName = "LIFO",
lid = "",
version = "0.0.5",
counter = 0,
pointer = 0,
stackbypointer = {},
stackbyid = {}
}
--- Instantiate a new LIFO Stack
-- @param #LIFO self
-- @return #LIFO self
function LIFO:New()
-- Inherit everything from BASE class.
local self=BASE:Inherit(self, BASE:New())
self.pointer = 0
self.counter = 0
self.uniquecounter = 0
self.stackbypointer = {}
self.stackbyid = {}
-- Set some string id for output to DCS.log file.
self.lid=string.format("%s (%s) | ", "LiFo", self.version)
self:T(self.lid .."Created.")
return self
end
--- Empty LIFO Stack
-- @param #LIFO self
-- @return #LIFO self
function LIFO:Clear()
self:T(self.lid.."Clear")
self.pointer = 0
self.counter = 0
self.stackbypointer = nil
self.stackbyid = nil
self.stackbypointer = {}
self.stackbyid = {}
self.uniquecounter = 0
return self
end
--- LIFO Push Object to Stack
-- @param #LIFO self
-- @param #table Object
-- @param #string UniqueID (optional) - will default to current pointer + 1
-- @return #LIFO self
function LIFO:Push(Object,UniqueID)
self:T(self.lid.."Push")
self:T({Object,UniqueID})
self.pointer = self.pointer + 1
self.counter = self.counter + 1
local uniID = UniqueID
if not UniqueID then
self.uniquecounter = self.uniquecounter + 1
uniID = self.uniquecounter
end
self.stackbyid[uniID] = { pointer = self.pointer, data = Object, uniqueID = uniID }
self.stackbypointer[self.pointer] = { pointer = self.pointer, data = Object, uniqueID = uniID }
return self
end
--- LIFO Pull Object from Stack
-- @param #LIFO self
-- @return #table Object or nil if stack is empty
function LIFO:Pull()
self:T(self.lid.."Pull")
if self.counter == 0 then return nil end
local object = self.stackbypointer[self.pointer].data
self.stackbypointer[self.pointer] = nil
--local object = self.stackbypointer[1].data
--self.stackbypointer[1] = nil
self.counter = self.counter - 1
self.pointer = self.pointer - 1
self:Flatten()
return object
end
--- LIFO Pull Object from Stack by Pointer
-- @param #LIFO self
-- @param #number Pointer
-- @return #table Object or nil if stack is empty
function LIFO:PullByPointer(Pointer)
self:T(self.lid.."PullByPointer " .. tostring(Pointer))
if self.counter == 0 then return nil end
local object = self.stackbypointer[Pointer] -- #FIFO.IDEntry
self.stackbypointer[Pointer] = nil
if object then self.stackbyid[object.uniqueID] = nil end
self.counter = self.counter - 1
self:Flatten()
if object then
return object.data
else
return nil
end
end
--- LIFO Read, not Pull, Object from Stack by Pointer
-- @param #LIFO self
-- @param #number Pointer
-- @return #table Object or nil if stack is empty or pointer does not exist
function LIFO:ReadByPointer(Pointer)
self:T(self.lid.."ReadByPointer " .. tostring(Pointer))
if self.counter == 0 or not Pointer or not self.stackbypointer[Pointer] then return nil end
local object = self.stackbypointer[Pointer] -- #LIFO.IDEntry
if object then
return object.data
else
return nil
end
end
--- LIFO Read, not Pull, Object from Stack by UniqueID
-- @param #LIFO self
-- @param #number UniqueID
-- @return #table Object or nil if stack is empty or ID does not exist
function LIFO:ReadByID(UniqueID)
self:T(self.lid.."ReadByID " .. tostring(UniqueID))
if self.counter == 0 or not UniqueID or not self.stackbyid[UniqueID] then return nil end
local object = self.stackbyid[UniqueID] -- #LIFO.IDEntry
if object then
return object.data
else
return nil
end
end
--- LIFO Pull Object from Stack by UniqueID
-- @param #LIFO self
-- @param #tableUniqueID
-- @return #table Object or nil if stack is empty
function LIFO:PullByID(UniqueID)
self:T(self.lid.."PullByID " .. tostring(UniqueID))
if self.counter == 0 then return nil end
local object = self.stackbyid[UniqueID] -- #LIFO.IDEntry
--self.stackbyid[UniqueID] = nil
if object then
return self:PullByPointer(object.pointer)
else
return nil
end
end
--- LIFO Housekeeping
-- @param #LIFO self
-- @return #LIFO self
function LIFO:Flatten()
self:T(self.lid.."Flatten")
-- rebuild stacks
local pointerstack = {}
local idstack = {}
local counter = 0
for _ID,_entry in pairs(self.stackbypointer) do
counter = counter + 1
pointerstack[counter] = { pointer = counter, data = _entry.data, uniqueID = _entry.uniqueID}
end
for _ID,_entry in pairs(pointerstack) do
idstack[_entry.uniqueID] = { pointer = _entry.pointer , data = _entry.data, uniqueID = _entry.uniqueID}
end
self.stackbypointer = nil
self.stackbypointer = pointerstack
self.stackbyid = nil
self.stackbyid = idstack
self.counter = counter
self.pointer = counter
return self
end
--- LIFO Check Stack is empty
-- @param #LIFO self
-- @return #boolean empty
function LIFO:IsEmpty()
self:T(self.lid.."IsEmpty")
return self.counter == 0 and true or false
end
--- LIFO Get stack size
-- @param #LIFO self
-- @return #number size
function LIFO:GetSize()
self:T(self.lid.."GetSize")
return self.counter
end
--- LIFO Get stack size
-- @param #LIFO self
-- @return #number size
function LIFO:Count()
self:T(self.lid.."Count")
return self.counter
end
--- LIFO Check Stack is NOT empty
-- @param #LIFO self
-- @return #boolean notempty
function LIFO:IsNotEmpty()
self:T(self.lid.."IsNotEmpty")
return not self:IsEmpty()
end
--- LIFO Get the data stack by pointer
-- @param #LIFO self
-- @return #table Table of #LIFO.IDEntry entries
function LIFO:GetPointerStack()
self:T(self.lid.."GetPointerStack")
return self.stackbypointer
end
--- LIFO Get the data stack by UniqueID
-- @param #LIFO self
-- @return #table Table of #LIFO.IDEntry entries
function LIFO:GetIDStack()
self:T(self.lid.."GetIDStack")
return self.stackbyid
end
--- LIFO Get table of UniqueIDs sorted smallest to largest
-- @param #LIFO self
-- @return #table Table of #LIFO.IDEntry entries
function LIFO:GetIDStackSorted()
self:T(self.lid.."GetIDStackSorted")
local stack = self:GetIDStack()
local idstack = {}
for _id,_entry in pairs(stack) do
idstack[#idstack+1] = _id
self:T({"pre",_id})
end
local function sortID(a, b)
return a < b
end
table.sort(idstack)
return idstack
end
--- LIFO Check if a certain UniqeID exists
-- @param #LIFO self
-- @return #boolean exists
function LIFO:HasUniqueID(UniqueID)
self:T(self.lid.."HasUniqueID")
return self.stackbyid[UniqueID] and true or false
end
--- LIFO Print stacks to dcs.log
-- @param #LIFO self
-- @return #LIFO self
function LIFO:Flush()
self:T(self.lid.."FiFo Flush")
self:I("LIFO Flushing Stack by Pointer")
for _id,_data in pairs (self.stackbypointer) do
local data = _data -- #LIFO.IDEntry
self:I(string.format("Pointer: %s | Entry: Number = %s Data = %s UniqueID = %s",tostring(_id),tostring(data.pointer),tostring(data.data),tostring(data.uniqueID)))
end
self:I("LIFO Flushing Stack by ID")
for _id,_data in pairs (self.stackbyid) do
local data = _data -- #LIFO.IDEntry
self:I(string.format("ID: %s | Entry: Number = %s Data = %s UniqueID = %s",tostring(_id),tostring(data.pointer),tostring(data.data),tostring(data.uniqueID)))
end
self:I("Counter = " .. self.counter)
self:I("Pointer = ".. self.pointer)
return self
end
--- LIFO Get table of data entries
-- @param #LIFO self
-- @return #table Raw table indexed [1] to [n] of object entries - might be empty!
function LIFO:GetDataTable()
self:T(self.lid.."GetDataTable")
local datatable = {}
for _,_entry in pairs(self.stackbypointer) do
datatable[#datatable+1] = _entry.data
end
return datatable
end
--- LIFO Get sorted table of data entries by UniqueIDs (must be numerical UniqueIDs only!)
-- @param #LIFO self
-- @return #table Table indexed [1] to [n] of sorted object entries - might be empty!
function LIFO:GetSortedDataTable()
self:T(self.lid.."GetSortedDataTable")
local datatable = {}
local idtablesorted = self:GetIDStackSorted()
for _,_entry in pairs(idtablesorted) do
datatable[#datatable+1] = self:ReadByID(_entry)
end
return datatable
end
--- Iterate the LIFO and call an iterator function for the given LIFO data, providing the object for each element of the stack and optional parameters.
-- @param #LIFO self
-- @param #function IteratorFunction The function that will be called.
-- @param #table Arg (Optional) Further Arguments of the IteratorFunction.
-- @param #function Function (Optional) A function returning a #boolean true/false. Only if true, the IteratorFunction is called.
-- @param #table FunctionArguments (Optional) Function arguments.
-- @return #LIFO self
function LIFO:ForEach( IteratorFunction, Arg, Function, FunctionArguments )
self:T(self.lid.."ForEach")
local Set = self:GetPointerStack() or {}
Arg = Arg or {}
local function CoRoutine()
local Count = 0
for ObjectID, ObjectData in pairs( Set ) do
local Object = ObjectData.data
self:T( {Object} )
if Function then
if Function( unpack( FunctionArguments or {} ), Object ) == true then
IteratorFunction( Object, unpack( Arg ) )
end
else
IteratorFunction( Object, unpack( Arg ) )
end
Count = Count + 1
end
return true
end
local co = CoRoutine
local function Schedule()
local status, res = co()
self:T( { status, res } )
if status == false then
error( res )
end
if res == false then
return true -- resume next time the loop
end
return false
end
Schedule()
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- End LIFO
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
end
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
+138 -31
View File
@@ -384,7 +384,7 @@ end
-- So all event listeners will catch the destroy event of this group for each unit in the group. -- So all event listeners will catch the destroy event of this group for each unit in the group.
-- To raise these events, provide the `GenerateEvent` parameter. -- To raise these events, provide the `GenerateEvent` parameter.
-- @param #GROUP self -- @param #GROUP self
-- @param #boolean GenerateEvent If true, a crash or dead event for each unit is generated. If false, if no event is triggered. If nil, a RemoveUnit event is triggered. -- @param #boolean GenerateEvent If true, a crash [AIR] or dead [GROUND] event for each unit is generated. If false, if no event is triggered. If nil, a RemoveUnit event is triggered.
-- @param #number delay Delay in seconds before despawning the group. -- @param #number delay Delay in seconds before despawning the group.
-- @usage -- @usage
-- -- Air unit example: destroy the Helicopter and generate a S_EVENT_CRASH for each unit in the Helicopter group. -- -- Air unit example: destroy the Helicopter and generate a S_EVENT_CRASH for each unit in the Helicopter group.
@@ -622,7 +622,7 @@ function GROUP:GetUnits()
local DCSGroup = self:GetDCSObject() local DCSGroup = self:GetDCSObject()
if DCSGroup then if DCSGroup then
local DCSUnits = DCSGroup:getUnits() local DCSUnits = DCSGroup:getUnits() or {}
local Units = {} local Units = {}
for Index, UnitData in pairs( DCSUnits ) do for Index, UnitData in pairs( DCSUnits ) do
Units[#Units+1] = UNIT:Find( UnitData ) Units[#Units+1] = UNIT:Find( UnitData )
@@ -680,6 +680,30 @@ function GROUP:GetUnit( UnitNumber )
return nil return nil
end end
--- Check if an (air) group is a client or player slot. Information is retrieved from the group template.
-- @param #GROUP self
-- @return #boolean If true, group is associated with a client or player slot.
function GROUP:IsPlayer()
-- Get group.
-- local group=self:GetGroup()
-- Units of template group.
local units=self:GetTemplate().units
-- Get numbers.
for _,unit in pairs(units) do
-- Check if unit name matach and skill is Client or Player.
if unit.name==self:GetName() and (unit.skill=="Client" or unit.skill=="Player") then
return true
end
end
return false
end
--- Returns the DCS Unit with number UnitNumber. --- Returns the DCS Unit with number UnitNumber.
-- If the underlying DCS Unit does not exist, the method will return nil. . -- If the underlying DCS Unit does not exist, the method will return nil. .
-- @param #GROUP self -- @param #GROUP self
@@ -687,11 +711,24 @@ end
-- @return DCS#Unit The DCS Unit. -- @return DCS#Unit The DCS Unit.
function GROUP:GetDCSUnit( UnitNumber ) function GROUP:GetDCSUnit( UnitNumber )
local DCSGroup=self:GetDCSObject() local DCSGroup = self:GetDCSObject()
if DCSGroup then if DCSGroup then
local DCSUnitFound=DCSGroup:getUnit( UnitNumber )
return DCSUnitFound if DCSGroup.getUnit and DCSGroup:getUnit( UnitNumber ) then
return DCSGroup:getUnit( UnitNumber )
else
local UnitFound = nil
-- 2.7.1 dead event bug, return the first alive unit instead
local units = DCSGroup:getUnits() or {}
for _,_unit in pairs(units) do
if _unit and _unit:isExist() then
return _unit
end
end
end
end end
return nil return nil
@@ -765,8 +802,7 @@ end
--- Returns the average velocity Vec3 vector. --- Returns the average velocity Vec3 vector.
-- @param Wrapper.Group#GROUP self -- @param Wrapper.Group#GROUP self
-- @return DCS#Vec3 The velocity Vec3 vector -- @return DCS#Vec3 The velocity Vec3 vector or `#nil` if the GROUP is not existing or alive.
-- @return #nil The GROUP is not existing or alive.
function GROUP:GetVelocityVec3() function GROUP:GetVelocityVec3()
self:F2( self.GroupName ) self:F2( self.GroupName )
@@ -797,11 +833,19 @@ function GROUP:GetVelocityVec3()
return nil return nil
end end
--- Returns the average group altitude in meters.
-- @param Wrapper.Group#GROUP self
-- @param #boolean FromGround Measure from the ground or from sea level (ASL). Provide **true** for measuring from the ground (AGL). **false** or **nil** if you measure from sea level.
-- @return #number The altitude of the group or nil if is not existing or alive.
function GROUP:GetAltitude(FromGround)
self:F2( self.GroupName )
return self:GetHeight(FromGround)
end
--- Returns the average group height in meters. --- Returns the average group height in meters.
-- @param Wrapper.Group#GROUP self -- @param Wrapper.Group#GROUP self
-- @param #boolean FromGround Measure from the ground or from sea level. Provide **true** for measuring from the ground. **false** or **nil** if you measure from sea level. -- @param #boolean FromGround Measure from the ground or from sea level (ASL). Provide **true** for measuring from the ground (AGL). **false** or **nil** if you measure from sea level.
-- @return DCS#Vec3 The height of the group or nil if is not existing or alive. -- @return #number The height of the group or nil if is not existing or alive.
function GROUP:GetHeight( FromGround ) function GROUP:GetHeight( FromGround )
self:F2( self.GroupName ) self:F2( self.GroupName )
@@ -901,6 +945,24 @@ function GROUP:GetTypeName()
return nil return nil
end end
--- [AIRPLANE] Get the NATO reporting name (platform, e.g. "Flanker") of a GROUP (note - first unit the group). "Bogey" if not found. Currently airplanes only!
--@param #GROUP self
--@return #string NatoReportingName or "Bogey" if unknown.
function GROUP:GetNatoReportingName()
self:F2( self.GroupName )
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local GroupTypeName = DCSGroup:getUnit(1):getTypeName()
self:T3( GroupTypeName )
return UTILS.GetReportingName(GroupTypeName)
end
return "Bogey"
end
--- Gets the player name of the group. --- Gets the player name of the group.
-- @param #GROUP self -- @param #GROUP self
-- @return #string The player name of the group. -- @return #string The player name of the group.
@@ -994,24 +1056,32 @@ end
-- @return Core.Point#COORDINATE The COORDINATE of the GROUP. -- @return Core.Point#COORDINATE The COORDINATE of the GROUP.
function GROUP:GetCoordinate() function GROUP:GetCoordinate()
local FirstUnit = self:GetUnit(1) local Units = self:GetUnits() or {}
if FirstUnit then for _,_unit in pairs(Units) do
local FirstUnitCoordinate = FirstUnit:GetCoordinate() local FirstUnit = _unit -- Wrapper.Unit#UNIT
return FirstUnitCoordinate
if FirstUnit then
local FirstUnitCoordinate = FirstUnit:GetCoordinate()
if FirstUnitCoordinate then
local Heading = self:GetHeading()
FirstUnitCoordinate.Heading = Heading
return FirstUnitCoordinate
end
end
end end
BASE:E( { "Cannot GetCoordinate", Group = self, Alive = self:IsAlive() } ) BASE:E( { "Cannot GetCoordinate", Group = self, Alive = self:IsAlive() } )
return nil
end end
--- Returns a random @{DCS#Vec3} vector (point in 3D of the UNIT within the mission) within a range around the first UNIT of the GROUP. --- Returns a random @{DCS#Vec3} vector (point in 3D of the UNIT within the mission) within a range around the first UNIT of the GROUP.
-- @param #GROUP self -- @param #GROUP self
-- @param #number Radius -- @param #number Radius Radius in meters.
-- @return DCS#Vec3 The random 3D point vector around the first UNIT of the GROUP. -- @return DCS#Vec3 The random 3D point vector around the first UNIT of the GROUP or #nil The GROUP is invalid or empty.
-- @return #nil The GROUP is invalid or empty
-- @usage -- @usage
-- -- If Radius is ignored, returns the DCS#Vec3 of first UNIT of the GROUP -- -- If Radius is ignored, returns the DCS#Vec3 of first UNIT of the GROUP
function GROUP:GetRandomVec3(Radius) function GROUP:GetRandomVec3(Radius)
@@ -1032,18 +1102,19 @@ end
--- Returns the mean heading of every UNIT in the GROUP in degrees --- Returns the mean heading of every UNIT in the GROUP in degrees
-- @param #GROUP self -- @param #GROUP self
-- @return #number mean heading of the GROUP -- @return #number Mean heading of the GROUP in degrees or #nil The first UNIT is not existing or alive.
-- @return #nil The first UNIT is not existing or alive.
function GROUP:GetHeading() function GROUP:GetHeading()
self:F2(self.GroupName) self:F2(self.GroupName)
self:F2(self.GroupName)
local GroupSize = self:GetSize() local GroupSize = self:GetSize()
local HeadingAccumulator = 0 local HeadingAccumulator = 0
local n=0 local n=0
local Units = self:GetUnits()
if GroupSize then if GroupSize then
for i = 1, GroupSize do for _,unit in pairs(Units) do
local unit=self:GetUnit(i)
if unit and unit:IsAlive() then if unit and unit:IsAlive() then
HeadingAccumulator = HeadingAccumulator + unit:GetHeading() HeadingAccumulator = HeadingAccumulator + unit:GetHeading()
n=n+1 n=n+1
@@ -1061,8 +1132,8 @@ end
--- Return the fuel state and unit reference for the unit with the least --- Return the fuel state and unit reference for the unit with the least
-- amount of fuel in the group. -- amount of fuel in the group.
-- @param #GROUP self -- @param #GROUP self
-- @return #number The fuel state of the unit with the least amount of fuel -- @return #number The fuel state of the unit with the least amount of fuel.
-- @return #Unit reference to #Unit object for further processing -- @return Wrapper.Unit#UNIT reference to #Unit object for further processing.
function GROUP:GetFuelMin() function GROUP:GetFuelMin()
self:F3(self.ControllableName) self:F3(self.ControllableName)
@@ -1104,7 +1175,7 @@ function GROUP:GetFuelAvg()
local TotalFuel = 0 local TotalFuel = 0
for UnitID, UnitData in pairs( self:GetUnits() ) do for UnitID, UnitData in pairs( self:GetUnits() ) do
local Unit = UnitData -- Wrapper.Unit#UNIT local Unit = UnitData -- Wrapper.Unit#UNIT
local UnitFuel = Unit:GetFuel() local UnitFuel = Unit:GetFuel() or 0
self:F( { Fuel = UnitFuel } ) self:F( { Fuel = UnitFuel } )
TotalFuel = TotalFuel + UnitFuel TotalFuel = TotalFuel + UnitFuel
end end
@@ -1181,13 +1252,14 @@ function GROUP:IsInZone( Zone )
for UnitID, UnitData in pairs(self:GetUnits()) do for UnitID, UnitData in pairs(self:GetUnits()) do
local Unit = UnitData -- Wrapper.Unit#UNIT local Unit = UnitData -- Wrapper.Unit#UNIT
-- Get 2D vector. That's all we need for the zone check. local vec2 = nil
local vec2=Unit:GetVec2() if Unit then
-- Get 2D vector. That's all we need for the zone check.
vec2=Unit:GetVec2()
end
if Zone:IsVec2InZone(vec2) then if vec2 and Zone:IsVec2InZone(vec2) then
return true -- At least one unit is in the zone. That is enough. return true -- At least one unit is in the zone. That is enough.
else
-- This one is not but another could be.
end end
end end
@@ -2609,6 +2681,41 @@ function GROUP:GetSkill()
return skill return skill
end end
--- Get the unit in the group with the highest threat level, which is still alive.
-- @param #GROUP self
-- @return Wrapper.Unit#UNIT The most dangerous unit in the group.
-- @return #number Threat level of the unit.
function GROUP:GetHighestThreat()
-- Get units of the group.
local units=self:GetUnits()
if units then
local threat=nil ; local maxtl=0
for _,_unit in pairs(units or {}) do
local unit=_unit --Wrapper.Unit#UNIT
if unit and unit:IsAlive() then
-- Threat level of group.
local tl=unit:GetThreatLevel()
-- Check if greater the current threat.
if tl>maxtl then
maxtl=tl
threat=unit
end
end
end
return threat, maxtl
end
return nil, nil
end
--do -- Smoke --do -- Smoke
-- --
----- Signal a flare at the position of the GROUP. ----- Signal a flare at the position of the GROUP.
@@ -176,7 +176,7 @@ function IDENTIFIABLE:GetCoalitionName()
if DCSIdentifiable then if DCSIdentifiable then
-- Get coaliton ID. -- Get coalition ID.
local IdentifiableCoalition = DCSIdentifiable:getCoalition() local IdentifiableCoalition = DCSIdentifiable:getCoalition()
self:T3( IdentifiableCoalition ) self:T3( IdentifiableCoalition )
+170 -183
View File
@@ -15,7 +15,6 @@
-- @module Wrapper.Marker -- @module Wrapper.Marker
-- @image Wrapper_Marker.png -- @image Wrapper_Marker.png
--- Marker class. --- Marker class.
-- @type MARKER -- @type MARKER
-- @field #string ClassName Name of the class. -- @field #string ClassName Name of the class.
@@ -24,7 +23,7 @@
-- @field #number mid Marker ID. -- @field #number mid Marker ID.
-- @field Core.Point#COORDINATE coordinate Coordinate of the mark. -- @field Core.Point#COORDINATE coordinate Coordinate of the mark.
-- @field #string text Text displayed in the mark panel. -- @field #string text Text displayed in the mark panel.
-- @field #string message Message dispayed when the mark is added. -- @field #string message Message displayed when the mark is added.
-- @field #boolean readonly Marker is read-only. -- @field #boolean readonly Marker is read-only.
-- @field #number coalition Coalition to which the marker is displayed. -- @field #number coalition Coalition to which the marker is displayed.
-- @extends Core.Fsm#FSM -- @extends Core.Fsm#FSM
@@ -42,29 +41,29 @@
-- # Create a Marker -- # Create a Marker
-- --
-- -- Create a MARKER object at Batumi with a trivial text. -- -- Create a MARKER object at Batumi with a trivial text.
-- local Coordinate=AIRBASE:FindByName("Batumi"):GetCoordinate() -- local Coordinate = AIRBASE:FindByName( "Batumi" ):GetCoordinate()
-- mymarker=MARKER:New(Coordinate, "I am Batumi Airfield") -- mymarker = MARKER:New( Coordinate, "I am Batumi Airfield" )
-- --
-- Now this does **not** show the marker yet. We still need to specifiy to whom it is shown. There are several options, i.e. -- Now this does **not** show the marker yet. We still need to specify to whom it is shown. There are several options, i.e.
-- show the marker to everyone, to a speficic coaliton only, or only to a specific group. -- show the marker to everyone, to a specific coalition only, or only to a specific group.
-- --
-- ## For Everyone -- ## For Everyone
-- --
-- If the marker should be visible to everyone, you can use the :ToAll() function. -- If the marker should be visible to everyone, you can use the :ToAll() function.
-- --
-- mymarker=MARKER:New(Coordinate, "I am Batumi Airfield"):ToAll() -- mymarker = MARKER:New( Coordinate, "I am Batumi Airfield" ):ToAll()
-- --
-- ## For a Coaliton -- ## For a Coalition
-- --
-- If the maker should be visible to a specific coalition, you can use the :ToCoalition() function. -- If the maker should be visible to a specific coalition, you can use the :ToCoalition() function.
-- --
-- mymarker=MARKER:New(Coordinate, "I am Batumi Airfield"):ToCoaliton(coaliton.side.BLUE) -- mymarker = MARKER:New( Coordinate , "I am Batumi Airfield" ):ToCoalition( coalition.side.BLUE )
-- --
-- ### To Blue Coaliton -- ### To Blue Coalition
-- --
-- ### To Red Coalition -- ### To Red Coalition
-- --
-- This would show the marker only to the Blue coaliton. -- This would show the marker only to the Blue coalition.
-- --
-- ## For a Group -- ## For a Group
-- --
@@ -76,28 +75,28 @@
-- --
-- The marker text and coordinate can be updated easily as shown below. -- The marker text and coordinate can be updated easily as shown below.
-- --
-- However, note that **updateing involves to remove and recreate the marker if either text or its coordinate is changed**. -- However, note that **updating involves to remove and recreate the marker if either text or its coordinate is changed**.
-- *This is a DCS scripting engine limitation.* -- *This is a DCS scripting engine limitation.*
-- --
-- ## Update Text -- ## Update Text
-- --
-- If you created a marker "mymarker" as shown above, you can update the dispayed test by -- If you created a marker "mymarker" as shown above, you can update the displayed test by
-- --
-- mymarker:UpdateText("I am the new text at Batumi") -- mymarker:UpdateText( "I am the new text at Batumi" )
-- --
-- The update can also be delayed by, e.g. 90 seconds, using -- The update can also be delayed by, e.g. 90 seconds, using
-- --
-- mymarker:UpdateText("I am the new text at Batumi", 90) -- mymarker:UpdateText( "I am the new text at Batumi", 90 )
-- --
-- ## Update Coordinate -- ## Update Coordinate
-- --
-- If you created a marker "mymarker" as shown above, you can update its coordinate on the F10 map by -- If you created a marker "mymarker" as shown above, you can update its coordinate on the F10 map by
-- --
-- mymarker:UpdateCoordinate(NewCoordinate) -- mymarker:UpdateCoordinate( NewCoordinate )
-- --
-- The update can also be delayed by, e.g. 60 seconds, using -- The update can also be delayed by, e.g. 60 seconds, using
-- --
-- mymarker:UpdateCoordinate(NewCoordinate, 60) -- mymarker:UpdateCoordinate( NewCoordinate , 60 )
-- --
-- # Retrieve Data -- # Retrieve Data
-- --
@@ -105,18 +104,18 @@
-- --
-- ## Text -- ## Text
-- --
-- local text=mymarker:GetText() -- local text =mymarker:GetText()
-- env.info("Marker Text = " .. text) -- env.info( "Marker Text = " .. text )
-- --
-- ## Coordinate -- ## Coordinate
-- --
-- local Coordinate=mymarker:GetCoordinate() -- local Coordinate = mymarker:GetCoordinate()
-- env.info("Marker Coordinate LL DSM = " .. Coordinate:ToStringLLDMS()) -- env.info( "Marker Coordinate LL DSM = " .. Coordinate:ToStringLLDMS() )
-- --
-- --
-- # FSM Events -- # FSM Events
-- --
-- Moose creates addditonal events, so called FSM event, when markers are added, changed, removed, and text or the coordianteis updated. -- Moose creates additional events, so called FSM event, when markers are added, changed, removed, and text or the coordinate is updated.
-- --
-- These events can be captured and used for processing via OnAfter functions as shown below. -- These events can be captured and used for processing via OnAfter functions as shown below.
-- --
@@ -133,26 +132,25 @@
-- --
-- # Examples -- # Examples
-- --
--
-- @field #MARKER -- @field #MARKER
MARKER = { MARKER = {
ClassName = "MARKER", ClassName = "MARKER",
Debug = false, Debug = false,
lid = nil, lid = nil,
mid = nil, mid = nil,
coordinate = nil, coordinate = nil,
text = nil, text = nil,
message = nil, message = nil,
readonly = nil, readonly = nil,
coalition = nil, coalition = nil,
} }
--- Marker ID. Running number. --- Marker ID. Running number.
_MARKERID=0 _MARKERID = 0
--- Marker class version. --- Marker class version.
-- @field #string version -- @field #string version
MARKER.version="0.1.0" MARKER.version = "0.1.0"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
@@ -172,38 +170,38 @@ MARKER.version="0.1.0"
-- @param Core.Point#COORDINATE Coordinate Coordinate where to place the marker. -- @param Core.Point#COORDINATE Coordinate Coordinate where to place the marker.
-- @param #string Text Text displayed on the mark panel. -- @param #string Text Text displayed on the mark panel.
-- @return #MARKER self -- @return #MARKER self
function MARKER:New(Coordinate, Text) function MARKER:New( Coordinate, Text )
-- Inherit everything from FSM class. -- Inherit everything from FSM class.
local self=BASE:Inherit(self, FSM:New()) -- #MARKER local self = BASE:Inherit( self, FSM:New() ) -- #MARKER
self.coordinate=Coordinate self.coordinate = Coordinate
self.text=Text self.text = Text
-- Defaults -- Defaults
self.readonly=false self.readonly = false
self.message="" self.message = ""
-- New marker ID. This is not the one of the actual marker. -- New marker ID. This is not the one of the actual marker.
_MARKERID=_MARKERID+1 _MARKERID = _MARKERID + 1
self.myid=_MARKERID self.myid = _MARKERID
-- Log ID. -- Log ID.
self.lid=string.format("Marker #%d | ", self.myid) self.lid = string.format( "Marker #%d | ", self.myid )
-- Start State. -- Start State.
self:SetStartState("Invisible") self:SetStartState( "Invisible" )
-- Add FSM transitions. -- Add FSM transitions.
-- From State --> Event --> To State -- From State --> Event --> To State
self:AddTransition("Invisible", "Added", "Visible") -- Marker was added. self:AddTransition( "Invisible", "Added", "Visible" ) -- Marker was added.
self:AddTransition("Visible", "Removed", "Invisible") -- Marker was removed. self:AddTransition( "Visible", "Removed", "Invisible" ) -- Marker was removed.
self:AddTransition("*", "Changed", "*") -- Marker was changed. self:AddTransition( "*", "Changed", "*" ) -- Marker was changed.
self:AddTransition("*", "TextUpdate", "*") -- Text updated. self:AddTransition( "*", "TextUpdate", "*" ) -- Text updated.
self:AddTransition("*", "CoordUpdate", "*") -- Coordinates updated. self:AddTransition( "*", "CoordUpdate", "*" ) -- Coordinates updated.
--- Triggers the FSM event "Added". --- Triggers the FSM event "Added".
-- @function [parent=#MARKER] Added -- @function [parent=#MARKER] Added
@@ -223,7 +221,6 @@ function MARKER:New(Coordinate, Text)
-- @param #string To To state. -- @param #string To To state.
-- @param Core.Event#EVENTDATA EventData Event data table. -- @param Core.Event#EVENTDATA EventData Event data table.
--- Triggers the FSM event "Removed". --- Triggers the FSM event "Removed".
-- @function [parent=#MARKER] Removed -- @function [parent=#MARKER] Removed
-- @param #MARKER self -- @param #MARKER self
@@ -242,7 +239,6 @@ function MARKER:New(Coordinate, Text)
-- @param #string To To state. -- @param #string To To state.
-- @param Core.Event#EVENTDATA EventData Event data table. -- @param Core.Event#EVENTDATA EventData Event data table.
--- Triggers the FSM event "Changed". --- Triggers the FSM event "Changed".
-- @function [parent=#MARKER] Changed -- @function [parent=#MARKER] Changed
-- @param #MARKER self -- @param #MARKER self
@@ -261,7 +257,6 @@ function MARKER:New(Coordinate, Text)
-- @param #string To To state. -- @param #string To To state.
-- @param Core.Event#EVENTDATA EventData Event data table. -- @param Core.Event#EVENTDATA EventData Event data table.
--- Triggers the FSM event "TextUpdate". --- Triggers the FSM event "TextUpdate".
-- @function [parent=#MARKER] TextUpdate -- @function [parent=#MARKER] TextUpdate
-- @param #MARKER self -- @param #MARKER self
@@ -280,7 +275,6 @@ function MARKER:New(Coordinate, Text)
-- @param #string To To state. -- @param #string To To state.
-- @param #string Text The new text. -- @param #string Text The new text.
--- Triggers the FSM event "CoordUpdate". --- Triggers the FSM event "CoordUpdate".
-- @function [parent=#MARKER] CoordUpdate -- @function [parent=#MARKER] CoordUpdate
-- @param #MARKER self -- @param #MARKER self
@@ -299,11 +293,10 @@ function MARKER:New(Coordinate, Text)
-- @param #string To To state. -- @param #string To To state.
-- @param Core.Point#COORDINATE Coordinate The updated Coordinate. -- @param Core.Point#COORDINATE Coordinate The updated Coordinate.
-- Handle events. -- Handle events.
self:HandleEvent(EVENTS.MarkAdded) self:HandleEvent( EVENTS.MarkAdded )
self:HandleEvent(EVENTS.MarkRemoved) self:HandleEvent( EVENTS.MarkRemoved )
self:HandleEvent(EVENTS.MarkChange) self:HandleEvent( EVENTS.MarkChange )
return self return self
end end
@@ -317,7 +310,7 @@ end
-- @return #MARKER self -- @return #MARKER self
function MARKER:ReadOnly() function MARKER:ReadOnly()
self.readonly=true self.readonly = true
return self return self
end end
@@ -326,9 +319,9 @@ end
-- @param #MARKER self -- @param #MARKER self
-- @param #string Text Message displayed when the marker is added. -- @param #string Text Message displayed when the marker is added.
-- @return #MARKER self -- @return #MARKER self
function MARKER:Message(Text) function MARKER:Message( Text )
self.message=Text or "" self.message = Text or ""
return self return self
end end
@@ -337,28 +330,28 @@ end
-- @param #MARKER self -- @param #MARKER self
-- @param #number Delay (Optional) Delay in seconds, before the marker is created. -- @param #number Delay (Optional) Delay in seconds, before the marker is created.
-- @return #MARKER self -- @return #MARKER self
function MARKER:ToAll(Delay) function MARKER:ToAll( Delay )
if Delay and Delay>0 then if Delay and Delay > 0 then
self:ScheduleOnce(Delay, MARKER.ToAll, self) self:ScheduleOnce( Delay, MARKER.ToAll, self )
else else
self.toall=true self.toall = true
self.tocoaliton=nil self.tocoalition = nil
self.coalition=nil self.coalition = nil
self.togroup=nil self.togroup = nil
self.groupname=nil self.groupname = nil
self.groupid=nil self.groupid = nil
-- First remove an existing mark. -- First remove an existing mark.
if self.shown then if self.shown then
self:Remove() self:Remove()
end end
self.mid=UTILS.GetMarkID() self.mid = UTILS.GetMarkID()
-- Call DCS function. -- Call DCS function.
trigger.action.markToAll(self.mid, self.text, self.coordinate:GetVec3(), self.readonly, self.message) trigger.action.markToAll( self.mid, self.text, self.coordinate:GetVec3(), self.readonly, self.message )
end end
@@ -367,32 +360,32 @@ end
--- Place marker visible for a specific coalition only. --- Place marker visible for a specific coalition only.
-- @param #MARKER self -- @param #MARKER self
-- @param #number Coalition Coalition 1=Red, 2=Blue, 0=Neutral. See `coaliton.side.RED`. -- @param #number Coalition Coalition 1=Red, 2=Blue, 0=Neutral. See `coalition.side.RED`.
-- @param #number Delay (Optional) Delay in seconds, before the marker is created. -- @param #number Delay (Optional) Delay in seconds, before the marker is created.
-- @return #MARKER self -- @return #MARKER self
function MARKER:ToCoalition(Coalition, Delay) function MARKER:ToCoalition( Coalition, Delay )
if Delay and Delay>0 then if Delay and Delay > 0 then
self:ScheduleOnce(Delay, MARKER.ToCoalition, self, Coalition) self:ScheduleOnce( Delay, MARKER.ToCoalition, self, Coalition )
else else
self.coalition=Coalition self.coalition = Coalition
self.tocoaliton=true self.tocoalition = true
self.toall=false self.toall = false
self.togroup=false self.togroup = false
self.groupname=nil self.groupname = nil
self.groupid=nil self.groupid = nil
-- First remove an existing mark. -- First remove an existing mark.
if self.shown then if self.shown then
self:Remove() self:Remove()
end end
self.mid=UTILS.GetMarkID() self.mid = UTILS.GetMarkID()
-- Call DCS function. -- Call DCS function.
trigger.action.markToCoalition(self.mid, self.text, self.coordinate:GetVec3(), self.coalition, self.readonly, self.message) trigger.action.markToCoalition( self.mid, self.text, self.coordinate:GetVec3(), self.coalition, self.readonly, self.message )
end end
@@ -403,8 +396,8 @@ end
-- @param #MARKER self -- @param #MARKER self
-- @param #number Delay (Optional) Delay in seconds, before the marker is created. -- @param #number Delay (Optional) Delay in seconds, before the marker is created.
-- @return #MARKER self -- @return #MARKER self
function MARKER:ToBlue(Delay) function MARKER:ToBlue( Delay )
self:ToCoalition(coalition.side.BLUE, Delay) self:ToCoalition( coalition.side.BLUE, Delay )
return self return self
end end
@@ -412,8 +405,8 @@ end
-- @param #MARKER self -- @param #MARKER self
-- @param #number Delay (Optional) Delay in seconds, before the marker is created. -- @param #number Delay (Optional) Delay in seconds, before the marker is created.
-- @return #MARKER self -- @return #MARKER self
function MARKER:ToRed(Delay) function MARKER:ToRed( Delay )
self:ToCoalition(coalition.side.RED, Delay) self:ToCoalition( coalition.side.RED, Delay )
return self return self
end end
@@ -421,51 +414,50 @@ end
-- @param #MARKER self -- @param #MARKER self
-- @param #number Delay (Optional) Delay in seconds, before the marker is created. -- @param #number Delay (Optional) Delay in seconds, before the marker is created.
-- @return #MARKER self -- @return #MARKER self
function MARKER:ToNeutral(Delay) function MARKER:ToNeutral( Delay )
self:ToCoalition(coalition.side.NEUTRAL, Delay) self:ToCoalition( coalition.side.NEUTRAL, Delay )
return self return self
end end
--- Place marker visible for a specific group only. --- Place marker visible for a specific group only.
-- @param #MARKER self -- @param #MARKER self
-- @param Wrapper.Group#GROUP Group The group to which the marker is displayed. -- @param Wrapper.Group#GROUP Group The group to which the marker is displayed.
-- @param #number Delay (Optional) Delay in seconds, before the marker is created. -- @param #number Delay (Optional) Delay in seconds, before the marker is created.
-- @return #MARKER self -- @return #MARKER self
function MARKER:ToGroup(Group, Delay) function MARKER:ToGroup( Group, Delay )
if Delay and Delay>0 then if Delay and Delay > 0 then
self:ScheduleOnce(Delay, MARKER.ToGroup, self, Group) self:ScheduleOnce( Delay, MARKER.ToGroup, self, Group )
else else
-- Check if group exists. -- Check if group exists.
if Group and Group:IsAlive()~=nil then if Group and Group:IsAlive() ~= nil then
self.groupid=Group:GetID() self.groupid = Group:GetID()
if self.groupid then if self.groupid then
self.groupname=Group:GetName() self.groupname = Group:GetName()
self.togroup=true self.togroup = true
self.tocoaliton=nil self.tocoalition = nil
self.coalition=nil self.coalition = nil
self.toall=nil self.toall = nil
-- First remove an existing mark. -- First remove an existing mark.
if self.shown then if self.shown then
self:Remove() self:Remove()
end end
self.mid=UTILS.GetMarkID() self.mid = UTILS.GetMarkID()
-- Call DCS function. -- Call DCS function.
trigger.action.markToGroup(self.mid, self.text, self.coordinate:GetVec3(), self.groupid, self.readonly, self.message) trigger.action.markToGroup( self.mid, self.text, self.coordinate:GetVec3(), self.groupid, self.readonly, self.message )
end end
else else
--TODO: Warning! -- TODO: Warning!
end end
end end
@@ -478,17 +470,17 @@ end
-- @param #string Text Updated text. -- @param #string Text Updated text.
-- @param #number Delay (Optional) Delay in seconds, before the marker is created. -- @param #number Delay (Optional) Delay in seconds, before the marker is created.
-- @return #MARKER self -- @return #MARKER self
function MARKER:UpdateText(Text, Delay) function MARKER:UpdateText( Text, Delay )
if Delay and Delay>0 then if Delay and Delay > 0 then
self:ScheduleOnce(Delay, MARKER.UpdateText, self, Text) self:ScheduleOnce( Delay, MARKER.UpdateText, self, Text )
else else
self.text=tostring(Text) self.text = tostring( Text )
self:Refresh() self:Refresh()
self:TextUpdate(tostring(Text)) self:TextUpdate( tostring( Text ) )
end end
@@ -500,17 +492,17 @@ end
-- @param Core.Point#COORDINATE Coordinate The new coordinate. -- @param Core.Point#COORDINATE Coordinate The new coordinate.
-- @param #number Delay (Optional) Delay in seconds, before the marker is created. -- @param #number Delay (Optional) Delay in seconds, before the marker is created.
-- @return #MARKER self -- @return #MARKER self
function MARKER:UpdateCoordinate(Coordinate, Delay) function MARKER:UpdateCoordinate( Coordinate, Delay )
if Delay and Delay>0 then if Delay and Delay > 0 then
self:ScheduleOnce(Delay, MARKER.UpdateCoordinate, self, Coordinate) self:ScheduleOnce( Delay, MARKER.UpdateCoordinate, self, Coordinate )
else else
self.coordinate=Coordinate self.coordinate = Coordinate
self:Refresh() self:Refresh()
self:CoordUpdate(Coordinate) self:CoordUpdate( Coordinate )
end end
@@ -521,28 +513,28 @@ end
-- @param #MARKER self -- @param #MARKER self
-- @param #number Delay (Optional) Delay in seconds, before the marker is created. -- @param #number Delay (Optional) Delay in seconds, before the marker is created.
-- @return #MARKER self -- @return #MARKER self
function MARKER:Refresh(Delay) function MARKER:Refresh( Delay )
if Delay and Delay>0 then if Delay and Delay > 0 then
self:ScheduleOnce(Delay, MARKER.Refresh, self) self:ScheduleOnce( Delay, MARKER.Refresh, self )
else else
if self.toall then if self.toall then
self:ToAll() self:ToAll()
elseif self.tocoaliton then elseif self.tocoalition then
self:ToCoalition(self.coalition) self:ToCoalition( self.coalition )
elseif self.togroup then elseif self.togroup then
local group=GROUP:FindByName(self.groupname) local group = GROUP:FindByName( self.groupname )
self:ToGroup(group) self:ToGroup( group )
else else
self:E(self.lid.."ERROR: unknown To in :Refresh()!") self:E( self.lid .. "ERROR: unknown To in :Refresh()!" )
end end
end end
@@ -554,16 +546,16 @@ end
-- @param #MARKER self -- @param #MARKER self
-- @param #number Delay (Optional) Delay in seconds, before the marker is removed. -- @param #number Delay (Optional) Delay in seconds, before the marker is removed.
-- @return #MARKER self -- @return #MARKER self
function MARKER:Remove(Delay) function MARKER:Remove( Delay )
if Delay and Delay>0 then if Delay and Delay > 0 then
self:ScheduleOnce(Delay, MARKER.Remove, self) self:ScheduleOnce( Delay, MARKER.Remove, self )
else else
if self.shown then if self.shown then
-- Call DCS function. -- Call DCS function.
trigger.action.removeMark(self.mid) trigger.action.removeMark( self.mid )
end end
@@ -590,24 +582,23 @@ end
-- @param #MARKER self -- @param #MARKER self
-- @param #string Text Marker text. Default is an empty sting "". -- @param #string Text Marker text. Default is an empty sting "".
-- @return #MARKER self -- @return #MARKER self
function MARKER:SetText(Text) function MARKER:SetText( Text )
self.text=Text and tostring(Text) or "" self.text = Text and tostring( Text ) or ""
return self return self
end end
--- Check if marker is currently visible on the F10 map. --- Check if marker is currently visible on the F10 map.
-- @param #MARKER self -- @param #MARKER self
-- @return #boolean True if the marker is currently visible. -- @return #boolean True if the marker is currently visible.
function MARKER:IsVisible() function MARKER:IsVisible()
return self:Is("Visible") return self:Is( "Visible" )
end end
--- Check if marker is currently invisible on the F10 map. --- Check if marker is currently invisible on the F10 map.
-- @param #MARKER self -- @param #MARKER self
-- @return -- @return
function MARKER:IsInvisible() function MARKER:IsInvisible()
return self:Is("Invisible") return self:Is( "Invisible" )
end end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -617,19 +608,19 @@ end
--- Event function when a MARKER is added. --- Event function when a MARKER is added.
-- @param #MARKER self -- @param #MARKER self
-- @param Core.Event#EVENTDATA EventData -- @param Core.Event#EVENTDATA EventData
function MARKER:OnEventMarkAdded(EventData) function MARKER:OnEventMarkAdded( EventData )
if EventData and EventData.MarkID then if EventData and EventData.MarkID then
local MarkID=EventData.MarkID local MarkID = EventData.MarkID
self:T3(self.lid..string.format("Captured event MarkAdded for Mark ID=%s", tostring(MarkID))) self:T3( self.lid .. string.format( "Captured event MarkAdded for Mark ID=%s", tostring( MarkID ) ) )
if MarkID==self.mid then if MarkID == self.mid then
self.shown=true self.shown = true
self:Added(EventData) self:Added( EventData )
end end
@@ -640,19 +631,19 @@ end
--- Event function when a MARKER is removed. --- Event function when a MARKER is removed.
-- @param #MARKER self -- @param #MARKER self
-- @param Core.Event#EVENTDATA EventData -- @param Core.Event#EVENTDATA EventData
function MARKER:OnEventMarkRemoved(EventData) function MARKER:OnEventMarkRemoved( EventData )
if EventData and EventData.MarkID then if EventData and EventData.MarkID then
local MarkID=EventData.MarkID local MarkID = EventData.MarkID
self:T3(self.lid..string.format("Captured event MarkAdded for Mark ID=%s", tostring(MarkID))) self:T3( self.lid .. string.format( "Captured event MarkAdded for Mark ID=%s", tostring( MarkID ) ) )
if MarkID==self.mid then if MarkID == self.mid then
self.shown=false self.shown = false
self:Removed(EventData) self:Removed( EventData )
end end
@@ -663,19 +654,19 @@ end
--- Event function when a MARKER changed. --- Event function when a MARKER changed.
-- @param #MARKER self -- @param #MARKER self
-- @param Core.Event#EVENTDATA EventData -- @param Core.Event#EVENTDATA EventData
function MARKER:OnEventMarkChange(EventData) function MARKER:OnEventMarkChange( EventData )
if EventData and EventData.MarkID then if EventData and EventData.MarkID then
local MarkID=EventData.MarkID local MarkID = EventData.MarkID
self:T3(self.lid..string.format("Captured event MarkChange for Mark ID=%s", tostring(MarkID))) self:T3( self.lid .. string.format( "Captured event MarkChange for Mark ID=%s", tostring( MarkID ) ) )
if MarkID==self.mid then if MarkID == self.mid then
self:Changed(EventData) self:Changed( EventData )
self:TextChanged(tostring(EventData.MarkText)) self:TextChanged( tostring( EventData.MarkText ) )
end end
@@ -693,17 +684,17 @@ end
-- @param #string Event Event. -- @param #string Event Event.
-- @param #string To To state. -- @param #string To To state.
-- @param Core.Event#EVENTDATA EventData Event data table. -- @param Core.Event#EVENTDATA EventData Event data table.
function MARKER:onafterAdded(From, Event, To, EventData) function MARKER:onafterAdded( From, Event, To, EventData )
-- Debug info. -- Debug info.
local text=string.format("Captured event MarkAdded for myself:\n") local text = string.format( "Captured event MarkAdded for myself:\n" )
text=text..string.format("Marker ID = %s\n", tostring(EventData.MarkID)) text = text .. string.format( "Marker ID = %s\n", tostring( EventData.MarkID ) )
text=text..string.format("Coalition = %s\n", tostring(EventData.MarkCoalition)) text = text .. string.format( "Coalition = %s\n", tostring( EventData.MarkCoalition ) )
text=text..string.format("Group ID = %s\n", tostring(EventData.MarkGroupID)) text = text .. string.format( "Group ID = %s\n", tostring( EventData.MarkGroupID ) )
text=text..string.format("Initiator = %s\n", EventData.IniUnit and EventData.IniUnit:GetName() or "Nobody") text = text .. string.format( "Initiator = %s\n", EventData.IniUnit and EventData.IniUnit:GetName() or "Nobody" )
text=text..string.format("Coordinate = %s\n", EventData.MarkCoordinate and EventData.MarkCoordinate:ToStringLLDMS() or "Nowhere") text = text .. string.format( "Coordinate = %s\n", EventData.MarkCoordinate and EventData.MarkCoordinate:ToStringLLDMS() or "Nowhere" )
text=text..string.format("Text: \n%s", tostring(EventData.MarkText)) text = text .. string.format( "Text: \n%s", tostring( EventData.MarkText ) )
self:T2(self.lid..text) self:T2( self.lid .. text )
end end
@@ -713,17 +704,17 @@ end
-- @param #string Event Event. -- @param #string Event Event.
-- @param #string To To state. -- @param #string To To state.
-- @param Core.Event#EVENTDATA EventData Event data table. -- @param Core.Event#EVENTDATA EventData Event data table.
function MARKER:onafterRemoved(From, Event, To, EventData) function MARKER:onafterRemoved( From, Event, To, EventData )
-- Debug info. -- Debug info.
local text=string.format("Captured event MarkRemoved for myself:\n") local text = string.format( "Captured event MarkRemoved for myself:\n" )
text=text..string.format("Marker ID = %s\n", tostring(EventData.MarkID)) text = text .. string.format( "Marker ID = %s\n", tostring( EventData.MarkID ) )
text=text..string.format("Coalition = %s\n", tostring(EventData.MarkCoalition)) text = text .. string.format( "Coalition = %s\n", tostring( EventData.MarkCoalition ) )
text=text..string.format("Group ID = %s\n", tostring(EventData.MarkGroupID)) text = text .. string.format( "Group ID = %s\n", tostring( EventData.MarkGroupID ) )
text=text..string.format("Initiator = %s\n", EventData.IniUnit and EventData.IniUnit:GetName() or "Nobody") text = text .. string.format( "Initiator = %s\n", EventData.IniUnit and EventData.IniUnit:GetName() or "Nobody" )
text=text..string.format("Coordinate = %s\n", EventData.MarkCoordinate and EventData.MarkCoordinate:ToStringLLDMS() or "Nowhere") text = text .. string.format( "Coordinate = %s\n", EventData.MarkCoordinate and EventData.MarkCoordinate:ToStringLLDMS() or "Nowhere" )
text=text..string.format("Text: \n%s", tostring(EventData.MarkText)) text = text .. string.format( "Text: \n%s", tostring( EventData.MarkText ) )
self:T2(self.lid..text) self:T2( self.lid .. text )
end end
@@ -733,17 +724,17 @@ end
-- @param #string Event Event. -- @param #string Event Event.
-- @param #string To To state. -- @param #string To To state.
-- @param Core.Event#EVENTDATA EventData Event data table. -- @param Core.Event#EVENTDATA EventData Event data table.
function MARKER:onafterChanged(From, Event, To, EventData) function MARKER:onafterChanged( From, Event, To, EventData )
-- Debug info. -- Debug info.
local text=string.format("Captured event MarkChange for myself:\n") local text = string.format( "Captured event MarkChange for myself:\n" )
text=text..string.format("Marker ID = %s\n", tostring(EventData.MarkID)) text = text .. string.format( "Marker ID = %s\n", tostring( EventData.MarkID ) )
text=text..string.format("Coalition = %s\n", tostring(EventData.MarkCoalition)) text = text .. string.format( "Coalition = %s\n", tostring( EventData.MarkCoalition ) )
text=text..string.format("Group ID = %s\n", tostring(EventData.MarkGroupID)) text = text .. string.format( "Group ID = %s\n", tostring( EventData.MarkGroupID ) )
text=text..string.format("Initiator = %s\n", EventData.IniUnit and EventData.IniUnit:GetName() or "Nobody") text = text .. string.format( "Initiator = %s\n", EventData.IniUnit and EventData.IniUnit:GetName() or "Nobody" )
text=text..string.format("Coordinate = %s\n", EventData.MarkCoordinate and EventData.MarkCoordinate:ToStringLLDMS() or "Nowhere") text = text .. string.format( "Coordinate = %s\n", EventData.MarkCoordinate and EventData.MarkCoordinate:ToStringLLDMS() or "Nowhere" )
text=text..string.format("Text: \n%s", tostring(EventData.MarkText)) text = text .. string.format( "Text: \n%s", tostring( EventData.MarkText ) )
self:T2(self.lid..text) self:T2( self.lid .. text )
end end
@@ -753,9 +744,9 @@ end
-- @param #string Event Event. -- @param #string Event Event.
-- @param #string To To state. -- @param #string To To state.
-- @param #string Text The updated text, displayed in the mark panel. -- @param #string Text The updated text, displayed in the mark panel.
function MARKER:onafterTextUpdate(From, Event, To, Text) function MARKER:onafterTextUpdate( From, Event, To, Text )
self:T(self.lid..string.format("New Marker Text:\n%s", Text)) self:T( self.lid .. string.format( "New Marker Text:\n%s", Text ) )
end end
@@ -765,12 +756,8 @@ end
-- @param #string Event Event. -- @param #string Event Event.
-- @param #string To To state. -- @param #string To To state.
-- @param Core.Point#COORDINATE Coordinate The updated coordinates. -- @param Core.Point#COORDINATE Coordinate The updated coordinates.
function MARKER:onafterCoordUpdate(From, Event, To, Coordinate) function MARKER:onafterCoordUpdate( From, Event, To, Coordinate )
self:T(self.lid..string.format("New Marker Coordinate in LL DMS: %s", Coordinate:ToStringLLDMS())) self:T( self.lid .. string.format( "New Marker Coordinate in LL DMS: %s", Coordinate:ToStringLLDMS() ) )
end end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -382,7 +382,8 @@ function POSITIONABLE:GetCoordinate()
local PositionableVec3 = self:GetVec3() local PositionableVec3 = self:GetVec3()
local coord = COORDINATE:NewFromVec3( PositionableVec3 ) local coord = COORDINATE:NewFromVec3( PositionableVec3 )
local heading = self:GetHeading()
coord.Heading = heading
-- Return a new coordinate object. -- Return a new coordinate object.
return coord return coord
@@ -536,7 +537,7 @@ function POSITIONABLE:GetBoundingRadius( MinDist )
return nil return nil
end end
--- Returns the altitude of the POSITIONABLE. --- Returns the altitude above sea level of the POSITIONABLE.
-- @param Wrapper.Positionable#POSITIONABLE self -- @param Wrapper.Positionable#POSITIONABLE self
-- @return DCS#Distance The altitude of the POSITIONABLE. -- @return DCS#Distance The altitude of the POSITIONABLE.
-- @return #nil The POSITIONABLE is not existing or alive. -- @return #nil The POSITIONABLE is not existing or alive.
@@ -778,7 +779,7 @@ function POSITIONABLE:GetRelativeVelocity( Positionable )
return UTILS.VecNorm( vtot ) return UTILS.VecNorm( vtot )
end end
--- Returns the POSITIONABLE height in meters. --- Returns the POSITIONABLE height above sea level in meters.
-- @param Wrapper.Positionable#POSITIONABLE self -- @param Wrapper.Positionable#POSITIONABLE self
-- @return DCS#Vec3 The height of the POSITIONABLE in meters. -- @return DCS#Vec3 The height of the POSITIONABLE in meters.
-- @return #nil The POSITIONABLE is not existing or alive. -- @return #nil The POSITIONABLE is not existing or alive.
@@ -1176,6 +1177,30 @@ function POSITIONABLE:MessageToClient( Message, Duration, Client, Name )
return nil return nil
end end
--- Send a message to a @{Wrapper.Unit}.
-- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message.
-- @param #POSITIONABLE self
-- @param #string Message The message text
-- @param DCS#Duration Duration The duration of the message.
-- @param Wrapper.Unit#UNIT MessageUnit The UNIT object receiving the message.
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
function POSITIONABLE:MessageToUnit( Message, Duration, MessageUnit, Name )
self:F2( { Message, Duration } )
local DCSObject = self:GetDCSObject()
if DCSObject then
if DCSObject:isExist() then
if MessageUnit:IsAlive() then
self:GetMessage( Message, Duration, Name ):ToUnit( MessageUnit )
else
BASE:E( { "Message not sent to Unit; Unit is not alive...", Message = Message, MessageUnit = MessageUnit } )
end
else
BASE:E( { "Message not sent to Unit; Positionable is not alive ...", Message = Message, Positionable = self, MessageUnit = MessageUnit } )
end
end
end
--- Send a message to a @{Wrapper.Group}. --- Send a message to a @{Wrapper.Group}.
-- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message. -- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message.
-- @param #POSITIONABLE self -- @param #POSITIONABLE self
@@ -1249,6 +1274,30 @@ function POSITIONABLE:MessageToSetGroup( Message, Duration, MessageSetGroup, Nam
return nil return nil
end end
--- Send a message to a @{Core.Set#SET_UNIT}.
-- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message.
-- @param #POSITIONABLE self
-- @param #string Message The message text
-- @param DCS#Duration Duration The duration of the message.
-- @param Core.Set#SET_UNIT MessageSetUnit The SET_UNIT collection receiving the message.
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
function POSITIONABLE:MessageToSetUnit( Message, Duration, MessageSetUnit, Name )
self:F2( { Message, Duration } )
local DCSObject = self:GetDCSObject()
if DCSObject then
if DCSObject:isExist() then
MessageSetUnit:ForEachUnit(
function( MessageGroup )
self:GetMessage( Message, Duration, Name ):ToUnit( MessageGroup )
end
)
end
end
return nil
end
--- Send a message to the players in the @{Wrapper.Group}. --- Send a message to the players in the @{Wrapper.Group}.
-- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message. -- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message.
-- @param #POSITIONABLE self -- @param #POSITIONABLE self
@@ -1551,7 +1600,11 @@ do -- Cargo
["Ural-4320 APA-5D"] = 10, ["Ural-4320 APA-5D"] = 10,
["Ural-4320T"] = 14, ["Ural-4320T"] = 14,
["ZBD04A"] = 7, -- new by kappa ["ZBD04A"] = 7, -- new by kappa
["VAB_Mephisto"] = 8 -- new by Apple ["VAB_Mephisto"] = 8, -- new by Apple
["tt_KORD"] = 6, -- 2.7.1 HL/TT
["tt_DSHK"] = 6,
["HL_KORD"] = 6,
["HL_DSHK"] = 6,
} }
local CargoBayWeightLimit = (Weights[Desc.typeName] or 0) * 95 local CargoBayWeightLimit = (Weights[Desc.typeName] or 0) * 95
+36 -25
View File
@@ -10,8 +10,11 @@
-- --
-- @module Wrapper.Static -- @module Wrapper.Static
-- @image Wrapper_Static.JPG -- @image Wrapper_Static.JPG
--- @type STATIC --- @type STATIC
-- @extends Wrapper.Positionable#POSITIONABLE -- @extends Wrapper.Positionable#POSITIONABLE
--- Wrapper class to handle Static objects. --- Wrapper class to handle Static objects.
-- --
-- Note that Statics are almost the same as Units, but they don't have a controller. -- Note that Statics are almost the same as Units, but they don't have a controller.
@@ -37,10 +40,13 @@
-- --
-- * @{#STATIC.FindByName}(): Find a STATIC instance from the _DATABASE object using a DCS Static name. -- * @{#STATIC.FindByName}(): Find a STATIC instance from the _DATABASE object using a DCS Static name.
-- --
-- IMPORTANT: ONE SHOULD NEVER SANITIZE these STATIC OBJECT REFERENCES! (make the STATIC object references nil). -- IMPORTANT: ONE SHOULD NEVER SANATIZE these STATIC OBJECT REFERENCES! (make the STATIC object references nil).
-- --
-- @field #STATIC -- @field #STATIC
STATIC = { ClassName = "STATIC" } STATIC = {
ClassName = "STATIC",
}
--- Register a static object. --- Register a static object.
-- @param #STATIC self -- @param #STATIC self
@@ -52,6 +58,7 @@ function STATIC:Register( StaticName )
return self return self
end end
--- Finds a STATIC from the _DATABASE using a DCSStatic object. --- Finds a STATIC from the _DATABASE using a DCSStatic object.
-- @param #STATIC self -- @param #STATIC self
-- @param DCS#StaticObject DCSStatic An existing DCS Static object reference. -- @param DCS#StaticObject DCSStatic An existing DCS Static object reference.
@@ -90,9 +97,8 @@ end
--- Destroys the STATIC. --- Destroys the STATIC.
-- @param #STATIC self -- @param #STATIC self
-- @param #boolean GenerateEvent (Optional) true to generate a crash or dead event, false to not generate any event. `nil` (default) creates a remove event. -- @param #boolean GenerateEvent (Optional) true if you want to generate a crash or dead event for the static.
-- @return #nil The DCS StaticObject is not existing or alive. -- @return #nil The DCS StaticObject is not existing or alive.
--
-- @usage -- @usage
-- -- Air static example: destroy the static Helicopter and generate a S_EVENT_CRASH. -- -- Air static example: destroy the static Helicopter and generate a S_EVENT_CRASH.
-- Helicopter = STATIC:FindByName( "Helicopter" ) -- Helicopter = STATIC:FindByName( "Helicopter" )
@@ -111,7 +117,7 @@ end
-- @usage -- @usage
-- -- Destroy without event generation example. -- -- Destroy without event generation example.
-- Ship = STATIC:FindByName( "Boat" ) -- Ship = STATIC:FindByName( "Boat" )
-- Ship:Destroy( false ) -- Don't generate any event upon destruction. -- Ship:Destroy( false ) -- Don't generate an event upon destruction.
-- --
function STATIC:Destroy( GenerateEvent ) function STATIC:Destroy( GenerateEvent )
self:F2( self.ObjectName ) self:F2( self.ObjectName )
@@ -142,6 +148,7 @@ function STATIC:Destroy( GenerateEvent )
return nil return nil
end end
--- Get DCS object of static of static. --- Get DCS object of static of static.
-- @param #STATIC self -- @param #STATIC self
-- @return DCS static object -- @return DCS static object
@@ -173,6 +180,7 @@ function STATIC:GetUnits()
return nil return nil
end end
--- Get threat level of static. --- Get threat level of static.
-- @param #STATIC self -- @param #STATIC self
-- @return #number Threat level 1. -- @return #number Threat level 1.
@@ -186,15 +194,15 @@ end
-- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static. -- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static.
-- @param #number Heading The heading of the static respawn in degrees. Default is 0 deg. -- @param #number Heading The heading of the static respawn in degrees. Default is 0 deg.
-- @param #number Delay Delay in seconds before the static is spawned. -- @param #number Delay Delay in seconds before the static is spawned.
function STATIC:SpawnAt( Coordinate, Heading, Delay ) function STATIC:SpawnAt(Coordinate, Heading, Delay)
Heading = Heading or 0 Heading=Heading or 0
if Delay and Delay > 0 then if Delay and Delay>0 then
SCHEDULER:New( nil, self.SpawnAt, { self, Coordinate, Heading }, Delay ) SCHEDULER:New(nil, self.SpawnAt, {self, Coordinate, Heading}, Delay)
else else
local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName ) local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName)
SpawnStatic:SpawnFromPointVec2( Coordinate, Heading, self.StaticName ) SpawnStatic:SpawnFromPointVec2( Coordinate, Heading, self.StaticName )
@@ -203,45 +211,48 @@ function STATIC:SpawnAt( Coordinate, Heading, Delay )
return self return self
end end
--- Respawn the @{Wrapper.Unit} at the same location with the same properties. --- Respawn the @{Wrapper.Unit} at the same location with the same properties.
-- This is useful to respawn a cargo after it has been destroyed. -- This is useful to respawn a cargo after it has been destroyed.
-- @param #STATIC self -- @param #STATIC self
-- @param DCS#country.id CountryID (Optional) The country ID used for spawning the new static. Default is same as currently. -- @param DCS#country.id CountryID (Optional) The country ID used for spawning the new static. Default is same as currently.
-- @param #number Delay (Optional) Delay in seconds before static is respawned. Default now. -- @param #number Delay (Optional) Delay in seconds before static is respawned. Default now.
function STATIC:ReSpawn( CountryID, Delay ) function STATIC:ReSpawn(CountryID, Delay)
if Delay and Delay > 0 then if Delay and Delay>0 then
SCHEDULER:New( nil, self.ReSpawn, { self, CountryID }, Delay ) SCHEDULER:New(nil, self.ReSpawn, {self, CountryID}, Delay)
else else
CountryID = CountryID or self:GetCountry() CountryID=CountryID or self:GetCountry()
local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName, CountryID ) local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName, CountryID)
SpawnStatic:Spawn( nil, self.StaticName ) SpawnStatic:Spawn(nil, self.StaticName)
end end
return self return self
end end
--- Respawn the @{Wrapper.Unit} at a defined Coordinate with an optional heading. --- Respawn the @{Wrapper.Unit} at a defined Coordinate with an optional heading.
-- @param #STATIC self -- @param #STATIC self
-- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static. -- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static.
-- @param #number Heading (Optional) The heading of the static respawn in degrees. Default is the current heading. -- @param #number Heading (Optional) The heading of the static respawn in degrees. Default the current heading.
-- @param #number Delay (Optional) Delay in seconds before static is respawned. Default is now. -- @param #number Delay (Optional) Delay in seconds before static is respawned. Default now.
function STATIC:ReSpawnAt( Coordinate, Heading, Delay ) function STATIC:ReSpawnAt(Coordinate, Heading, Delay)
-- Heading=Heading or 0 --Heading=Heading or 0
if Delay and Delay > 0 then if Delay and Delay>0 then
SCHEDULER:New( nil, self.ReSpawnAt, { self, Coordinate, Heading }, Delay ) SCHEDULER:New(nil, self.ReSpawnAt, {self, Coordinate, Heading}, Delay)
else else
local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName, self:GetCountry() )
SpawnStatic:SpawnFromCoordinate( Coordinate, Heading, self.StaticName ) local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName, self:GetCountry())
SpawnStatic:SpawnFromCoordinate(Coordinate, Heading, self.StaticName)
end end
return self return self
end end
+133 -21
View File
@@ -88,8 +88,9 @@
-- --
-- @field #UNIT UNIT -- @field #UNIT UNIT
UNIT = { UNIT = {
ClassName="UNIT", ClassName="UNIT",
UnitName=nil, UnitName=nil,
GroupName=nil,
} }
@@ -110,11 +111,20 @@ UNIT = {
function UNIT:Register( UnitName ) function UNIT:Register( UnitName )
-- Inherit CONTROLLABLE. -- Inherit CONTROLLABLE.
local self = BASE:Inherit( self, CONTROLLABLE:New( UnitName ) ) local self = BASE:Inherit( self, CONTROLLABLE:New( UnitName ) ) --#UNIT
-- Set unit name. -- Set unit name.
self.UnitName = UnitName self.UnitName = UnitName
local unit=Unit.getByName(self.UnitName)
if unit then
local group = unit:getGroup()
if group then
self.GroupName=group:getName()
end
end
-- Set event prio. -- Set event prio.
self:SetEventPriority( 3 ) self:SetEventPriority( 3 )
@@ -168,6 +178,29 @@ function UNIT:GetDCSObject()
return nil return nil
end end
--- Returns the unit altitude above sea level in meters.
-- @param Wrapper.Unit#UNIT self
-- @param #boolean FromGround Measure from the ground or from sea level (ASL). Provide **true** for measuring from the ground (AGL). **false** or **nil** if you measure from sea level.
-- @return #number The height of the group or nil if is not existing or alive.
function UNIT:GetAltitude(FromGround)
local DCSUnit = Unit.getByName( self.UnitName )
if DCSUnit then
local altitude = 0
local point = DCSUnit:getPoint() --DCS#Vec3
altitude = point.y
if FromGround then
local land = land.getHeight( { x = point.x, y = point.z } ) or 0
altitude = altitude - land
end
return altitude
end
return nil
end
--- Respawn the @{Wrapper.Unit} using a (tweaked) template of the parent Group. --- Respawn the @{Wrapper.Unit} using a (tweaked) template of the parent Group.
-- --
-- This function will: -- This function will:
@@ -260,6 +293,8 @@ function UNIT:ReSpawnAt( Coordinate, Heading )
_DATABASE:Spawn( SpawnGroupTemplate ) _DATABASE:Spawn( SpawnGroupTemplate )
end end
--- Returns if the unit is activated. --- Returns if the unit is activated.
-- @param #UNIT self -- @param #UNIT self
-- @return #boolean `true` if Unit is activated. `nil` The DCS Unit is not existing or alive. -- @return #boolean `true` if Unit is activated. `nil` The DCS Unit is not existing or alive.
@@ -296,6 +331,8 @@ function UNIT:IsAlive()
return nil return nil
end end
--- Returns the Unit's callsign - the localized string. --- Returns the Unit's callsign - the localized string.
-- @param #UNIT self -- @param #UNIT self
-- @return #string The Callsign of the Unit. -- @return #string The Callsign of the Unit.
@@ -401,6 +438,17 @@ function UNIT:GetClient()
return nil return nil
end end
--- [AIRPLANE] Get the NATO reporting name of a UNIT. Currently airplanes only!
--@param #UNIT self
--@return #string NatoReportingName or "Bogey" if unknown.
function UNIT:GetNatoReportingName()
local typename = self:GetTypeName()
return UTILS.GetReportingName(typename)
end
--- Returns the unit's number in the group. --- Returns the unit's number in the group.
-- The number is the same number the unit has in ME. -- The number is the same number the unit has in ME.
-- It may not be changed during the mission. -- It may not be changed during the mission.
@@ -517,6 +565,63 @@ function UNIT:IsTanker()
return tanker, system return tanker, system
end end
--- Check if the unit can supply ammo. Currently, we have
--
-- * M 818
-- * Ural-375
-- * ZIL-135
--
-- This list needs to be extended, if DCS adds other units capable of supplying ammo.
--
-- @param #UNIT self
-- @return #boolean If `true`, unit can supply ammo.
function UNIT:IsAmmoSupply()
-- Type name is the only thing we can check. There is no attribute (Sep. 2021) which would tell us.
local typename=self:GetTypeName()
if typename=="M 818" then
-- Blue ammo truck.
return true
elseif typename=="Ural-375" then
-- Red ammo truck.
return true
elseif typename=="ZIL-135" then
-- Red ammo truck. Checked that it can also provide ammo.
return true
end
return false
end
--- Check if the unit can supply fuel. Currently, we have
--
-- * M978 HEMTT Tanker
-- * ATMZ-5
-- * ATMZ-10
-- * ATZ-5
--
-- This list needs to be extended, if DCS adds other units capable of supplying fuel.
--
-- @param #UNIT self
-- @return #boolean If `true`, unit can supply fuel.
function UNIT:IsFuelSupply()
-- Type name is the only thing we can check. There is no attribute (Sep. 2021) which would tell us.
local typename=self:GetTypeName()
if typename=="M978 HEMTT Tanker" then
return true
elseif typename=="ATMZ-5" then
return true
elseif typename=="ATMZ-10" then
return true
elseif typename=="ATZ-5" then
return true
end
return false
end
--- Returns the unit's group if it exist and nil otherwise. --- Returns the unit's group if it exist and nil otherwise.
-- @param Wrapper.Unit#UNIT self -- @param Wrapper.Unit#UNIT self
@@ -544,14 +649,14 @@ end
-- @return #string The name of the DCS Unit. -- @return #string The name of the DCS Unit.
-- @return #nil The DCS Unit is not existing or alive. -- @return #nil The DCS Unit is not existing or alive.
function UNIT:GetPrefix() function UNIT:GetPrefix()
self:F2( self.UnitName ) self:F2( self.UnitName )
local DCSUnit = self:GetDCSObject() local DCSUnit = self:GetDCSObject()
if DCSUnit then if DCSUnit then
local UnitPrefix = string.match( self.UnitName, ".*#" ):sub( 1, -2 ) local UnitPrefix = string.match( self.UnitName, ".*#" ):sub( 1, -2 )
self:T3( UnitPrefix ) self:T3( UnitPrefix )
return UnitPrefix return UnitPrefix
end end
return nil return nil
@@ -676,8 +781,6 @@ function UNIT:GetAmmunition()
return nammo, nshells, nrockets, nbombs, nmissiles return nammo, nshells, nrockets, nbombs, nmissiles
end end
--- Returns the unit sensors. --- Returns the unit sensors.
-- @param #UNIT self -- @param #UNIT self
-- @return DCS#Unit.Sensors Table of sensors. -- @return DCS#Unit.Sensors Table of sensors.
@@ -954,6 +1057,7 @@ end
-- @return #string Some text. -- @return #string Some text.
function UNIT:GetThreatLevel() function UNIT:GetThreatLevel()
local ThreatLevel = 0 local ThreatLevel = 0
local ThreatText = "" local ThreatText = ""
@@ -979,6 +1083,7 @@ function UNIT:GetThreatLevel()
"LR SAMs" "LR SAMs"
} }
if Attributes["LR SAM"] then ThreatLevel = 10 if Attributes["LR SAM"] then ThreatLevel = 10
elseif Attributes["MR SAM"] then ThreatLevel = 9 elseif Attributes["MR SAM"] then ThreatLevel = 9
elseif Attributes["SR SAM"] and elseif Attributes["SR SAM"] and
@@ -992,7 +1097,7 @@ function UNIT:GetThreatLevel()
elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and
not Attributes["ATGM"] then ThreatLevel = 3 not Attributes["ATGM"] then ThreatLevel = 3
elseif Attributes["Old Tanks"] or Attributes["APC"] or Attributes["Artillery"] then ThreatLevel = 2 elseif Attributes["Old Tanks"] or Attributes["APC"] or Attributes["Artillery"] then ThreatLevel = 2
elseif Attributes["Infantry"] then ThreatLevel = 1 elseif Attributes["Infantry"] or Attributes["EWR"] then ThreatLevel = 1
end end
ThreatText = ThreatLevels[ThreatLevel+1] ThreatText = ThreatLevels[ThreatLevel+1]
@@ -1014,6 +1119,7 @@ function UNIT:GetThreatLevel()
"Fighter" "Fighter"
} }
if Attributes["Fighters"] then ThreatLevel = 10 if Attributes["Fighters"] then ThreatLevel = 10
elseif Attributes["Multirole fighters"] then ThreatLevel = 9 elseif Attributes["Multirole fighters"] then ThreatLevel = 9
elseif Attributes["Battleplanes"] then ThreatLevel = 8 elseif Attributes["Battleplanes"] then ThreatLevel = 8
@@ -1111,26 +1217,32 @@ end
-- @return true If the other DCS Unit is within the radius of the 2D point of the DCS Unit. -- @return true If the other DCS Unit is within the radius of the 2D point of the DCS Unit.
-- @return #nil The DCS Unit is not existing or alive. -- @return #nil The DCS Unit is not existing or alive.
function UNIT:OtherUnitInRadius( AwaitUnit, Radius ) function UNIT:OtherUnitInRadius( AwaitUnit, Radius )
self:F2( { self.UnitName, AwaitUnit.UnitName, Radius } ) self:F2( { self.UnitName, AwaitUnit.UnitName, Radius } )
local DCSUnit = self:GetDCSObject() local DCSUnit = self:GetDCSObject()
if DCSUnit then if DCSUnit then
local UnitVec3 = self:GetVec3() local UnitVec3 = self:GetVec3()
local AwaitUnitVec3 = AwaitUnit:GetVec3() local AwaitUnitVec3 = AwaitUnit:GetVec3()
if (((UnitVec3.x - AwaitUnitVec3.x)^2 + (UnitVec3.z - AwaitUnitVec3.z)^2)^0.5 <= Radius) then if (((UnitVec3.x - AwaitUnitVec3.x)^2 + (UnitVec3.z - AwaitUnitVec3.z)^2)^0.5 <= Radius) then
self:T3( "true" ) self:T3( "true" )
return true return true
else else
self:T3( "false" ) self:T3( "false" )
return false return false
end end
end end
return nil return nil
end end
--- Returns if the unit is a friendly unit. --- Returns if the unit is a friendly unit.
-- @param #UNIT self -- @param #UNIT self
-- @return #boolean IsFriendly evaluation result. -- @return #boolean IsFriendly evaluation result.
+1
View File
@@ -5,6 +5,7 @@ Utilities/Enums.lua
Utilities/Profiler.lua Utilities/Profiler.lua
Utilities/Templates.lua Utilities/Templates.lua
Utilities/STTS.lua Utilities/STTS.lua
Utilities/FiFo.lua
Core/Base.lua Core/Base.lua
Core/Beacon.lua Core/Beacon.lua
+3 -2
View File
@@ -61,9 +61,10 @@ Documentation on the MOOSE class hierarchy, usage guides and background informat
## [MOOSE Youtube Channel](https://www.youtube.com/channel/UCjrA9j5LQoWsG4SpS8i79Qg) ## [MOOSE Youtube Tutorials](https://youtube.com/playlist?list=PLLkY2GByvtC2ME0Q9wrKRDE6qnXJYV3iT)
MOOSE has a [broadcast and training channel on YouTube](https://www.youtube.com/channel/UCjrA9j5LQoWsG4SpS8i79Qg) with various channels that you can watch. Pene has kindly created a [tutorial series for MOOSE](https://youtube.com/playlist?list=PLLkY2GByvtC2ME0Q9wrKRDE6qnXJYV3iT)
with various videos that you can watch.