Compare commits

...

603 Commits

Author SHA1 Message Date
Frank 6eb1d78ecb Merge branch 'FF/Ops' into FF/OpsDev 2025-11-23 20:47:49 +01:00
Frank 88a8a3318f Merge branch 'develop' into FF/Ops 2025-11-23 20:42:00 +01:00
Frank 60b561449b Merge branch 'master' into develop 2025-11-23 20:40:48 +01:00
Frank 88396b5a5e Merge pull request #2452 from FlightControl-Master/FF/MasterDev
AIRBASE name enums
2025-11-23 20:39:19 +01:00
Frank a0c5466f7f Update Airbase.lua
- Updated TheChannel map airbase enums
- Fixed some Umlaute in the Germany map
2025-11-23 20:35:12 +01:00
Thomas b62b218257 Merge pull request #2451 from FlightControl-Master/master
Merge from master
2025-11-23 19:39:38 +01:00
Thomas 7a3c23527f Merge pull request #2450 from shaji-Dev/master
[FIXED] SCENERY:GetVec2() and SCENERY:GetVec3() return nil if not registered with SceneryZone
2025-11-23 19:38:32 +01:00
Frank 2f002703ee Update Airbase.lua
- Updated Syria map airbase enums
2025-11-23 18:45:09 +01:00
Frank b0c6ed1d59 Update Airbase.lua
- Updated Sinai map airbase enums
2025-11-23 17:40:05 +01:00
Frank 950e97b6d7 Update Airbase.lua
- Updated Persian Gulf map airbase enums
2025-11-23 17:35:38 +01:00
Frank 6d54e5a47f Update Airbase.lua
- Updated Normand map airbase enums
2025-11-23 17:28:31 +01:00
Shafik 87d65123ba [FIXED] SCENERY:GetVec2() and SCENERY:GetVec3() return nil if not registered with Scenery Zone 2025-11-23 18:25:47 +02:00
Frank 35c8803f8b Update Airbase.lua
- Update Nevada map airbase enums
2025-11-23 17:19:18 +01:00
Frank d9c8542993 Update Airbase.lua
- Update of Mariana Island (WWII) airbase enums
2025-11-23 17:15:30 +01:00
Shafik d7a6809fec [FIXED] SCENERY:GetVec2() and SCENERY:GetVec3() return nil if not registered with Scenery Zone 2025-11-23 18:14:45 +02:00
Frank f3c38a4288 Update Airbase.lua
- Updated Kola map airbase enums
2025-11-23 16:46:23 +01:00
Frank 626b03c93a Update Airbase.lua
- Updated Iraq map airbase enums
2025-11-23 16:44:40 +01:00
Frank cf5c55209a Update Airbase
- Updated South Atlantic airbase enums
2025-11-23 16:36:20 +01:00
Frank 19921414cf Update Airbase.lua
- Update Caucasus Airbase Enums
2025-11-23 16:20:49 +01:00
Frank c4de2b6990 Update Airbase.lua
- Afghanistan Airbase Enums updated
2025-11-23 16:10:40 +01:00
Frank 7907e4b830 Airbase
- Added new Germany CW airbases
- Added function in UTILS to create airbase enums
2025-11-23 16:08:28 +01:00
Applevangelist 9e55118d3e #AUFTRAG/#OPS/#RESCUEHELO - make the NewRescueHelo AUFTRAG use Ops.Rescuehelo as basis. #2199 2025-11-22 16:18:28 +01:00
Thomas a5076eddba Merge pull request #2449 from FlightControl-Master/master
Merge from master
2025-11-22 15:24:16 +01:00
Thomas bc24d75ad7 Merge pull request #2448 from FlightControl-Master/master
Merge from master
2025-11-22 15:21:28 +01:00
Thomas af11264841 Merge pull request #2447 from shaji-Dev/develop
[FIXED] attempt to index local 'asset' (a nil value)
2025-11-22 15:20:29 +01:00
Applevangelist 5955c2d61f #VARIOUS Small fixes 2025-11-22 15:17:26 +01:00
Shafik 95e2bf4a21 [FIXED] attempt to index local 'asset' (a nil value) 2025-11-22 13:37:22 +02:00
Shafik 6f22534b8d Merge branch 'develop' of https://github.com/shaji-Dev/MOOSE into develop 2025-11-22 13:35:11 +02:00
Frank ed091c4f3e Merge branch 'develop' into FF/Ops 2025-11-22 11:21:25 +01:00
Applevangelist 5d1123e7df xx 2025-11-16 16:54:04 +01:00
Applevangelist 5929b7b0ed #COORDINATE
* Remove POINT_VEC2/3 classses
2025-11-16 16:53:59 +01:00
Frank ff86cc9f11 Flightplan 2025-11-15 21:39:30 +01:00
Applevangelist 0de95b1830 #image path 2025-11-15 16:58:56 +01:00
Applevangelist ccaf926c6d #FORMATION - move useable #FORMATION class from AI into functional
#AIRBOSS - add F-14A Early model support
2025-11-15 16:49:20 +01:00
Applevangelist 26fbae8672 #AIRBOSS Added early F-14A 2025-11-15 16:48:05 +01:00
Frank 1e78c6dea2 Merge branch 'FF/Ops' into FF/OpsDev 2025-11-15 10:14:15 +01:00
Frank d37cd1613d Merge branch 'develop' into FF/Ops 2025-11-14 23:44:40 +01:00
Frank 96a923b5a8 Merge branch 'master' into FF/Ops 2025-11-14 23:44:25 +01:00
Applevangelist cdbf1e147e Merge remote-tracking branch 'origin/master' into develop 2025-11-14 17:27:02 +01:00
Applevangelist 5089e7df25 xx 2025-11-14 17:26:24 +01:00
Thomas 1de19731d0 Merge pull request #2446 from FlightControl-Master/master
merge from master
2025-11-14 13:46:24 +01:00
Thomas 0c0d1cc794 Merge pull request #2445 from shaji-Dev/master
[ADDED] STATIC:GetVec2Cached() and STATIC:GetVec3Cached()
2025-11-14 13:45:22 +01:00
Shafik 21b968a9c6 [ADDED] STATIC:GetVec2Cached() and STATIC:GetVec3Cached() for statics considering respawns 2025-11-14 14:42:52 +02:00
Applevangelist d857e3acad #NAVIGATION - some fixes and additions 2025-11-14 13:23:57 +01:00
Thomas d7b0b3c898 Merge pull request #2444 from FlightControl-Master/master
Merge from master
2025-11-11 12:57:41 +01:00
Thomas 704bb8668f Merge pull request #2443 from FlightControl-Master/Applevangelist-DC-1
Update DynamicCargo.lua
2025-11-11 12:54:42 +01:00
Thomas 763bfb4bb1 Update DynamicCargo.lua
Added Destroy-Functionality
2025-11-11 12:41:30 +01:00
Shafik e43173de60 Merge branch 'develop' of https://github.com/shaji-Dev/MOOSE into develop 2025-11-09 23:27:00 +02:00
Frank 16706cd483 Merge pull request #2442 from FlightControl-Master/FF/Ops
New Classes + OPS Improvements
2025-11-09 22:02:44 +01:00
Frank 505c86399c Merge branch 'develop' into FF/Ops 2025-11-09 21:46:36 +01:00
Frank 65ddff13f7 OPS Ammo
- Added guns/cannons differentiation to ammo count
2025-11-09 21:46:23 +01:00
Frank bedd105649 Update Auftrag.lua
- added NAVALENGAGEMENT to :NewFromTarget
2025-11-09 18:22:39 +01:00
Applevangelist c10e57ec3b Merge remote-tracking branch 'origin/master' into develop 2025-11-09 17:19:57 +01:00
Applevangelist 046bd37fd5 xx 2025-11-09 17:19:29 +01:00
Applevangelist 55c3e7aba6 Merge remote-tracking branch 'origin/master' into develop 2025-11-09 16:53:39 +01:00
Applevangelist fbf83b3aed #SET - Rationalize FilterCoalitions() and allow values like coalition.side.BLUE 2025-11-09 16:53:10 +01:00
Frank ea60795e84 Assets
- remove assets from warehouse if they are removed from cohort
2025-11-09 16:21:38 +01:00
Applevangelist 4a5e262695 Merge remote-tracking branch 'origin/master' into develop 2025-11-09 14:43:32 +01:00
Applevangelist 22097987dc #CTLD, #DYNAMICCARGO - C-130j-30 additions 2025-11-09 14:43:11 +01:00
Thomas 94617e573f Merge pull request #2441 from FlightControl-Master/master
Merge from master
2025-11-09 09:28:05 +01:00
Thomas 98d02d7532 Merge pull request #2440 from leka1986/patch-2
Update CTLD.lua
2025-11-09 09:24:48 +01:00
leka1986 7b44e7a3f6 Update CTLD.lua
Removed quantity menu if there is only one set.
2025-11-09 02:16:02 +01:00
leka1986 aa4cf37aed Clean up comments in CTLD.lua
Removed unnecessary comment lines in CTLD.lua.
2025-11-08 02:52:23 +01:00
leka1986 caf2b4e736 Update CTLD.lua
Added ability to drop "Sets" from 2 and up will be sets, else it will be like before.

Changed _PackCratesNearby to handle all cargo within the range instead of the nearest.
2025-11-08 02:47:56 +01:00
Frank da516a0167 Merge pull request #2439 from shaji-Dev/master
[FIXED] `dcsgroup:getUnit(1)` nil pointer
2025-11-07 21:53:26 +01:00
Frank 1267a64fcb Ops Navy Update 2025-11-06 23:26:30 +01:00
Shafik 17d1cf673d [ADDED] IsRed, IsBlue and IsNeutral to IDENTIFIABLE 2025-11-06 12:23:13 +02:00
Frank 74c3b9fbcb Update Auftrag.lua 2025-11-05 22:05:02 +01:00
Shafik 1fcd70b0d0 Merge remote-tracking branch 'origin/master' 2025-11-05 12:36:03 +02:00
Shafik 832941494b [FIXED] dcsgroup:getUnit(1) nil pointer 2025-11-05 12:35:55 +02:00
Shafik 581d812cd4 Merge branch 'develop' of https://github.com/shaji-Dev/MOOSE into develop 2025-11-05 07:47:04 +02:00
Shafik 56dbf6bbe1 Merge branch 'develop' of https://github.com/shaji-Dev/MOOSE into develop 2025-11-05 07:46:28 +02:00
Thomas d5792fb02a Merge pull request #2438 from FlightControl-Master/master
Merge
2025-11-05 06:23:06 +01:00
Thomas 0e82cb977e Merge pull request #2437 from leka1986/patch-2
Update CTLD.lua
2025-11-05 06:22:03 +01:00
leka1986 e9237146a3 Update CTLD.lua
Fixed a bug. 

When dropping crates, while choosing "Drop and build" The first drop will put the crate into a crate that can be loaded but a second later, that crate will be cleaned up due to build. This makes sure that the crates are not in the "load crates" list.
2025-11-04 20:09:48 +01:00
Thomas b72899b500 Merge pull request #2436 from shaji-Dev/master
[ADDED] Complete list of DCS warehouse equipment
2025-11-04 13:24:09 +01:00
Shafik 2f39e38264 [ADDED] CTLD. option to load back crates from save file. 2025-11-04 11:32:24 +02:00
Shafik db54672fbd [ADDED] CTLD. option to load back crates from save file. 2025-11-04 11:30:59 +02:00
Shafik 30d9a7d703 [ADDED] Complete list of DCS warehouse equipment 2025-11-04 10:27:16 +02:00
Thomas 4ade4c78ba Merge pull request #2435 from FlightControl-Master/master
Merge
2025-11-03 06:30:00 +01:00
Thomas 5a088a740e Merge pull request #2434 from nasgroup94/master
more T-45 AOA adjustments
2025-11-03 06:29:14 +01:00
nasgroup94 be220b6c1d more T-45 AOA adjustments 2025-11-02 19:48:16 -05:00
Frank e194d6073f OPS improvements
- AUFTRAG success is checked based on really alive targets
- AUFTRAG is done if all groups are done with the mission
2025-11-02 22:45:52 +01:00
Thomas 38858cd01f Merge pull request #2433 from FlightControl-Master/master
Merge from Master
2025-11-02 18:05:46 +01:00
Thomas 645c4a4dbc Merge pull request #2432 from FlightControl-Master/Applevangelist-patch-1
Update CTLD.lua
2025-11-02 17:57:09 +01:00
Thomas c2a630dabd Update CTLD.lua
Cargo counters by @lekaa
2025-11-02 17:56:55 +01:00
Applevangelist 30cf1f5713 #PLAYERTASK/MANAGER
- Option to Cancel a task silently
- Option to allow smoking per task (default: true)
- Option to show threat details per task (default: true)
2025-11-02 14:22:56 +01:00
Thomas 33f7a7084c Merge pull request #2431 from FlightControl-Master/master
Merge from master
2025-11-01 17:26:25 +01:00
Thomas 07a14bc176 Merge pull request #2425 from nasgroup94/master
added Tomcat AA grading adjustments and T45 AOA adjustments
2025-11-01 17:20:08 +01:00
Frank 3c57177a11 Update Radios.lua
- Improved airbase identification by UID
2025-10-31 22:55:37 +01:00
Frank 7cc18b8082 Nav updates 2025-10-31 19:13:36 +01:00
Frank bef805d694 Update Pathline.lua 2025-10-31 14:18:38 +01:00
Frank 3479d08193 VECTOR + PATHLINE 2025-10-31 14:16:20 +01:00
Thomas 8ca9474c1c Merge pull request #2429 from FlightControl-Master/master
Merge from master
2025-10-31 06:28:57 +01:00
Thomas 2047e818ef Merge pull request #2428 from shaji-Dev/master
[FIXED] SET_CLIENT:HandleCASlots()
2025-10-31 06:28:04 +01:00
Shafik 94b8835340 Merge branch 'develop' of https://github.com/shaji-Dev/MOOSE into develop 2025-10-30 23:52:55 +02:00
Shafik 801b4f01b6 [FIXED] SET_CLIENT:HandleCASlots() 2025-10-30 23:10:17 +02:00
Thomas 37a5f68339 Merge pull request #2427 from FlightControl-Master/master
Merge from master
2025-10-29 07:03:23 +01:00
Thomas 6058160160 Merge pull request #2426 from FlightControl-Master/Applevangelist-patch-3-1
Update Positionable.lua
2025-10-29 07:02:10 +01:00
Thomas 479dfc28b9 Update Positionable.lua 2025-10-29 07:01:33 +01:00
Frank e3cee8dafe VECTOR 2025-10-28 23:07:59 +01:00
Frank 3686e0de63 Vector 2025-10-26 23:00:32 +01:00
Frank 56d7638617 Update Utils.lua 2025-10-26 20:58:31 +01:00
Frank 2931b32ce6 Vector 0.0.3 2025-10-26 20:54:52 +01:00
Applevangelist b28e66fe6d Merge remote-tracking branch 'origin/master' into develop 2025-10-26 16:19:44 +01:00
Applevangelist 8392788cdb Minor fixes 2025-10-26 16:19:10 +01:00
Frank 386be19b8b Merge branch 'FF/Ops' into FF/OpsDev 2025-10-26 15:20:48 +01:00
Frank 42221f406a Update Radios.lua 2025-10-26 15:19:34 +01:00
nasgroup94 4a1b23c52a added Tomcat AA grading adjustments and T45 AOA adjustments 2025-10-26 09:43:17 -04:00
Thomas 1965e24860 Merge pull request #2424 from FlightControl-Master/master
Merge from master
2025-10-26 07:31:50 +01:00
Thomas 55242edbde Merge pull request #2423 from nasgroup94/master
Minor Fixes
2025-10-26 07:27:37 +01:00
Shafik ebbe64f279 Merge branch 'develop' of https://github.com/shaji-Dev/MOOSE into develop 2025-10-26 06:52:04 +02:00
Frank 0e4eb9a263 Merge branch 'develop' into FF/OpsDev 2025-10-25 21:18:04 +02:00
Frank 654f752ca0 Merge branch 'develop' into FF/Ops 2025-10-25 21:17:01 +02:00
nasgroup94 2c5d9f043e fixed TIG bug causing NG Removed old into wind function 2025-10-25 12:42:09 -04:00
nasgroup94 2b2acbe244 Merge branch 'master' of https://github.com/nasgroup94/MOOSE 2025-10-25 12:39:54 -04:00
Applevangelist 7d4e103660 #UTILS - SpawnFarp helper, build multiple pads as a single static group, not separate objects. 2025-10-25 16:56:16 +02:00
Applevangelist 3da6187782 #AUFTRAG, #OPSGROUP - close issue #2201 2025-10-25 16:53:53 +02:00
Applevangelist 0bd79d518f Merge remote-tracking branch 'origin/master' into develop 2025-10-24 16:27:53 +02:00
Applevangelist 86798ae9ea #CTLD - fix 2025-10-24 16:27:17 +02:00
Thomas dfc63de745 Merge pull request #2422 from FlightControl-Master/master
Merge from master
2025-10-24 16:17:15 +02:00
Thomas 2830e6d199 Merge pull request #2421 from leka1986/patch-1
Update CTLD.lua
2025-10-24 16:16:00 +02:00
leka1986 8c07573f8f Update CTLD.lua 2025-10-24 16:13:18 +02:00
leka1986 f3af0262df Update CTLD.lua
Added Crates amount to the menus and AddCratesCargoNoMove.
2025-10-24 14:02:06 +02:00
Applevangelist d7841b47a5 Merge remote-tracking branch 'origin/master' into develop 2025-10-23 12:10:43 +02:00
Applevangelist 0f270a6a35 #UTILS - FARP helper - add options to spawn multiple pads 2025-10-23 12:10:05 +02:00
Applevangelist 92db68c36b Merge remote-tracking branch 'origin/master' into develop 2025-10-23 08:11:11 +02:00
Applevangelist 8432f46e48 #AIRBOSS - Fixed omission of magnetic in expected BRC call 2025-10-23 08:10:26 +02:00
nasgroup94 faccee88e7 added missing end to TIG "_OK_" subtraction 2025-10-22 05:19:50 -04:00
Thomas e8246b3b90 Merge pull request #2420 from FlightControl-Master/revert-2417-master
Revert "Minor fixes"
2025-10-22 07:23:37 +02:00
Thomas eb4e7b9281 Revert "Minor fixes" 2025-10-22 07:23:21 +02:00
Thomas e91bcdfe5e Merge pull request #2419 from FlightControl-Master/revert-2418-master
Revert "Merge from master"
2025-10-22 07:16:51 +02:00
Thomas 1f1a068d45 Revert "Merge from master" 2025-10-22 07:16:33 +02:00
Thomas a57c0a8c7d Merge pull request #2418 from FlightControl-Master/master
Merge from master
2025-10-22 06:32:18 +02:00
Thomas 1e2190a6cc Merge pull request #2417 from nasgroup94/master
Minor fixes
2025-10-22 06:31:24 +02:00
nasgroup94 1644c1dc5b fixes to goshawk AOA f14 AA and TIG bug 2025-10-21 16:49:51 -04:00
nasgroup94 5fc3798c42 Update Airboss.lua
Fixed possible bug where a player will get points deducted when getting a perfect groove time
2025-10-21 05:44:19 -04:00
Shafik dcae8eb7f3 Merge branch 'develop' of https://github.com/shaji-Dev/MOOSE into develop
# Conflicts:
#	Moose Development/Moose/Ops/CTLD.lua
2025-10-20 14:57:15 +03:00
Shafik 3fa5c92058 [ADDED] UNIT:IsEWR() 2025-10-20 14:57:01 +03:00
Applevangelist 512c3c57e9 #TIRESIAS - Fixed a problem when a script wants to add exceptions pre-start. 2025-10-19 14:38:50 +02:00
Applevangelist 3a95e75c2d Merge remote-tracking branch 'origin/master' into develop 2025-10-13 18:52:26 +02:00
Applevangelist fae81cdb1b Merge remote-tracking branch 'origin/master' 2025-10-13 18:51:59 +02:00
Applevangelist dbae37f151 xx 2025-10-13 18:51:55 +02:00
Applevangelist f7d58a0b76 #ZONE - Added missing ZONE_UNIT:UpdateFromUnit() and ZONE_GROUP:UpdateFromGroup() 2025-10-13 18:51:22 +02:00
Applevangelist 08fb458aa4 Merge remote-tracking branch 'origin/master' into develop 2025-10-12 19:16:14 +02:00
Applevangelist 2e0d1fd90f #DYNAMICCARGO - Added Mi-8 type and dimensions 2025-10-12 19:14:59 +02:00
Applevangelist 3694e9649d Merge remote-tracking branch 'origin/develop' into develop 2025-10-12 17:25:03 +02:00
Applevangelist 2c79fa6492 Merge remote-tracking branch 'origin/master' into develop 2025-10-12 17:24:58 +02:00
Applevangelist 69e6497655 #CTLD - Small fix for subcats 2025-10-12 17:23:54 +02:00
Thomas aca713b8d9 Merge pull request #2416 from FlightControl-Master/master
Merge
2025-10-10 11:42:36 +02:00
Thomas ef8c7c9084 Merge pull request #2415 from shaji-Dev/master
[ADDED] UH-60L 'DAP' to `UTILS.IsLoadingDoorOpen` check
2025-10-10 11:41:52 +02:00
shaji-Dev e4eaf72769 Merge branch 'FlightControl-Master:master' into master 2025-10-10 09:19:46 +02:00
smiki 5138ced630 Merge remote-tracking branch 'origin/master' 2025-10-10 09:19:31 +02:00
smiki 534f445f8c [ADDED] UH-60L 'DAP' to UTILS.IsLoadingDoorOpen check 2025-10-10 09:19:23 +02:00
Thomas a2da4f0e77 Merge pull request #2414 from FlightControl-Master/master
Merge
2025-10-09 17:43:05 +02:00
Thomas b2cc3e5329 Merge pull request #2413 from leka1986/master
Master
2025-10-09 17:41:07 +02:00
Thomas efde321616 Merge pull request #2412 from shaji-Dev/master
[ADDED] Fuel Tanks and gunner seat loadout enums for UH-60L mod
2025-10-09 17:40:03 +02:00
leka1986 3a61581608 #Added a new option ( true by default), returntroopstobase. If set to false, troops would not return to base when dropped at load zone. 2025-10-09 17:33:15 +02:00
shaji-Dev 3a10f0b946 Merge branch 'FlightControl-Master:master' into master 2025-10-09 13:30:04 +02:00
smiki 6dc6972bdb [ADDED] Fuel Tanks and gunner seat loadout enums for UH-60L mod 2025-10-09 13:29:45 +02:00
Applevangelist 5910c9502a Merge remote-tracking branch 'origin/master' into develop 2025-10-08 16:01:48 +02:00
Applevangelist 405235a59d #CONTROLLABLE - Fix for double brackets 2025-10-08 16:01:21 +02:00
Thomas bd8ac9551c Merge pull request #2411 from FlightControl-Master/master
Merge from master
2025-10-08 13:26:04 +02:00
Thomas 9c148625e4 Merge pull request #2410 from shaji-Dev/master
[FIXED] CTLD. Memory leak adding zones of the same name and type
2025-10-08 13:24:57 +02:00
shaji-Dev a08d82a3d9 Merge branch 'FlightControl-Master:master' into master 2025-10-08 13:15:39 +02:00
smiki 48b51f21de [FIXED] CTLD. Memory leak adding zones of the same name and type
[FIXED] CSAR. nil pointer
2025-10-08 13:15:22 +02:00
Applevangelist c850ea10f5 Merge remote-tracking branch 'origin/master' into develop 2025-10-08 12:30:37 +02:00
Applevangelist 3260279cb7 xx 2025-10-08 12:29:59 +02:00
Applevangelist 2fcef1f277 xx 2025-10-07 18:10:00 +02:00
Applevangelist 1e60a0a32a #RAT 3.0.0 2025-10-07 18:06:20 +02:00
Applevangelist 7707522ce5 Merge remote-tracking branch 'origin/master' into develop
# Conflicts:
#	Moose Development/Moose/Functional/RAT.lua
2025-10-07 17:52:03 +02:00
Applevangelist 91e26adc6a #AUFTRAG - Added functions to check repeatability
#OPS - less noise in log
#LEGION - Added mission housekeeping
2025-10-07 17:50:29 +02:00
Applevangelist 146f869aaa #RAT - reduce log noise 2025-10-07 17:48:06 +02:00
Thomas dc863be38a Merge pull request #2409 from FlightControl-Master/master
Merge from master
2025-10-07 15:52:14 +02:00
Thomas 0bd35727f4 Merge pull request #2408 from shaji-Dev/master
[FIXED] Incorrect Airbase center position. [ADDED] UH-60L weapons
2025-10-07 15:51:44 +02:00
Thomas 5183fcc316 Update Enums.lua 2025-10-07 15:51:22 +02:00
shaji-Dev bf7596521c Merge branch 'FlightControl-Master:master' into master 2025-10-07 15:42:17 +02:00
smiki a85b6c960c [FIXED] Incorrect Airbase center position
[ADDED] UH-60L weapons
2025-10-07 15:41:54 +02:00
Applevangelist 8cb91477cf #AWACS - added function to set own BullsEye coordinate if necessary 2025-10-07 11:41:00 +02:00
Thomas a8a69fdbca Merge pull request #2407 from FlightControl-Master/master
Merge from master
2025-10-07 10:15:02 +02:00
Thomas a1aebf0575 Merge pull request #2406 from nasgroup94/master
AIRBOSS adjustments
2025-10-07 10:14:31 +02:00
nasgroup94 0f42218681 minor edits 2025-10-06 18:08:23 -04:00
frankiep95 5404f9ef19 Minor Fixes to grading fixed high low comments and BRC comment 2025-10-05 13:31:18 -04:00
frankiep95 8a84be19df Merge branch 'master' of https://github.com/nasgroup94/MOOSE 2025-10-05 12:50:58 -04:00
frankiep95 fd4ea81e46 minor fixes to grading, and BRC callout 2025-10-05 12:50:56 -04:00
Thomas 025bd3237c Merge pull request #2405 from FlightControl-Master/master
Merge from master
2025-10-05 18:04:43 +02:00
Thomas 3df79aedb1 Merge pull request #2404 from shaji-Dev/master
[FIXED] SPAWNSTATIC not registering script spawned static templates therefore ReSpawn is not working.
2025-10-05 18:04:09 +02:00
smiki 4141aa35ba Merge remote-tracking branch 'origin/master' 2025-10-05 16:57:41 +02:00
smiki 6e45ee558e [FIXED] SPAWNSTATIC not registering script spawned static templates therefore ReSpawn is not working. 2025-10-05 16:57:33 +02:00
Applevangelist e867ce4c73 Merge remote-tracking branch 'origin/master' into develop 2025-10-05 13:52:59 +02:00
Applevangelist db138be5f3 #SCoRING - suppress autocreation of CSV files better 2025-10-05 13:52:22 +02:00
Applevangelist 5ae6495e69 #CSAR Added functionality to determine if a landing took place at a helo base (named "H ..." in newer maps). 2025-10-05 13:51:55 +02:00
Thomas d8281b0103 Merge pull request #2403 from FlightControl-Master/master
Merge from master
2025-10-01 15:26:49 +02:00
Thomas 58f1bc5531 Merge pull request #2402 from shaji-Dev/master
[ADDED] `SET_OPSGROUP:CountAlive`
2025-10-01 15:25:45 +02:00
smiki 935b52c489 [FIXED] ZONE_POLYGON from RECT ME drawing rotation not taken into account. 2025-10-01 15:11:44 +02:00
smiki aace98545a [FIXED] ZONE_POLYGON from RECT ME drawing rotation not taken into account. 2025-10-01 15:07:18 +02:00
smiki f39236c8fd [FIXED] ZONE_POLYGON from RECT ME drawing rotation not taken into account. 2025-10-01 14:59:59 +02:00
smiki 43b4a6834b [FIXED] ZONE_POLYGON from RECT ME drawing rotation not taken into account. 2025-10-01 14:50:04 +02:00
shaji-Dev aa3978b04d Merge branch 'FlightControl-Master:master' into master 2025-09-30 21:17:51 +02:00
smiki b51e758516 [ADDED] SET_OPSGROUP:CountAlive 2025-09-30 21:17:29 +02:00
Frank 5a92fb1f65 Merge branch 'develop' into FF/Ops 2025-09-28 20:48:52 +02:00
Thomas 4a870d0183 Merge pull request #2401 from FlightControl-Master/master
Merge from master
2025-09-26 15:53:13 +02:00
Thomas 5be1832c09 Merge pull request #2400 from shaji-Dev/master
[ADDED] `UH-60L DAP to CSAR and CTLD`
2025-09-26 15:52:30 +02:00
smiki 362652ac6c [ADDED] CSAR. UH-60L DAP aircraft type 2025-09-26 15:14:12 +02:00
smiki aec69884dc [ADDED] CTLD. UH-60L DAP default unit capability 2025-09-26 15:13:03 +02:00
Applevangelist 47ddc64239 #EASYGCICAP added EASYGCICAP:SetCAPEngageTargetTypes(types) 2025-09-25 15:17:03 +02:00
Applevangelist 2a32be40f6 Merge remote-tracking branch 'origin/master' into develop 2025-09-25 12:13:55 +02:00
Applevangelist 8fb4d4c7c6 #CTLD - add CratesPickedUp at one more point 2025-09-25 12:13:29 +02:00
Thomas b17748cfeb Merge pull request #2399 from FlightControl-Master/master
Merge from master
2025-09-24 19:16:55 +02:00
Thomas b5524b9a69 Merge pull request #2398 from shaji-Dev/master
[Fixed] Zone Scans returns objects outside the zone
2025-09-24 19:16:14 +02:00
smiki ec0ff7afcd [Fixed] Zone Scans returns objects outside the zone 2025-09-24 15:14:21 +02:00
Thomas c761d5020b Merge pull request #2397 from FlightControl-Master/master
Merge from master
2025-09-23 15:31:20 +02:00
Thomas d0cf68c2e2 Merge pull request #2396 from FlightControl-Master/Applevangelist-patch-3
Update Storage.lua
2025-09-23 15:26:54 +02:00
Thomas 3beb98a5e9 Update Storage.lua 2025-09-23 15:26:38 +02:00
Thomas dd83ebe0e2 Merge pull request #2395 from FlightControl-Master/Applevangelist-patch-3
Fix for #Storage.lua some missing string concats
2025-09-23 15:19:42 +02:00
Thomas 6fdf3957bd Fix for #Storage.lua some missing string concats 2025-09-23 15:19:10 +02:00
Thomas 608fa1ca6c Merge pull request #2394 from FlightControl-Master/master
small fix
2025-09-22 11:15:13 +02:00
Thomas f3d586d455 Merge pull request #2393 from FlightControl-Master/Applevangelist-patch-2
Update Airbase.lua
2025-09-22 11:13:59 +02:00
Thomas c2f0ce0fa2 Update Airbase.lua 2025-09-22 11:13:44 +02:00
Thomas 8535c39c2c Merge pull request #2392 from FlightControl-Master/master
Merge from master
2025-09-22 11:11:46 +02:00
Thomas da8f023f5b Merge pull request #2386 from shaji-Dev/develop
[Added] `PLAYERTASK:CanJoinTask`
2025-09-22 11:10:45 +02:00
Thomas 354490d149 Merge pull request #2391 from FlightControl-Master/Applevangelist-patch-2
Update Airbase.lua Iraq Airbases
2025-09-22 11:09:47 +02:00
Thomas c5b0be5d21 Update Airbase.lua Iraq Airbases 2025-09-22 11:08:15 +02:00
Thomas 7123562f85 Merge pull request #2388 from m1ndgames/develop
fix: Add conditional checks for AWACS markers based on AllowMarkers property
2025-09-18 07:41:14 +02:00
Thomas 6301f5a939 Merge pull request #2389 from FlightControl-Master/master
Merge from master
2025-09-18 06:48:20 +02:00
Thomas 5e8676cf8a Merge pull request #2387 from shaji-Dev/master
LandingAfterEjection error
2025-09-18 06:47:01 +02:00
m1nd b320ba597a fix: Add conditional checks for AWACS markers based on AllowMarkers property 2025-09-18 00:32:48 +02:00
shaji-Dev 12d16f355b Merge branch 'FlightControl-Master:develop' into develop 2025-09-17 20:20:37 +02:00
smiki f4264cd149 Merge remote-tracking branch 'origin/master' 2025-09-17 20:19:54 +02:00
smiki 65d1c4187e [Fixed] attempt to index field 'Place' (a nil value) in LandingAfterEjection 2025-09-17 20:19:17 +02:00
Applevangelist 58b169cb03 Merge remote-tracking branch 'origin/develop' into develop 2025-09-17 12:04:38 +02:00
Applevangelist 04fcb985fe #PLAYERTASK - Ensure call task failed on the controller 2025-09-17 12:04:34 +02:00
smiki 5414adc8d8 [Added] PLAYERTASK:CanJoinTask Override function in order to implement custom logic if a player can join a task or not. 2025-09-17 09:26:34 +02:00
Thomas 95210366e9 Merge pull request #2385 from shaji-Dev/develop
[Added] `PLAYERTASKCONTROLLER:CanJoinTask`
2025-09-16 19:14:17 +02:00
smiki f73dc6c3be [Added] PLAYERTASKCONTROLLER:CanJoinTask Override function in order to implement custom logic if a player can join a task or not. 2025-09-16 15:14:28 +02:00
Applevangelist 74383e3111 Merge remote-tracking branch 'origin/develop' into develop 2025-09-15 09:28:39 +02:00
Applevangelist 3f8954d21d #EASYGCICAP - Fixed - for Tanker Squadrons the TACAN channel could not be nil. 2025-09-15 09:28:35 +02:00
Thomas ebf393c58b Merge pull request #2384 from FlightControl-Master/master
Merge from master
2025-09-11 05:14:54 +02:00
Thomas cda1432d04 Merge pull request #2383 from shaji-Dev/master
[FIXED] CTLD. Injected spawns not using `InitValidateAndRepositionGroundUnits`
2025-09-11 05:13:50 +02:00
smiki 09e5fca1a5 [FIXED] CTLD. Injected spawns not using InitValidateAndRepositionGroundUnits 2025-09-11 00:38:20 +02:00
Applevangelist 9c3c27f809 Merge remote-tracking branch 'origin/develop' into develop 2025-09-07 18:58:47 +02:00
Applevangelist 03275938e7 #CLIENTMENU - add missing func from documentation 2025-09-07 18:58:41 +02:00
Thomas a90e321a91 Merge pull request #2382 from FlightControl-Master/master
Merge from master
2025-09-03 10:37:03 +02:00
Thomas e38d73df8b Merge pull request #2381 from shaji-Dev/master
[ADDED] `GROUP:GetBoundingBox()`
2025-09-03 10:35:42 +02:00
smiki 7df90b2d30 [ADDED] GROUP:GetBoundingBox() since POSITIONABLE:GetBoundingBox() is only for units 2025-09-03 09:12:20 +02:00
Thomas e531f12f94 Merge pull request #2380 from FlightControl-Master/master
Merge from master
2025-09-01 10:59:59 +02:00
Thomas a917ee8f1e Merge pull request #2379 from shaji-Dev/master
[FIXED] `UTILS.HdgTo to accept both Vec2 or Vec3`
2025-09-01 10:57:48 +02:00
smiki 44f3c776eb [FIXED] UTILS.HdgTo to accept both Vec2 or Vec3 2025-09-01 09:30:05 +02:00
Applevangelist 5d3f5dac1e Merge remote-tracking branch 'origin/master' into develop 2025-08-31 18:22:41 +02:00
Applevangelist 873879ff79 #AIRBOSS - Slight tweak to EnableSRS() when no parameters are handed in. 2025-08-31 18:22:17 +02:00
Applevangelist 2f46a63041 Merge remote-tracking branch 'origin/master' into develop 2025-08-31 13:28:00 +02:00
Applevangelist 6c1907f7e0 GROUO correction in class self line 3205 2025-08-31 13:27:24 +02:00
Applevangelist f43dd389e2 Merge remote-tracking branch 'origin/master' into develop 2025-08-31 13:25:47 +02:00
Applevangelist 42ecdd3b14 #AIRBASE - Add Sinai Damascus Intl Airbase in the enumerator 2025-08-31 13:24:35 +02:00
Thomas 297164a0ee Merge pull request #2378 from shaji-Dev/master
[ADDED] `SPAWNSTATIC:InitValidateAndRepositionStatic(OnOff, MaxRadius)`
2025-08-28 19:58:19 +02:00
smiki 323f09b06c [ADDED] SPAWNSTATIC:InitValidateAndRepositionStatic(OnOff, MaxRadius) 2025-08-28 19:38:05 +02:00
smiki e003b91bbe [ADDED] SPAWNSTATIC:InitValidateAndRepositionStatic(OnOff, MaxRadius) 2025-08-28 19:36:22 +02:00
Applevangelist efb1d79e77 #COHORT - Fix Callsign Setting 2025-08-28 13:17:57 +02:00
Applevangelist ee23daa651 Merge remote-tracking branch 'origin/develop' into develop 2025-08-28 11:13:56 +02:00
Applevangelist e590701c01 xx 2025-08-28 11:13:50 +02:00
Thomas 9f1777ca9b Merge pull request #2376 from FlightControl-Master/master
Airboss Vietnam Era Vessels
2025-08-27 14:24:43 +02:00
Thomas f1636fc5a9 Merge pull request #2375 from tspindler-cms/TeTeT/master
Add support for Vietnam War Vessels carriers to Airboss
2025-08-27 14:23:36 +02:00
TeTeT Nimitz db053398d2 Add support for Vietnam War Vessels carriers to Airboss:
- CVA-31 Bon Homme Richard
    - Generic Essex with SCB-125 upgrade (angled Deck)
    - CVAN-65 Enterprise 1966
    - CVN-65 Enterprise modern
2025-08-27 13:12:50 +02:00
Thomas 527c4d0de4 Merge pull request #2373 from shaji-Dev/develop
[ADDED] `ValidateAndRepositionGroundUnits` to OPSGROUP
2025-08-26 18:38:41 +02:00
smiki 2479147fd9 [ADDED] ValidateAndRepositionGroundUnits to OPSGROUP 2025-08-26 13:15:12 +02:00
Thomas 7b7a8c1bab Merge pull request #2372 from FlightControl-Master/master
Merge from master
2025-08-26 06:23:46 +02:00
Thomas 417caf1b62 Merge pull request #2370 from shaji-Dev/master
[ADDED] `ValidateAndRepositionGroundUnits` to UNIT and GROUP respawns
2025-08-26 06:22:57 +02:00
Thomas 2ff630d394 Merge pull request #2371 from shaji-Dev/develop
[ADDED] `ValidateAndRepositionGroundUnits` to OPSGROUP
2025-08-26 06:21:58 +02:00
smiki b6d725920a [ADDED] ValidateAndRepositionGroundUnits to OPSGROUP 2025-08-25 23:22:00 +02:00
smiki cc76851614 [ADDED] ValidateAndRepositionGroundUnits to UNIT and GROUP respawns 2025-08-25 23:08:25 +02:00
Frank 1ddb2aaec0 Merge branch 'develop' into FF/Ops 2025-08-25 20:53:40 +02:00
Thomas c30d517a00 Merge pull request #2369 from FlightControl-Master/master
Merge from master
2025-08-25 12:09:30 +02:00
Thomas 27fe314c1e Merge pull request #2368 from shaji-Dev/master
[ADDED] WAREHOUSE:SetValidateAndRepositionGroundUnits
2025-08-25 12:08:27 +02:00
smiki 2c12cfe4fd [ADDED] WAREHOUSE:SetValidateAndRepositionGroundUnits to use for Brigades 2025-08-24 17:06:45 +02:00
smiki 9adf342dd8 [ADDED] WAREHOUSE:SetValidateAndRepositionGroundUnits to use for Brigades 2025-08-24 16:45:54 +02:00
smiki facac82130 [ADDED] WAREHOUSE:SetValidateAndRepositionGroundUnits to use for Brigades 2025-08-24 16:44:41 +02:00
Thomas c96249906e Merge pull request #2367 from FlightControl-Master/master
QuickFix
2025-08-24 16:36:33 +02:00
Thomas 3e095711f4 Merge pull request #2366 from shaji-Dev/master
[FIXED] Maintain valid unit positions
2025-08-24 16:35:43 +02:00
smiki ca1ddb4013 Merge remote-tracking branch 'origin/master' 2025-08-24 16:28:08 +02:00
smiki 8169235d2f [FIXED] Maintain valid unit positions 2025-08-24 16:27:55 +02:00
Thomas 0a56624657 Merge pull request #2365 from FlightControl-Master/master
Ground Spawn Options to avoid non-terrain by Heartbreaker
2025-08-24 15:25:46 +02:00
Thomas 4553785918 Merge pull request #2364 from shaji-Dev/master
[ADDED] Validate and Reposition Ground Units algorithm
2025-08-24 15:23:32 +02:00
smiki 78b3efcf00 Validate and Reposition Ground Units algorithm
[ADDED] UTILS.ValidateAndRepositionGroundUnits
[ADDED] SPAWN:InitValidateAndRepositionGroundUnits
[ADDED] CTLD.validateAndRepositionUnits
2025-08-24 15:10:47 +02:00
smiki 67cb844550 Validate and Reposition Ground Units algorithm
[ADDED] UTILS.ValidateAndRepositionGroundUnits
[ADDED] SPAWN:InitValidateAndRepositionGroundUnits
[ADDED] CTLD.validateAndRepositionUnits
2025-08-24 15:07:27 +02:00
Applevangelist 1b169f007c xx 2025-08-15 14:26:41 +02:00
Applevangelist 8efcad123c Merge remote-tracking branch 'origin/master' into develop 2025-08-14 17:18:03 +02:00
Applevangelist b9cf1e46af xx 2025-08-14 17:17:34 +02:00
Applevangelist 47409b11cb Merge remote-tracking branch 'origin/master' into develop 2025-08-14 17:16:30 +02:00
Applevangelist 4a04d7cce7 xx 2025-08-14 17:15:43 +02:00
Applevangelist 5ef6383c63 Merge remote-tracking branch 'origin/master' into develop 2025-08-14 09:13:32 +02:00
Applevangelist 35f15435a3 #MANTIS - Added Pantsir S1, TOR M2, IRIS-T SLM to main man SAM data (from CH mod) 2025-08-14 09:12:58 +02:00
Thomas 38dcd04334 Merge pull request #2363 from FlightControl-Master/Applevangelist-patch-1
#EasyGCICAP Adjustments for CAP ZONE using
2025-08-11 12:58:30 +02:00
Thomas b32a8a2899 #EasyGCICAP Adjustments for CAP ZONE using
Added missing computations for coordinate when the zone is moving
2025-08-11 12:57:37 +02:00
Applevangelist 8cac4dbf9e #TIRESIAS - allow documents to build 2025-08-10 13:35:24 +02:00
Applevangelist ef2bec72a1 Merge remote-tracking branch 'origin/master' into develop 2025-08-10 13:33:44 +02:00
Applevangelist 03c3a20b1b #AIRWING, #EASYGCICAP - allow moving zones as patrol points for CAP/TANKER/AWACS/RECON 2025-08-10 13:33:16 +02:00
Applevangelist 4c97d966a2 #MSRS - align google voices catalog with new voice types. 2025-08-10 13:20:22 +02:00
Applevangelist f075c02db5 #EASYGCICAP - small tweak to allow the airbase to be a carrier ship 2025-08-07 18:47:49 +02:00
Applevangelist 32e61a7a09 Merge remote-tracking branch 'origin/master' into develop 2025-08-07 11:31:54 +02:00
Applevangelist 674c6eec81 More randomness in functions using random coordinates 2025-08-07 11:30:44 +02:00
Thomas da535eaddc Merge pull request #2362 from FlightControl-Master/master
Merge from Master - SCORING
2025-08-07 11:17:24 +02:00
Thomas c75c3d8777 Merge pull request #2361 from FlightControl-Master/Applevangelist-score-1
Update Scoring.lua
2025-08-07 11:16:29 +02:00
Thomas 4fa63986dc Update Scoring.lua
Further changes
2025-08-07 11:16:07 +02:00
Thomas 029f7a3f5c Update Scoring.lua
Better check for Scenery hits where target category is usually nil
2025-08-06 12:27:28 +02:00
Thomas eb6eba25f3 Merge pull request #2359 from FlightControl-Master/master
Airbase new Sinai Bases
2025-08-04 16:12:01 +02:00
Thomas e9194c59f4 Merge pull request #2358 from FlightControl-Master/Applevangelist-patch-1
Update Airbase.lua
2025-08-04 16:11:00 +02:00
Thomas c8d693c8e7 Update Airbase.lua
Sinai add'l bases
2025-08-04 16:10:10 +02:00
Thomas 72d8b29699 Merge pull request #2357 from FlightControl-Master/master
Merge from master
2025-08-02 18:35:43 +02:00
Thomas 2341014882 Merge pull request #2356 from leka1986/master
Master
2025-08-02 18:34:25 +02:00
leka1986 eb15fadcfe Added SetPartlyInside. if used, it the :Trigger will trigger as soon as any of the group units enteres the zone even if they are far apart. 2025-08-02 17:40:55 +02:00
Applevangelist 1e7203320f Merge remote-tracking branch 'origin/master' into develop 2025-08-01 14:03:29 +02:00
Applevangelist e3b587aa95 xx 2025-08-01 14:03:12 +02:00
Applevangelist 13fa8f373e #MANTIS - added radar entry for Dog Ear and Nike 2025-08-01 14:02:57 +02:00
Applevangelist c3dc055fb2 #FLIGHTGROUP - avoid nil error on dynamic spawn clients when looking for theri original prarking space 2025-08-01 09:23:40 +02:00
Applevangelist 4e024f7173 #EASYGCICAP - added FuelLow/Critical settings and stopping of Airwings if you call Stop(). 2025-07-31 12:35:40 +02:00
Applevangelist 1248006f3d Merge remote-tracking branch 'origin/master' into develop 2025-07-31 09:48:23 +02:00
Applevangelist b318e8ae13 #AIRBOSS - Added :SetCarrierIllumination(Mode) 2025-07-31 09:47:54 +02:00
Thomas 1af4d44ca5 Merge pull request #2355 from FlightControl-Master/master
Merge from master
2025-07-31 09:27:03 +02:00
Thomas 7e963bef41 Merge pull request #2354 from shaji-Dev/master
[ADDED] `UTILS.ShowPicture` For all, coalition, country, group and unit
2025-07-31 09:26:09 +02:00
smiki 933000ffc7 [ADDED] UNIT:SetCarrierIlluminationMode 2025-07-31 09:06:19 +02:00
smiki 9b217e1c97 [ADDED] UTILS.ShowPicture For all, coalition, country, group and unit
[ADDED] `UTILS.ShowHelperGateForUnit`
2025-07-31 08:57:20 +02:00
smiki 324f4944b4 [ADDED] UTILS.ShowPicture For all, coalition, country, group and unit
[ADDED] `UTILS.ShowHelperGateForUnit`
2025-07-31 08:54:16 +02:00
Applevangelist b9d738c1ad Merge remote-tracking branch 'origin/master' into develop 2025-07-29 17:39:08 +02:00
Applevangelist f735f1eb53 CTLD - correct ground speed for routing 2025-07-29 17:38:35 +02:00
Thomas 7149226283 Merge pull request #2353 from shaji-Dev/master
[CHANGED] Use file path instead of resource key
2025-07-29 13:04:46 +02:00
shaji-Dev 4164a5288a Merge branch 'FlightControl-Master:master' into master 2025-07-29 12:50:45 +02:00
smiki 1992276b07 Merge remote-tracking branch 'origin/master' 2025-07-29 12:50:27 +02:00
smiki 21a7023b7b Removed getValueResourceByKey UTILS.ShowPicture and UTILS.SetMissionBriefing to use full file paths 2025-07-29 12:50:19 +02:00
Applevangelist bd054b26c0 Merge remote-tracking branch 'origin/master' into develop 2025-07-29 12:05:29 +02:00
Applevangelist f094716b73 CTLD - Added option for Vehicle Formation when going to a MOVE zone. 2025-07-29 12:04:41 +02:00
Applevangelist b3100d2f5e Merge remote-tracking branch 'origin/master' into develop 2025-07-29 10:03:34 +02:00
Applevangelist 4b1888a34d CSAR - Allow also the initial down message to be suppressed 2025-07-29 10:02:22 +02:00
Applevangelist f4cd214823 #TIRESIAS - Avoid creating SET_GROUPs all the time for player objects, cached now 2025-07-27 19:24:43 +02:00
Applevangelist b9be3aa7f8 xx 2025-07-27 14:50:45 +02:00
Thomas bb6db2b7f8 Merge pull request #2352 from FlightControl-Master/master
Merge from Master
2025-07-27 14:16:07 +02:00
Thomas fd2dacaefb Merge pull request #2351 from shaji-Dev/master
[ADDED] `UTILS.LoadMission` and `UTILS.SetMissionBriefing`
2025-07-27 14:15:16 +02:00
smiki cc60e85901 [ADDED] UTILS.LoadMission and UTILS.SetMissionBriefing 2025-07-27 13:18:56 +02:00
Thomas 9520782a04 Merge pull request #2350 from FlightControl-Master/master
Merge from master
2025-07-26 09:02:12 +02:00
Thomas f172f6efeb Merge pull request #2349 from shaji-Dev/master
[ADDED] `UTILS.ShowPicture`. Overlay pictures for players
2025-07-26 09:01:02 +02:00
smiki b6b6686873 [ADDED] COORDINATE:GetLandProfile 2025-07-25 23:43:00 +02:00
smiki 5e724e7a3f [ADDED] COORDINATE:GetLandProfile 2025-07-25 23:39:53 +02:00
smiki 90f1d1df2a Merge remote-tracking branch 'origin/master' 2025-07-25 23:27:11 +02:00
smiki a5726c0ed8 [ADDED] UTILS.ShowPicture. Overlay pictures for players.
Refactoring
2025-07-25 23:27:01 +02:00
Applevangelist 1860366986 Merge remote-tracking branch 'origin/master' into develop 2025-07-25 19:06:34 +02:00
Applevangelist 9db1d38a15 #TIRESIAS - some optimization 2025-07-25 19:06:12 +02:00
Applevangelist 04ceda693b #AUTOLASE - switch off increased ground awareness as default 2025-07-25 19:05:40 +02:00
Applevangelist 23ff128ac8 #ZONE added ZONE_BASE:FindNearestCoordinateOnRadius() 2025-07-25 19:05:01 +02:00
Applevangelist 3749142670 Merge remote-tracking branch 'origin/master' into develop 2025-07-25 14:58:41 +02:00
Applevangelist 7d3fc1740a xx 2025-07-25 14:57:58 +02:00
Applevangelist b2a084d669 xx 2025-07-25 14:57:06 +02:00
Applevangelist 30203668e4 Revert "#UTILS - Added FindNearestPointOnCircle()"
This reverts commit 2cc1ddd4679b0e3fb7a5f72ea5e4822112e2f2d1.
2025-07-25 14:57:06 +02:00
Thomas ebecc70693 Merge pull request #2348 from shaji-Dev/master
[ADDED] `Disposition.getSimpleZones`  support for ZONE_POLYGON_BASE
2025-07-25 14:47:08 +02:00
smiki 74712b6e27 [ADDED] Disposition.getSimpleZones to ZONE_POLYGON_BASE to support all zone types 2025-07-25 14:17:03 +02:00
Applevangelist 40253ea8bb xx 2025-07-24 18:27:44 +02:00
Applevangelist 7279da7f09 xx 2025-07-24 18:26:28 +02:00
Applevangelist 31c40c96f2 Merge remote-tracking branch 'origin/develop' into develop 2025-07-24 16:21:24 +02:00
Applevangelist 7a833365ce #CONTROLLABLE - added options for landing approaches
* Prefer vertical for helos
and for aircraft
* Straight in
* Overhead break
* Force pair
* Restrict pair

You can also set these on AirWing and FlightGroup levels

#AUFTRAG - added dive bomb option to NewBombing
2025-07-24 16:21:04 +02:00
Applevangelist 4e56078d2a #CONTROLLABLE - added options for landing approaches
* Prefer vertical for helos
and for aircraft
* Straight in
* Overhead break
* Force pair
* Restrict pair
2025-07-24 16:17:09 +02:00
Thomas 0ac156f2b5 Merge pull request #2346 from FlightControl-Master/master
Merge from master
2025-07-24 09:40:00 +02:00
Thomas 4bbf20ca4e Merge pull request #2345 from shaji-Dev/master
[Fixed] `Disposition.getSimpleZones`
2025-07-24 09:39:09 +02:00
smiki a462c5a493 [Fixed] Disposition.getSimpleZones 2025-07-24 01:51:10 +02:00
Applevangelist 179cb5f589 Merge remote-tracking branch 'origin/master' into develop 2025-07-23 15:48:26 +02:00
Applevangelist 367014ebf3 xxx 2025-07-23 15:47:56 +02:00
Applevangelist 9d8d0c2155 Merge remote-tracking branch 'origin/develop' into develop 2025-07-23 12:36:05 +02:00
Applevangelist edfe91274d Merge remote-tracking branch 'origin/master' into develop 2025-07-23 12:36:01 +02:00
Thomas 326b20b08d Merge pull request #2344 from shaji-Dev/master
[ADDED] `Disposition.getSimpleZones`
2025-07-23 12:35:16 +02:00
Applevangelist 11b0ce6275 #AIRBASE - remove some differences between data produced by _InitRunways and GetRunwayData 2025-07-23 12:34:52 +02:00
smiki 03763e16d6 [ADDED] Disposition.getSimpleZones 2025-07-23 12:19:00 +02:00
smiki c1e8ee12e0 [ADDED] Disposition.getSimpleZones 2025-07-23 11:48:33 +02:00
smiki ac8cc408c1 [FIXED] Disposition.getSimpleZones 2025-07-23 11:48:07 +02:00
Thomas dad91cca8c Merge pull request #2343 from shaji-Dev/develop
[FIXED] COMMANDER.maxMissionsAssignPerCycle typo
2025-07-22 18:55:50 +02:00
smiki 2f9c384ac0 [FIXED] COMMANDER.maxMissionsAssignPerCycle typo 2025-07-22 18:45:02 +02:00
Applevangelist f23a2267cf Merge remote-tracking branch 'origin/master' into develop 2025-07-22 13:09:48 +02:00
Applevangelist ada38fa3ea #AIRBOSS - SRS 2.2.x path in documentation 2025-07-22 13:08:46 +02:00
Applevangelist 2ee0597d48 #CTLD - added FSM event "CratesPacked"
#UTILS - more options for MASH building
2025-07-22 13:08:18 +02:00
Applevangelist b39a83ad6e Merge remote-tracking branch 'origin/master' into develop 2025-07-21 15:03:22 +02:00
Applevangelist 7ae4cdc8f1 #Documentation 2025-07-21 15:02:45 +02:00
Applevangelist 7d47227fab Merge remote-tracking branch 'origin/develop' into develop 2025-07-21 14:50:42 +02:00
Applevangelist 1f09b8fe0b Merge remote-tracking branch 'origin/master' into develop 2025-07-21 14:50:38 +02:00
Applevangelist 8c92a578ed #UTILS - added UTILS.SpawnMASHStatics() 2025-07-21 14:50:08 +02:00
Thomas 992f09d9ba Merge pull request #2342 from FlightControl-Master/master
Vnao Airboss exits
2025-07-21 09:11:12 +02:00
Thomas 096f2caf9c Merge pull request #2341 from nasgroup94/master
Added VNAO Edits
2025-07-21 09:09:54 +02:00
frankiep95 0b37c909b3 Added VNAO Edits 2025-07-20 16:58:33 -04:00
Applevangelist bdad38e707 Merge remote-tracking branch 'origin/master' into develop 2025-07-20 14:04:24 +02:00
Applevangelist 1b18ae1597 #MANTIS - Added SAMP/T, SA-17 data correction, HDS explanations expanded 2025-07-20 14:03:33 +02:00
Thomas 8dcc3cbe8f Merge pull request #2340 from shaji-Dev/develop
[ADDED] COMMANDER:SetMaxMissionsAssignPerCycle
2025-07-20 12:28:36 +02:00
smiki aa8f3a824c [ADDED] COMMANDER:SetMaxMissionsStartPerCycle 2025-07-20 12:12:31 +02:00
smiki b5a19528a1 [ADDED] COMMANDER:SetMaxMissionsStartPerCycle 2025-07-20 12:11:57 +02:00
Thomas bd20e8fc81 Merge pull request #2339 from shaji-Dev/develop
Develop
2025-07-20 11:51:57 +02:00
smiki 2f806801bc [ADDED] AUFTRAG:SetRepeatDelay 2025-07-20 11:48:35 +02:00
shaji-Dev be006bd2bf Merge branch 'FlightControl-Master:develop' into develop 2025-07-20 11:44:34 +02:00
Applevangelist 18a08288ec Merge remote-tracking branch 'origin/master' into develop 2025-07-19 18:36:40 +02:00
Applevangelist c9ac6d73e6 #MANTIS - Better documented use of SA-10B/C/12/23 naming usage with launcher differences. 2025-07-19 18:36:03 +02:00
Applevangelist f602455d1f Merge remote-tracking branch 'origin/master' into develop 2025-07-19 16:05:55 +02:00
Applevangelist d5c34a37b0 #AICSAR - Added functionality to use rescue zones instead of pure distance. Fixed helo not despawning after landing back at base. 2025-07-19 16:05:28 +02:00
Applevangelist 8a0e9a3400 #Fix SRS TTS folder path in documentation and defaults 2025-07-19 16:05:06 +02:00
Applevangelist 0e836973fd #Fix SRS TTS folder path in documentation and defaults 2025-07-19 16:04:49 +02:00
Applevangelist 1b57af88a0 Merge remote-tracking branch 'origin/master' into develop 2025-07-18 18:10:43 +02:00
Applevangelist be40d7be9a #SPAWNSTATIC - NewFromStatic now creates a new template in the database under the new name - if not already there. This allows #WAREHOUSE static warehouses spawned that way to be respawned eg on coalition change. 2025-07-18 18:09:57 +02:00
Applevangelist 00de8d911c Merge remote-tracking branch 'origin/develop' into develop 2025-07-18 09:24:58 +02:00
smiki 9df3fcfdf7 [FIXED] attempt to index local 'airbase' (a nil value) 2025-07-17 23:36:23 +02:00
Thomas d675e34f37 Merge pull request #2338 from shaji-Dev/develop
[FIXED] AIRWING operational regardless of airbase coalition
2025-07-17 07:38:43 +02:00
smiki 2d96ba0f56 [FIXED] AIRWING operational regardless of airbase coalition 2025-07-16 23:39:04 +02:00
smiki 21c2bd1103 [FIXED] AIRWING operational regardless of airbase coalition 2025-07-16 23:17:47 +02:00
Thomas f3b7740041 Merge pull request #2335 from shaji-Dev/master
[ADDED] GROUP.Attribute.GROUND_SHORAD
2025-07-15 11:19:41 +02:00
smiki 7d7488db6f [ADDED] GROUP.Attribute.GROUND_SHORAD 2025-07-15 11:05:03 +02:00
Thomas b9197d65d5 Merge pull request #2334 from shaji-Dev/develop
[ADDED] GROUP.Attribute.GROUND_SHORAD
2025-07-15 10:35:36 +02:00
smiki ec6f190b68 [ADDED] GROUP.Attribute.GROUND_SHORAD 2025-07-15 10:16:49 +02:00
Thomas 5e4f9f035b Merge pull request #2333 from shaji-Dev/develop
[FIXED] AUFTRAG and FSM state mismatch workaround `AUFTRAG:CheckGroupsDone`
2025-07-14 19:18:05 +02:00
smiki 7b6bf7f39b [FIXED] AUFTRAG and FSM state mismatch workaround for AUFTRAG:CheckGroupsDone 2025-07-14 19:04:26 +02:00
Thomas 2e8875dd2f Update Auftrag.lua
Fix catch all check groups 
2025-07-14 10:13:38 +02:00
Thomas 4964cc2f2d Merge pull request #2332 from FlightControl-Master/Applevangelist-Vertical
Controllable - add option prefer vertical landing
2025-07-09 12:18:10 +02:00
Thomas f0a4c5b008 Merge pull request #2331 from FlightControl-Master/Applevangelist-patch-2
Update Controllable.lua
2025-07-09 12:16:29 +02:00
Thomas 1b6412821b Update Controllable.lua 2025-07-09 12:15:34 +02:00
Thomas 926a0733e4 Controllable - add option prefer vertical landing
Addrd
2025-07-09 12:14:41 +02:00
Applevangelist 124ebd3240 xxx 2025-07-05 18:57:39 +02:00
Applevangelist 7728609165 Merge remote-tracking branch 'origin/master' into develop 2025-07-05 18:57:27 +02:00
Applevangelist da70f4ce6c #DynamicSlots for dynamic FARPs 2025-07-05 18:56:59 +02:00
Applevangelist 69b3e9abad Small fix 2025-07-04 18:47:08 +02:00
Applevangelist f9f77bfa7b xxx 2025-07-04 18:46:56 +02:00
Applevangelist 429db73854 #AUFTRAG - fix condition for assets done test 2025-07-04 16:22:55 +02:00
Applevangelist 1e9c45c115 Merge remote-tracking branch 'origin/master' into develop 2025-07-03 16:39:55 +02:00
Applevangelist 727cb3276c #SET fix for table insert of GetAliveSet 2025-07-03 16:39:24 +02:00
Applevangelist a68175448d Merge remote-tracking branch 'origin/master' into develop 2025-07-03 15:15:54 +02:00
Thomas 33e63a4819 Merge pull request #2330 from shaji-Dev/master
[FIXED] index `nil` at  `MARKEROPS_BASE:OnEventMark`
2025-07-03 15:14:11 +02:00
Applevangelist d7431478d1 Merge remote-tracking branch 'origin/master' into develop 2025-07-03 14:59:14 +02:00
smiki 3543b2c79a [FIXED] index nil at MARKEROPS_BASE:OnEventMark 2025-07-03 14:59:07 +02:00
Applevangelist 4489efff94 #POSITIONABLE - make GetVec3/GetCoordinate a bit more robust 2025-07-03 14:57:48 +02:00
Applevangelist dcdea379ef Merge remote-tracking branch 'origin/master' into develop 2025-07-03 11:59:23 +02:00
Applevangelist 6a4bddde99 #SET - do not create a new SET on GetAliveSet is we only send back the object table 2025-07-03 11:58:44 +02:00
Applevangelist 2088359756 Merge remote-tracking branch 'origin/master' into develop 2025-07-03 10:55:02 +02:00
Thomas dc2511942c Merge pull request #2329 from shaji-Dev/master
[FIXED] Memory leaks
2025-07-03 10:52:40 +02:00
Thomas f0c257c4a5 Merge branch 'master' into master 2025-07-03 10:52:29 +02:00
Applevangelist 068d21612f #MARKEROPS - do not crate a COORDINATE b4 you need it
#UTILS - added
* UTILS.ShowHelperGate(pos, heading)
* UTILS.ShellZone
* UTILS.RemoveObjects
* UTILS.DestroyScenery
2025-07-03 10:48:54 +02:00
smiki 773461aad9 [FIXED] Memory leaks 2025-07-03 09:54:07 +02:00
smiki f9257b2b0d [FIXED] Memory leaks 2025-07-03 09:45:21 +02:00
smiki 9e0f03a3cd [FIXED] Memory leaks 2025-07-03 08:44:41 +02:00
Applevangelist 0ab6a10ec4 #AWACS - added FSM transitions for VID Success and Failure 2025-06-29 15:50:24 +02:00
Thomas 73f393c542 Merge pull request #2328 from FlightControl-Master/master
Merge
2025-06-24 19:28:33 +02:00
Thomas a467fabdc8 Merge pull request #2327 from leka1986/patch-1
Update CTLD.lua
2025-06-24 19:26:36 +02:00
leka1986 a2ab84c45a Update CTLD.lua
Added Herc fix when dropping from air.

Added CratesName in the OnAfterCratesBuildStarted parm
2025-06-24 19:24:00 +02:00
Thomas 7ec7228cb8 Merge pull request #2326 from FlightControl-Master/master
Merge from master
2025-06-24 18:23:19 +02:00
Thomas 9fd6729967 Merge pull request #2325 from FlightControl-Master/Applevangelist-patch-1-1
Update Airbase.lua
2025-06-24 18:22:18 +02:00
Thomas f1d4f1753a Update Airbase.lua 2025-06-24 16:28:46 +02:00
Frank 1f268b3b5d Merge branch 'master' into develop 2025-06-23 22:43:13 +02:00
Frank 6d9c3fd0aa Merge pull request #2324 from FlightControl-Master/FF/MasterDevel
AIRBOSS Essex+Corsair
2025-06-23 22:41:16 +02:00
Frank 28ae63bd8d Update Airboss.lua 2025-06-23 22:39:52 +02:00
Frank 8d6d4b3765 Merge branch 'FF/MasterDevel' into FF/Ops 2025-06-23 22:29:30 +02:00
Frank 7ddb72885d Merge branch 'develop' into FF/Ops 2025-06-23 22:29:20 +02:00
Frank 42e7e3f94f Update Airboss.lua 2025-06-23 22:28:48 +02:00
Thomas 4a5204aecb Merge pull request #2323 from FlightControl-Master/master
Merge
2025-06-23 19:09:47 +02:00
Thomas 6466c5e95e Merge pull request #2322 from leka1986/master
Merge branch 'master' of https://github.com/FlightControl-Master/MOOSE
2025-06-23 19:02:56 +02:00
leka1986 829f5af25f Merge branch 'master' of https://github.com/FlightControl-Master/MOOSE 2025-06-23 18:28:08 +02:00
leka1986 0d1147bac4 Added the missing lines to check if the droped troop is engineer, then start the engineer instance. Added missing messages when dropping single crate type, ie truck for example. Added a call to refreshdropcratesmenu in the takeoff / land event. Drop and build is only created if fixedwing is on the ground. 2025-06-23 18:28:02 +02:00
Frank 24b47b02e0 AIRBOSS
- Essex
- Corsair
2025-06-22 22:29:52 +02:00
Thomas 10262fd30b Merge pull request #2321 from FlightControl-Master/master
Merge from master
2025-06-20 14:03:40 +02:00
Thomas 3cabc07d58 Merge pull request #2320 from shaji-Dev/master
[ADDED] New Kola Airbases
2025-06-20 14:01:24 +02:00
shaji b0546b1e60 [ADDED] New Kola Airbases 2025-06-20 12:58:50 +02:00
shaji a988e67490 [ADDED] New Kola Airbases 2025-06-20 12:20:58 +02:00
Thomas fa951838d2 Merge pull request #2319 from FlightControl-Master/master
Merge from master
2025-06-19 09:16:30 +02:00
Thomas 2594c5bbf0 Merge pull request #2318 from shaji-Dev/master
[FIXED] Error: attempt to index local 'Schedule' (a nil value)
2025-06-19 09:15:42 +02:00
shaji db70fa341c Merge remote-tracking branch 'origin/master' 2025-06-19 07:42:52 +02:00
shaji 763e3852ac [FIXED] Error: attempt to index local 'Schedule' (a nil value) 2025-06-19 07:42:29 +02:00
Thomas 66157d0596 Merge pull request #2316 from FlightControl-Master/master
Fixes from Master
2025-06-18 14:34:04 +02:00
Thomas 8ec86973c6 Update SpawnStatic.lua
Fix SpawnFromZone()
2025-06-18 14:29:34 +02:00
Thomas eb2c6ac6f2 Update SRS.lua
#MSRS Voice mapping correction
2025-06-18 14:19:50 +02:00
Applevangelist 93a8d8bc2d Merge remote-tracking branch 'origin/master' into develop 2025-06-15 17:02:35 +02:00
Applevangelist cbcc893ce5 #CTLD - avoid smoking runways on airbase zones 2025-06-15 17:01:58 +02:00
Applevangelist c19713949d Merge remote-tracking branch 'origin/master' into develop 2025-06-15 15:42:57 +02:00
Applevangelist 54450935a1 Merge remote-tracking branch 'origin/develop' into develop 2025-06-15 15:42:37 +02:00
Applevangelist d0f5712ca8 #AUFTRAG - Allow CAP Auftrag w/o Zone 2025-06-15 15:42:33 +02:00
Applevangelist 382b049c5f #AIRBASE - Syria and Sinai few names corrected 2025-06-15 15:38:12 +02:00
Thomas 6c2cc37abe Merge pull request #2315 from FlightControl-Master/master
Update Airbase.lua
2025-06-15 13:15:30 +02:00
Thomas a53763221c Update Airbase.lua
Correct afb name gor Borg al arab on Sinai
2025-06-15 13:14:05 +02:00
Thomas 83447a3fb4 Merge pull request #2314 from FlightControl-Master/master
Csar
2025-06-15 10:19:19 +02:00
Thomas b7bac28113 Merge pull request #2313 from FlightControl-Master/Applevangelist-patch-1
Update CSAR.lua
2025-06-15 10:18:01 +02:00
Thomas a9edb16554 Update CSAR.lua
Make static and zone mash SETs dynamic
2025-06-15 10:17:32 +02:00
Applevangelist 52cbb7202c #EASYGCICAP - more noob catches 2025-06-12 09:09:38 +02:00
Frank 1d04f7c945 Merge branch 'develop' into FF/Ops 2025-06-10 21:58:38 +02:00
Frank f6a8317c42 Merge branch 'develop' of https://github.com/FlightControl-Master/MOOSE into develop 2025-06-10 21:58:21 +02:00
Frank 2a9a7db9b8 OPSGROUP
- Improved behaviour when mission is unpaused and groups are teleported
2025-06-10 21:58:19 +02:00
Applevangelist 3aeec78f79 Merge remote-tracking branch 'origin/master' into develop 2025-06-10 18:05:58 +02:00
Applevangelist 0aeb1fc6af #UTILS - Small fix for GetReportingName to distinguish Shark from Mainstay 2025-06-10 18:05:02 +02:00
Applevangelist 26565d7549 #EASYGCICAP - Added noob/fatfinger checks for airbase static warehouses and CAP point airwing names 2025-06-10 09:25:00 +02:00
Applevangelist 62816f217b Merge remote-tracking branch 'origin/master' into develop 2025-06-08 18:43:35 +02:00
Applevangelist eeeeda4e5e #POINT - Offset options for smoke 2025-06-08 18:43:01 +02:00
Applevangelist 95767c5ef4 AIRBOSS - Remove useless E Messages for non-debug 2025-06-01 12:20:08 +02:00
Applevangelist f5881eda53 AIRBOSS - Remove useless E Messages for non-debug 2025-06-01 12:19:42 +02:00
Applevangelist fca6faa3a8 xx 2025-05-30 20:51:04 +02:00
Thomas c1997d9f70 Merge pull request #2311 from FlightControl-Master/Applevangelist-patch-1
Update CSAR.lua
2025-05-30 18:37:50 +02:00
Thomas bb1caa6642 Update CSAR.lua 2025-05-30 18:37:38 +02:00
Thomas 638f083729 Merge pull request #2310 from shaji-Dev/develop
CSAR Downed Pilot Queue
2025-05-30 18:36:33 +02:00
shaji 59d41cf98b - [ADDED] Removes previous downed pilot with the same player name in Ejection event
- [ADDED] `useFIFOLimitReplacement` If true, it will remove the oldest downed pilot when a new one is added, if the limit is reached (FIFO queue), otherwise does not add downed pilots when the limit is reached.
2025-05-30 16:03:01 +02:00
Applevangelist 793adafda7 Merge remote-tracking branch 'origin/master' into develop 2025-05-30 11:14:33 +02:00
Applevangelist dd5ca93f26 CSAR Small addition 2025-05-30 11:13:50 +02:00
Frank baa3f3234e Merge branch 'develop' into FF/Ops 2025-04-18 22:04:07 +02:00
Frank ab5a4c43c3 Merge branch 'develop' into FF/Ops 2025-04-17 21:40:58 +02:00
Frank 1f20b830b7 Merge branch 'develop' into FF/OpsDev 2025-04-17 21:40:41 +02:00
Frank ce61f454bf Update Beacons.lua
- Added excludelist to GetClosestBeacon function
2025-04-17 20:34:46 +02:00
Frank 4772dafe7f Update Beacons.lua
- Fixed bug in counting beacons
- Added option to mark certain beacon types
- Improved maker text
2025-04-15 22:06:53 +02:00
Frank 629925e2d8 Update Towns.lua 2025-04-14 22:42:29 +02:00
Frank 17f672dad4 Towns
- Added new Class for towns
2025-04-14 15:49:39 +02:00
Frank 0a9717a8c2 RADIOS
- Added new class RADIOS
2025-04-13 23:14:19 +02:00
Frank e0049bea2b Update Beacons.lua
- Added channel
- Added scenery
2025-04-12 21:18:19 +02:00
Frank 73525feb68 Update Beacons.lua
- Added user functions
- Fixed bug with incorrect position
2025-04-12 11:14:01 +02:00
Frank 0531b8f57e Merge branch 'FF/Ops' into FF/OpsDev 2025-04-09 22:47:34 +02:00
Frank 98e2997e26 Merge branch 'develop' into FF/OpsDev 2025-04-09 22:43:24 +02:00
Frank 7d3bffcfef Naviation
- Added beacons class
- Added navpoints class
- Added vector class
2025-04-09 22:36:26 +02:00
Frank 5b76ec6b99 Merge branch 'develop' into FF/Ops 2025-04-09 21:17:49 +02:00
Frank eeca95c77f Merge branch 'develop' into FF/Ops 2025-01-02 20:49:06 +01:00
Frank e406fb0c88 Update Brigade.lua 2024-12-29 21:02:56 +01:00
Frank bdec6eb82d Merge branch 'FF/Ops' into FF/OpsDev 2024-07-07 11:33:44 +02:00
Frank 9bdacfbcf7 Merge branch 'develop' into FF/OpsDev 2024-07-01 16:46:22 +02:00
Frank be2eda25df Merge branch 'develop' into FF/OpsDev 2024-06-27 21:31:24 +02:00
Frank fa8e1d2a56 Merge branch 'FF/Ops' into FF/OpsDev 2024-06-19 07:53:54 +02:00
Frank 5242754c60 Merge branch 'FF/Ops' into FF/OpsDev 2024-04-26 14:33:05 +02:00
Frank 3dbafd7a19 Update Astar.lua 2024-04-20 14:34:15 +02:00
Frank 56e751193a Merge branch 'FF/Ops' into FF/OpsDev 2024-04-04 09:35:53 +02:00
Frank b2b258cff0 Merge branch 'FF/Ops' into FF/OpsDev 2024-03-30 22:32:42 +01:00
Frank 75068a2a26 Merge branch 'FF/Ops' into FF/OpsDev 2024-03-24 17:25:02 +01:00
Frank d82df93eff Merge branch 'FF/Ops' into FF/OpsDev 2024-01-23 11:05:50 +01:00
Frank 5168380eac Update Modules.lua 2024-01-21 16:41:09 +01:00
Frank a7ffbab850 Update Modules.lua 2024-01-16 19:44:08 +01:00
Frank a238294079 Merge branch 'FF/Ops' into FF/OpsDev 2024-01-16 17:37:41 +01:00
Frank accb4b45fb Merge branch 'FF/Ops' into FF/OpsDev 2024-01-07 21:17:17 +01:00
Frank ba4ff5c9e9 Update Procedure.lua 2024-01-07 10:29:31 +01:00
Frank 171c450576 Merge branch 'FF/Ops' into FF/OpsDev 2024-01-04 15:14:25 +01:00
Frank f35078b426 Merge branch 'FF/Ops' into FF/OpsDev 2024-01-02 17:07:15 +01:00
Frank 8921007805 Update Point.lua 2024-01-02 17:04:04 +01:00
Frank 8fd3034503 Merge branch 'FF/Ops' into FF/OpsDev 2024-01-01 21:55:52 +01:00
Frank a20b6e8a62 Merge branch 'FF/Ops' into FF/OpsDev 2023-12-03 21:10:24 +01:00
Frank 4ce6cc7776 Merge branch 'FF/Ops' into FF/OpsDev 2023-11-17 18:06:17 +01:00
Frank 434296ab11 Merge branch 'FF/Ops' into FF/OpsDev 2023-11-07 00:06:35 +01:00
Frank a5232f49c0 Navigation 2023-11-06 21:35:20 +01:00
Frank befc8207f5 Update Nav 2023-11-02 23:42:59 +01:00
Frank 23947b7c30 Merge branch 'FF/Ops' into FF/OpsDev 2023-11-02 20:33:53 +01:00
Frank da91b710a9 Merge branch 'FF/Ops' into FF/OpsDev 2023-10-31 20:17:10 +01:00
Frank d656bbc014 Update Vector.lua 2023-10-19 21:49:45 +02:00
Frank ed23f11e4a Merge branch 'FF/Ops' into FF/OpsDev 2023-10-13 22:00:57 +02:00
Frank 068465a8f2 Merge branch 'FF/Ops' into FF/OpsDev 2023-10-13 21:33:33 +02:00
Frank 4e24a7bf80 Navigation 2023-10-08 21:49:15 +02:00
Frank 1ece7238dc NAv 2023-10-05 16:52:41 +02:00
Frank db53f427e3 Update Point.lua 2023-10-02 17:02:21 +02:00
Frank d2e2c51275 Update FlightPlan.lua 2023-09-29 13:27:59 +02:00
Frank 691748082b Merge branch 'FF/Ops' into FF/OpsDev 2023-09-27 22:29:38 +02:00
Frank b294ef10c8 Update NavyGroup.lua 2023-09-27 10:54:37 +02:00
Frank 5d40091947 NAVYGROUP
- Improved heading into wind
2023-09-27 10:53:19 +02:00
Frank 7453a6c55d navygroup
- into wind
2023-09-26 17:02:13 +02:00
Frank 153ef7cd08 utils 2023-09-26 01:44:30 +02:00
Frank b6a550a247 Update Vector.lua 2023-09-25 16:59:41 +02:00
Frank 3568f27150 Vector 2023-09-24 22:46:50 +02:00
Frank c46061466c VECTOR
- Added functions
2023-09-24 22:09:49 +02:00
Frank 9c7b5e8506 VECTOR
**VECTOR**
- New core class for vector algebra
2023-09-23 01:15:08 +02:00
Frank b11df6b523 Navigation
Refactored names etc.
2023-09-21 23:37:25 +02:00
Frank 66dcd44fb8 Merge branch 'FF/OpsDev' of https://github.com/FlightControl-Master/MOOSE into FF/OpsDev 2023-09-21 17:48:12 +02:00
Frank f35237f86f Update FlightControl.lua 2023-09-21 17:48:04 +02:00
Frank f19c877a11 Navigation 2023-09-21 17:01:32 +02:00
Frank 792a487eeb Merge branch 'FF/Ops' into FF/OpsDev 2023-09-20 22:32:16 +02:00
Frank f5e8bd0ffc Merge branch 'FF/Ops' into FF/OpsDev 2023-09-20 14:39:41 +02:00
Frank cbdbf36f32 FC
- Improved taxi phrases
2023-09-20 11:52:14 +02:00
Frank 1116b19b00 FC 2023-09-19 17:04:37 +02:00
Frank edb490fa97 Update Pathline.lua
- json
2023-09-19 03:01:47 +02:00
Frank 9fa16c6385 Update FlightControl.lua 2023-09-18 16:52:13 +02:00
Frank a0ee957493 Update FlightControl.lua 2023-09-17 23:07:59 +02:00
Frank f32c303c0a pathline 2023-09-17 22:58:53 +02:00
Frank 6febbbc8e6 FC 2023-09-17 21:55:29 +02:00
Frank d5aa9eaf0f Merge branch 'FF/Ops' into FF/OpsDev 2023-09-17 16:26:23 +02:00
Frank 321b2d761d FLIGHTCONTROL
- Improved Taxi comms
2023-09-17 16:10:59 +02:00
Frank 18581e4a78 Merge branch 'FF/OpsDev' of https://github.com/FlightControl-Master/MOOSE into FF/OpsDev 2023-09-14 17:40:11 +02:00
Frank b03905154d ASTAR Pathlines 2023-09-14 17:40:02 +02:00
Frank 2fda1709bc Update FlightControl.lua 2023-09-14 16:46:08 +02:00
Frank 39f626390a Merge branch 'FF/Ops' into FF/OpsDev 2023-09-13 11:14:55 +02:00
Frank aa1e12163d ASTAR/PATHLINE 2023-09-12 23:57:50 +02:00
Frank abb7f860ae Update Astar.lua 2023-09-12 03:21:23 +02:00
Frank cffada1a1e AIRBASE
- Taxiways
2023-09-11 16:54:10 +02:00
Frank f8d91798e3 Update FlightControl.lua 2023-09-10 23:55:40 +02:00
Frank 548aa8d5a9 Merge branch 'FF/Ops' into FF/OpsDev 2023-09-10 22:43:13 +02:00
Frank 1e929d1d19 ASTAR Taxi 2023-09-10 22:38:00 +02:00
Frank d5f215505e Merge branch 'FF/Ops' into FF/OpsDev 2023-09-09 00:32:29 +02:00
Frank 851660c793 NAV 2023-09-08 16:11:11 +02:00
Frank ee57b46c14 Update Astar.lua 2023-09-07 23:32:07 +02:00
Frank 36eac9fccc PATH 2023-09-06 22:50:24 +02:00
Frank 1f630ab490 Astar Pathline 2023-09-05 23:29:27 +02:00
Frank 8f0c6948ac Merge branch 'FF/Ops' into FF/OpsDev 2023-09-05 20:53:45 +02:00
Frank eb2380c3f6 Merge branch 'FF/Ops' into FF/OpsDev 2023-09-04 23:35:05 +02:00
Frank df3e182de4 Merge branch 'FF/OpsDev' of https://github.com/FlightControl-Master/MOOSE into FF/OpsDev 2023-09-04 23:34:26 +02:00
Frank bda7988118 Astar 2023-09-04 23:34:23 +02:00
Frank 72deb9ee17 Merge branch 'FF/OpsDev' of https://github.com/FlightControl-Master/MOOSE into FF/OpsDev 2023-08-30 15:53:49 +02:00
Frank ced1f30f3f Update DCS.lua 2023-08-30 15:53:35 +02:00
Frank 47c0006537 ASTAR & PATHLINE 2023-08-29 22:43:36 +02:00
Frank 21e5c124c0 Astar 2023-08-28 22:38:37 +02:00
Frank 3ff374e1b9 Merge branch 'FF/Ops' into FF/OpsDev 2023-08-27 20:39:51 +02:00
Frank 02000be9af Merge branch 'FF/Ops' into FF/OpsDev 2023-08-17 19:15:12 +02:00
Frank 663c6cead0 Update FlightControl.lua 2023-08-16 20:46:53 +02:00
Frank 6867df58c4 Merge branch 'FF/Ops' into FF/OpsDev 2023-08-13 18:22:12 +02:00
Frank 639625d3d5 Update FlightControl.lua 2023-08-13 14:16:40 +02:00
Frank 0ace200e5a Nav
- Small stuff
2023-08-04 00:08:16 +02:00
Frank e76c26ff59 FLIGHTPLAN 2023-08-03 00:41:34 +02:00
Frank cb11de6f9c Navigation
- Added NavFix
2023-08-02 10:53:28 +02:00
Frank cdd78e5163 Update FlightPlan.lua 2023-08-01 00:25:08 +02:00
Frank a8da02774a Update FlightPlan.lua 2023-08-01 00:24:49 +02:00
Frank 04258a69c4 Navigation
- Init stuff
2023-07-31 21:58:01 +02:00
Frank 49882f03d9 Update FlightControl.lua 2023-07-26 16:05:15 +02:00
70 changed files with 18416 additions and 6295 deletions
+401 -40
View File
@@ -157,6 +157,8 @@ ASTAR = {
-- @field #number surfacetype Surface type.
-- @field #table valid Cached valid/invalid nodes.
-- @field #table cost Cached cost.
-- @field Core.Pathline#PATHLINE pathline Pathline that node is part of.
-- @field Core.Pathline#PATHLINE.Point pathpoint Pathline point.
--- ASTAR infinity.
-- @field #number INF
@@ -164,7 +166,7 @@ ASTAR.INF=1/0
--- ASTAR class version.
-- @field #string version
ASTAR.version="0.4.0"
ASTAR.version="0.5.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@@ -172,6 +174,7 @@ ASTAR.version="0.4.0"
-- TODO: Add more valid neighbour functions.
-- TODO: Write docs.
-- DONE: Add pathlines for seach/valid neighbours.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
@@ -244,7 +247,10 @@ end
function ASTAR:AddNode(Node)
self.nodes[Node.id]=Node
self.Nnodes=self.Nnodes+1
self.Nnodes=self.Nnodes+1
self:T3(self.lid..string.format("Adding node UID=%d", Node.id))
--Node.coordinate:MarkToAll(string.format("Node ID=%d", Node.id))
return self
end
@@ -262,6 +268,47 @@ function ASTAR:AddNodeFromCoordinate(Coordinate)
return node
end
--- Adds nodes to the table of grid nodes from a PATHLINE.
-- @param #ASTAR self
-- @param Core.Pathline#PATHLINE Pathline Pathline or name of pathline. Has to exist.
-- @return #ASTAR self
function ASTAR:AddNodeFromPathlineName(Pathline)
if type(Pathline)=="string" then
Pathline=PATHLINE:FindByName(Pathline)
end
if Pathline then
for i,_point in pairs(Pathline.points) do
local point=_point --Core.Pathline#PATHLINE.Point
-- Create node from point coordinate.
local node=self:AddNodeFromCoordinate(COORDINATE:NewFromVec3(point.vec3))
-- Add pathline parameters.
node.pathline=Pathline
node.pathpoint=point
-- Debug.
local name=node.pathline and node.pathline.name or "N/A"
local idx=node.pathline and node.pathline:_GetPointIndex(node.pathpoint) or "N/A"
-- Debug message.
self:T(self.lid..string.format("Adding node UID=%d pathline=%s [%s]", node.id, name, tostring(idx)))
-- Debug mark
--node.coordinate:MarkToAll(string.format("Node ID=%d\npathline=%s [%s]", node.id, name, tostring(idx)))
end
else
env.error("FF error pathline")
end
return self
end
--- Check if the coordinate of a node has is at a valid surface type.
-- @param #ASTAR self
-- @param #ASTAR.Node Node The node to be added.
@@ -340,6 +387,18 @@ function ASTAR:SetValidNeighbourRoad(MaxDistance)
return self
end
--- Set valid neighbours to be on the same pathline or not further apart than 10 meters to jump from one pathline to another.
-- @param #ASTAR self
-- @param #number MaxDistance Max allowed distance between nodes of different pathlines in meters. Default is 10 m.
-- @return #ASTAR self
function ASTAR:SetValidNeighbourPathline(MaxDistance)
self:SetValidNeighbourFunction(ASTAR.Pathline, MaxDistance)
return self
end
--- Set the function which calculates the "cost" to go from one to another node.
-- The first to arguments of this function are always the two nodes under consideration. But you can add optional arguments.
-- Very often the distance between nodes is a good measure for the cost.
@@ -384,7 +443,7 @@ end
-- @return #ASTAR self
function ASTAR:SetCostRoad()
self:SetCostFunction(ASTAR)
self:SetCostFunction(ASTAR.Road)
return self
end
@@ -544,6 +603,55 @@ function ASTAR.Road(nodeA, nodeB)
end
--- Function to check if two nodes are on the same pathline or if nodes are less than 10 meters apart.
-- @param #ASTAR.Node nodeA First node.
-- @param #ASTAR.Node nodeB Other node.
-- @param #number distmax Max distance in meters. Default is 10 m.
-- @return #boolean If true, two nodes are connected.
function ASTAR.Pathline(nodeA, nodeB, distmax)
distmax=distmax or 10
if nodeA.pathline.name==nodeB.pathline.name then
-- Nodes are on the same pathline. We use the index to check if they are neighbours.
local pathline=nodeA.pathline
local idxA=pathline:_GetPointIndex(nodeA.pathpoint)
local idxB=pathline:_GetPointIndex(nodeB.pathpoint)
if math.abs(idxA-idxB)<=1 then
return true
end
else
-- Check if nodeB is close to pathline of nodeA.
local c, dist, segA=nodeA.pathline:GetClosestPoint3D(nodeB.coordinate)
local seg=segA --Core.Pathline#PATHLINE.Segment
if dist<distmax and (nodeA.pathpoint.uid==seg.p1.uid or nodeA.pathpoint.uid==seg.p2.uid) then
--env.info(string.format("FF NodeB=%d [pathline=%s] is close to NodeA=%d [pathline=%s] ==> valid neighbour", nodeB.id, nodeB.pathline.name, nodeA.id, nodeA.pathline.name))
return true
end
-- Check if nodeA is close to pathline of nodeB.
local c, dist, segB=nodeB.pathline:GetClosestPoint3D(nodeA.coordinate)
local seg=segB --Core.Pathline#PATHLINE.Segment
if dist<distmax and (nodeB.pathpoint.uid==seg.p1.uid or nodeB.pathpoint.uid==seg.p2.uid) then
--env.info(string.format("FF NodeA=%d [pathline=%s] is close to NodeB=%d [pathline=%s] ==> valid neighbour", nodeA.id, nodeA.pathline.name, nodeB.id, nodeB.pathline.name))
return true
end
end
return false
end
--- Function to check if distance between two nodes is less than a threshold distance.
-- @param #ASTAR.Node nodeA First node.
-- @param #ASTAR.Node nodeB Other node.
@@ -567,7 +675,9 @@ end
-- @param #ASTAR.Node nodeB Other node.
-- @return #number Distance between the two nodes.
function ASTAR.Dist2D(nodeA, nodeB)
local dist=nodeA.coordinate:Get2DDistance(nodeB)
local dist=nodeA.coordinate:Get2DDistance(nodeB.coordinate)
--local text=string.format("FF Cost Dist2D NodeA=%d-->NodeB=%d = %.1f", nodeA.id, nodeB.id, dist)
--env.info(text)
return dist
end
@@ -594,7 +704,7 @@ function ASTAR.DistRoad(nodeA, nodeB)
local dist=0
for i=2,#path do
local b=path[i] --DCS#Vec2
local b=path[i] --DCS#Vec2
local a=path[i-1] --DCS#Vec2
dist=dist+UTILS.VecDist2D(a,b)
@@ -604,7 +714,6 @@ function ASTAR.DistRoad(nodeA, nodeB)
return dist
end
return math.huge
end
@@ -614,10 +723,11 @@ end
--- Find the closest node from a given coordinate.
-- @param #ASTAR self
-- @param Core.Point#COORDINATE Coordinate.
-- @return #ASTAR.Node Cloest node to the coordinate.
-- @param Core.Point#COORDINATE Coordinate Reference coordinate.
-- @param #table ExcludeNodes Table of nodes that are excluded.
-- @return #ASTAR.Node Closest node to the coordinate.
-- @return #number Distance to closest node in meters.
function ASTAR:FindClosestNode(Coordinate)
function ASTAR:FindClosestNode(Coordinate, ExcludeNodes)
local distMin=math.huge
local closeNode=nil
@@ -625,11 +735,15 @@ function ASTAR:FindClosestNode(Coordinate)
for _,_node in pairs(self.nodes) do
local node=_node --#ASTAR.Node
local dist=node.coordinate:Get2DDistance(Coordinate)
if ExcludeNodes==nil or self:_IsNodeNotInTable(ExcludeNodes, node) then
if dist<distMin then
distMin=dist
closeNode=node
local dist=node.coordinate:Get2DDistance(Coordinate)
if dist<distMin then
distMin=dist
closeNode=node
end
end
end
@@ -637,38 +751,162 @@ function ASTAR:FindClosestNode(Coordinate)
return closeNode, distMin
end
--- Find the closest pathline to a given reference coordinate.
-- @param #ASTAR self
-- @param Core.Point#COORDINATE Coordinate Reference coordinate.
-- @return Core.Pathline#PATHLINE Closest pathline
-- @return #number Distance in meters.
-- @return DCS#Vec3 Closest point on pathline to the ref coordinate.
-- @return Core.Pathline#PATHLINE.Segment Segment.
function ASTAR:FindClosestPathline(Coordinate)
local pathline=nil --Core.Pathline#PATHLINE
local dist=math.huge
local vec3=nil
local S=nil
for _,_node in pairs(self.nodes) do
local node=_node --#ASTAR.Node
if node.pathline then
local vec, d, s=node.pathline:GetClosestPoint3D(Coordinate)
if d<dist then
pathline=node.pathline
dist=d
vec3=vec
S=s
end
end
end
if pathline then
-- Debug info.
self:T(self.lid..string.format("Closest pathline %s: dist=%.1f", pathline.name, dist))
end
return pathline, dist, vec3, S
end
--- Find the closest node to the given coordinate.
-- @param #ASTAR self
-- @param Core.Point#COORDINATE Coord Reference coordinate.
-- @param #table ExcludeNodes Nodes that are excluded.
-- @return #ASTAR.Node The node that was fround
function ASTAR:_FindClosestTerminalNode(Coord, ExcludeNodes)
-- Find the closest pathline to the ref coordinate.
local pathline, dist, vec3, s=self:FindClosestPathline(Coord)
-- Find the closest node to the given start coordinate.
local node, dist2=self:FindClosestNode(Coord)
if pathline and vec3 and dist and dist2>dist then
-- Create a node on the closest pathline so we first go straight there and then along the pathline.
local node=self:AddNodeFromCoordinate(COORDINATE:NewFromVec3(vec3))
-- We also need the pathline point.
local point=pathline:AddPointFromVec3(vec3, nil, s.p1)
node.pathline=pathline
node.pathpoint=point
self:T2(self.lid..string.format("Added new node=%d, which is closest to start coord. dist=%.1f m", node.id, dist))
end
-- Find the closest node to the given start coordinate.
local Node, dist3=self:FindClosestNode(Coord, ExcludeNodes)
-- Debug info.
self:T(self.lid..string.format("CLOSEST node ID=%d, distance=%.1f", Node.id, dist3))
return Node, dist3
end
--- Find the start node.
-- @param #ASTAR self
-- @param #ASTAR.Node Node The node to be added to the nodes table.
-- @return #ASTAR self
function ASTAR:FindStartNode()
-- Find the closest pathline to the
local pathline, dist, vec3, s=self:FindClosestPathline(self.startCoord)
local node, dist=self:FindClosestNode(self.startCoord)
-- Find the closest node to the given start coordinate.
local node, dist2=self:FindClosestNode(self.startCoord)
self.startNode=node
if dist>1000 then
self:T(self.lid.."Adding start node to node grid!")
self:AddNode(node)
if pathline and vec3 and dist and dist2>dist then
-- Create a node on the closest pathline so we first go straight there and then along the pathline.
local node=self:AddNodeFromCoordinate(COORDINATE:NewFromVec3(vec3))
-- We also need the pathline point.
local point=pathline:AddPointFromVec3(vec3, nil, s.p1)
node.pathline=pathline
node.pathpoint=point
self:T2(self.lid..string.format("Added new node=%d, which is closest to start coord. dist=%.1f m", node.id, dist))
end
-- Find the closest node to the given start coordinate.
self.startNode, dist2=self:FindClosestNode(self.startCoord)
--self.startNode.coordinate:MarkToAll("Start Node")
-- Debug info.
self:T(self.lid..string.format("START node ID=%d", self.startNode.id))
-- Not sure why I did this. The node does not need to be added again as it is already contained in self.nodes!
-- if dist>1000 then
-- self:T(self.lid.."Adding start node to node grid!")
-- self:AddNode(node)
-- end
return self
end
--- Add a node.
--- Find the end node.
-- @param #ASTAR self
-- @param #ASTAR.Node Node The node to be added to the nodes table.
-- @return #ASTAR self
function ASTAR:FindEndNode()
local node, dist=self:FindClosestNode(self.endCoord)
self.endNode=node
if dist>1000 then
self:T(self.lid.."Adding end node to node grid!")
self:AddNode(node)
local pathline, dist, vec3, s=self:FindClosestPathline(self.endCoord)
-- Find the closest node to the given start coordinate.
local node, dist2=self:FindClosestNode(self.endCoord)
if pathline and vec3 and dist and dist2>dist then
-- Create a node on the closest pathline so we first go straight there and then along the pathline.
local node=self:AddNodeFromCoordinate(COORDINATE:NewFromVec3(vec3))
-- We also need the point.
local point=pathline:AddPointFromVec3(vec3, nil, s.p1)
-- Add pathline parameters to node.
node.pathline=pathline
node.pathpoint=point
self:T2(self.lid..string.format("Added new node=%d, which is closest to END coord: dist=%.1f m", node.id, dist))
end
-- Find closest node to the end coordinate (exclude the start coordinate.
self.endNode, dist=self:FindClosestNode(self.endCoord, {self.startNode})
--self.endNode.coordinate:MarkToAll("End Node")
-- Debug info.
self:T(self.lid..string.format("END node ID=%d", self.endNode.id))
-- Not sure why I did this. The node does not need to be added again as it is already contained in self.nodes!
-- if dist>1000 then
-- self:T(self.lid.."Adding end node to node grid!")
-- self:AddNode(node)
-- end
return self
end
@@ -684,12 +922,21 @@ end
-- @return #table Table of nodes from start to finish.
function ASTAR:GetPath(ExcludeStartNode, ExcludeEndNode)
self:FindStartNode()
self:FindEndNode()
-- self:FindStartNode()
-- self:FindEndNode()
-- Find start Node (closest node to start coordinate).
self.startNode=self:_FindClosestTerminalNode(self.startCoord)
-- Find end node, which is not the start node (excluded).
self.endNode=self:_FindClosestTerminalNode(self.endCoord, {self.startNode})
local nodes=self.nodes
local start=self.startNode
local goal=self.endNode
-- Debug info.
self:T(self.lid..string.format("GetPath Start Node=%d, End Node=%d", start.id, goal.id))
-- Sets.
local openset = {}
@@ -746,7 +993,12 @@ function ASTAR:GetPath(ExcludeStartNode, ExcludeEndNode)
text=text..string.format(", OS Time %.6f sec", dT)
end
text=text..string.format(", Nvalid=%d [%d cached]", self.nvalid, self.nvalidcache)
text=text..string.format(", Ncost=%d [%d cached]", self.ncost, self.ncostcache)
text=text..string.format(", Ncost=%d [%d cached]", self.ncost, self.ncostcache)
text=text..string.format("\nNodes:")
for i,_node in ipairs(path) do
local node=_node --#ASTAR.Node
text=text..string.format("\n[%d] Node ID=%d", i, node.id)
end
self:T(self.lid..text)
return path
@@ -759,13 +1011,16 @@ function ASTAR:GetPath(ExcludeStartNode, ExcludeEndNode)
-- Get neighbour nodes.
local neighbors=self:_NeighbourNodes(current, nodes)
-- Loop over neighbours.
for _,neighbor in pairs(neighbors) do
-- Node is not in closed set.
if self:_NotIn(closedset, neighbor.id) then
local tentative_g_score=g_score[current.id]+self:_DistNodes(current, neighbor)
-- Calculate tentative_g_score.
--local tentative_g_score=g_score[current.id] + self:_DistNodes(current, neighbor)
local tentative_g_score=g_score[current.id] + self:_HeuristicCost(current, neighbor)
if self:_NotIn(openset, neighbor.id) or tentative_g_score < g_score[neighbor.id] then
@@ -793,6 +1048,73 @@ function ASTAR:GetPath(ExcludeStartNode, ExcludeEndNode)
return nil -- no valid path
end
--- A* pathfinding function. This seaches the path along nodes between start and end nodes/coordinates.
-- It automatically creates a PATHLINE object that is returned in combination with the nodes of the optimal path.
-- @param #ASTAR self
-- @param #boolean ExcludeStartNode If *true*, do not include start node in found path. Default is to include it.
-- @param #boolean ExcludeEndNode If *true*, do not include end node in found path. Default is to include it.
-- @return Core.Pathline#PATHLINE Pathline.
-- @return #table Nodes of path.
function ASTAR:GetPathline(ExcludeStartNode, ExcludeEndNode)
local nodes=self:GetPath(ExcludeStartNode, ExcludeEndNode)
local pathline=nil --Core.Pathline#PATHLINE
if nodes then
pathline=PATHLINE:New("Astar")
for _,_node in pairs(nodes) do
local node=_node --#ASTAR.Node
local point=pathline:AddPointFromVec3(node.coordinate)
point.name=node.pathline.name
end
end
return pathline, nodes
end
--- Get pathlines from nodes.
-- @param #ASTAR self
-- @param #table Nodes Given nodes.
-- @return #table Table of PATHLINES used in the path.
function ASTAR:GetPathlinesFromNodes(Nodes)
local pathlines={}
--for _,_node in pairs(Nodes or {}) do
for i=1,#Nodes do
local node=Nodes[i] --#ASTAR.Node
-- Pathline.
local pathline=node.pathline
if pathline and i>1 and i<#Nodes then
-- Previous and next nodes.
local n=Nodes[i-1] --#ASTAR.Node
local N=Nodes[i+1] --#ASTAR.Node
-- Check if previous and next nodes are on the same pathline.
-- If only one point in beteen is of another pathline, this is a junction and we dont actually switch to the other pathline.
if n.pathline and N.pathline and n.pathline.name==N.pathline.name and n.pathline.name~=pathline.name then
pathline=n.pathline
end
end
-- We do not want to add the same pathline two times in a row.
if #pathlines==0 or (#pathlines>0 and pathlines[#pathlines].name~=pathline.name) then
table.insert(pathlines, pathline)
end
end
return pathlines
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- A* pathfinding helper functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -811,16 +1133,18 @@ function ASTAR:_HeuristicCost(nodeA, nodeB)
local cost=nodeA.cost[nodeB.id]
if cost~=nil then
self.ncostcache=self.ncostcache+1
self:T(self.lid..string.format("Cost nodeA=%d --> nodeB=%d = %.1f (Cashed!)", nodeA.id, nodeB.id, cost))
return cost
end
local cost=nil
if self.CostFunc then
cost=self.CostFunc(nodeA, nodeB, unpack(self.CostArg))
else
cost=self:_DistNodes(nodeA, nodeB)
end
self:T(self.lid..string.format("Cost nodeA=%d --> nodeB=%d = %.1f", nodeA.id, nodeB.id, cost))
nodeA.cost[nodeB.id]=cost
nodeB.cost[nodeA.id]=cost -- Symmetric problem.
@@ -834,9 +1158,10 @@ end
-- @return #boolean If true, transition between nodes is possible.
function ASTAR:_IsValidNeighbour(node, neighbor)
-- Counter.
-- Counter of function calls.
self.nvalid=self.nvalid+1
-- Check if neighbour is in cached set.
local valid=node.valid[neighbor.id]
if valid~=nil then
--env.info(string.format("Node %d has valid=%s neighbour %d", node.id, tostring(valid), neighbor.id))
@@ -844,13 +1169,16 @@ function ASTAR:_IsValidNeighbour(node, neighbor)
return valid
end
-- Check if this is a valid neighbour.
local valid=nil
if self.ValidNeighbourFunc then
valid=self.ValidNeighbourFunc(node, neighbor, unpack(self.ValidNeighbourArg))
else
-- If no valid neighbour function is defined, we assume all nodes are valid neighbours.
valid=true
end
-- Cache valid neighbour.
node.valid[neighbor.id]=valid
neighbor.valid[node.id]=valid -- Symmetric problem.
@@ -884,6 +1212,9 @@ function ASTAR:_LowestFscore(set, f_score)
end
end
-- Debug info.
self:T(self.lid..string.format("Lowest Fscore=%.1f, Node=%s", lowest, tostring(bestNode)))
return self.nodes[bestNode]
end
@@ -928,16 +1259,46 @@ end
-- @param #table map Map.
-- @param #ASTAR.Node current_node The current node.
-- @return #table Unwinded path.
function ASTAR:_UnwindPath( flat_path, map, current_node )
function ASTAR:_UnwindPath(flat_path, map, current_node)
if map [current_node] then
table.insert (flat_path, 1, map[current_node])
return self:_UnwindPath(flat_path, map, map[current_node])
local previous_node=map[current_node]
if previous_node then
table.insert(flat_path, 1, previous_node)
return self:_UnwindPath(flat_path, map, previous_node)
else
-- No previous node ==> return path.
return flat_path
end
end
--- Function to check if a certain node is in a given table.
-- @param #ASTAR self
-- @param #table Nodes Nodes table.
-- @param #ASTAR.Node Node The node to check.
-- @return #boolean If true, the node is not in the set.
function ASTAR:_IsNodeInTable(Nodes, Node)
for _,_node in pairs(Nodes) do
local node=_node --#ASTAR.Node
if node.id==Node.id then
return true
end
end
return false
end
--- Function to check if a certain node is **not** in a given table.
-- @param #ASTAR self
-- @param #table Nodes Nodes table.
-- @param #ASTAR.Node Node The node to check.
-- @return #boolean If true, the node is not in the set.
function ASTAR:_IsNodeNotInTable(Nodes, Node)
local is=self:_IsNodeInTable(Nodes, Node)
return not is
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+12 -2
View File
@@ -20,7 +20,7 @@
--
-- @module Core.ClientMenu
-- @image Core_Menu.JPG
-- last change: Jan 2025
-- last change: Sept 2025
-- TODO
----------------------------------------------------------------------------------------------------------------
@@ -417,7 +417,7 @@ end
CLIENTMENUMANAGER = {
ClassName = "CLIENTMENUMANAGER",
lid = "",
version = "0.1.6",
version = "0.1.7",
name = nil,
clientset = nil,
menutree = {},
@@ -806,6 +806,16 @@ function CLIENTMENUMANAGER:ResetMenuComplete()
return self
end
--- Remove the entry and all entries below the given entry from the client's F10 menus.
-- @param #CLIENTMENUMANAGER self
-- @param #CLIENTMENU Entry The entry to remove
-- @param Wrapper.Client#CLIENT Client (optional) If given, make this change only for this client.
-- @return #CLIENTMENUMANAGER self
function CLIENTMENUMANAGER:DeleteEntry(Entry,Client)
self:T(self.lid.."DeleteEntry")
return self:DeleteF10Entry(Entry,Client)
end
--- Remove the entry and all entries below the given entry from the client's F10 menus.
-- @param #CLIENTMENUMANAGER self
-- @param #CLIENTMENU Entry The entry to remove
+18 -12
View File
@@ -577,13 +577,19 @@ do -- Zones and Pathlines
-- For a rectangular polygon drawing, we have the width (y) and height (x).
local w=objectData.width
local h=objectData.height
local rotation = UTILS.ToRadian(objectData.angle or 0)
-- Create points from center using with and height (width for y and height for x is a bit confusing, but this is how ED implemented it).
local points={}
points[1]={x=vec2.x-h/2, y=vec2.y+w/2} --Upper left
points[2]={x=vec2.x+h/2, y=vec2.y+w/2} --Upper right
points[3]={x=vec2.x+h/2, y=vec2.y-w/2} --Lower right
points[4]={x=vec2.x-h/2, y=vec2.y-w/2} --Lower left
local sinRot = math.sin(rotation)
local cosRot = math.cos(rotation)
local dx = h / 2
local dy = w / 2
local points = {
{ x = -dx * cosRot - (-dy * sinRot) + vec2.x, y = -dx * sinRot + (-dy * cosRot) + vec2.y },
{ x = dx * cosRot - (-dy * sinRot) + vec2.x, y = dx * sinRot + (-dy * cosRot) + vec2.y },
{ x = dx * cosRot - (dy * sinRot) + vec2.x, y = dx * sinRot + (dy * cosRot) + vec2.y },
{ x = -dx * cosRot - (dy * sinRot) + vec2.x, y = -dx * sinRot + (dy * cosRot) + vec2.y },
}
--local coord=COORDINATE:NewFromVec2(vec2):MarkToAll("MapX, MapY")
@@ -1112,7 +1118,7 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, Category
self:E("WARNING: Invalid STN "..tostring(UnitTemplate.AddPropAircraft.STN_L16).." for ".. UnitTemplate.name)
else
self.STNS[stn] = UnitTemplate.name
self:I("Register STN "..tostring(UnitTemplate.AddPropAircraft.STN_L16).." for ".. UnitTemplate.name)
self:T("Register STN "..tostring(UnitTemplate.AddPropAircraft.STN_L16).." for ".. UnitTemplate.name)
end
end
if UnitTemplate.AddPropAircraft.SADL_TN then
@@ -1121,7 +1127,7 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, Category
self:E("WARNING: Invalid SADL "..tostring(UnitTemplate.AddPropAircraft.SADL_TN).." for ".. UnitTemplate.name)
else
self.SADL[sadl] = UnitTemplate.name
self:I("Register SADL "..tostring(UnitTemplate.AddPropAircraft.SADL_TN).." for ".. UnitTemplate.name)
self:T("Register SADL "..tostring(UnitTemplate.AddPropAircraft.SADL_TN).." for ".. UnitTemplate.name)
end
end
end
@@ -1382,7 +1388,7 @@ function DATABASE:GetCoalitionFromClientTemplate( ClientName )
if self.Templates.ClientsByName[ClientName] then
return self.Templates.ClientsByName[ClientName].CoalitionID
end
self:E("WARNING: Template does not exist for client "..tostring(ClientName))
self:T("WARNING: Template does not exist for client "..tostring(ClientName))
return nil
end
@@ -1394,7 +1400,7 @@ function DATABASE:GetCategoryFromClientTemplate( ClientName )
if self.Templates.ClientsByName[ClientName] then
return self.Templates.ClientsByName[ClientName].CategoryID
end
self:E("WARNING: Template does not exist for client "..tostring(ClientName))
self:T("WARNING: Template does not exist for client "..tostring(ClientName))
return nil
end
@@ -1406,7 +1412,7 @@ function DATABASE:GetCountryFromClientTemplate( ClientName )
if self.Templates.ClientsByName[ClientName] then
return self.Templates.ClientsByName[ClientName].CountryID
end
self:E("WARNING: Template does not exist for client "..tostring(ClientName))
self:T("WARNING: Template does not exist for client "..tostring(ClientName))
return nil
end
@@ -1699,7 +1705,7 @@ function DATABASE:_EventOnBirth( Event )
if PlayerName then
-- Debug info.
self:I(string.format("Player '%s' joined unit '%s' of group '%s'", tostring(PlayerName), tostring(Event.IniDCSUnitName), tostring(Event.IniDCSGroupName)))
self:I(string.format("Player '%s' joined unit '%s' (%s) of group '%s'", tostring(PlayerName), tostring(Event.IniDCSUnitName), tostring(Event.IniTypeName), tostring(Event.IniDCSGroupName)))
-- Add client in case it does not exist already.
if client == nil or (client and client:CountPlayers() == 0) then
+4 -2
View File
@@ -1475,7 +1475,7 @@ function EVENT:onEvent( Event )
-- SCENERY
---
Event.TgtDCSUnit = Event.target
Event.TgtDCSUnitName = Event.TgtDCSUnit.getName and Event.TgtDCSUnit.getName() or nil
Event.TgtDCSUnitName = Event.TgtDCSUnit.getName and Event.TgtDCSUnit:getName() or nil
if Event.TgtDCSUnitName~=nil then
Event.TgtUnitName = Event.TgtDCSUnitName
Event.TgtUnit = SCENERY:Register( Event.TgtDCSUnitName, Event.target )
@@ -1508,7 +1508,9 @@ function EVENT:onEvent( Event )
else
if Event.place:isExist() and Object.getCategory(Event.place) ~= Object.Category.SCENERY then
Event.Place=AIRBASE:Find(Event.place)
Event.PlaceName=Event.Place:GetName()
if Event.Place then
Event.PlaceName=Event.Place:GetName()
end
end
end
end
+1
View File
@@ -79,6 +79,7 @@
do -- FSM
--- FSM class
-- @type FSM
-- @field #string ClassName Name of the class.
-- @field Core.Scheduler#SCHEDULER CallScheduler Call scheduler.
+18 -13
View File
@@ -50,7 +50,7 @@ MARKEROPS_BASE = {
ClassName = "MARKEROPS",
Tag = "mytag",
Keywords = {},
version = "0.1.3",
version = "0.1.4",
debug = false,
Casesensitive = true,
}
@@ -154,14 +154,7 @@ function MARKEROPS_BASE:OnEventMark(Event)
self:E("Skipping onEvent. Event or Event.idx unknown.")
return true
end
--position
local vec3={y=Event.pos.y, x=Event.pos.x, z=Event.pos.z}
local coord=COORDINATE:NewFromVec3(vec3)
if self.debug then
local coordtext = coord:ToStringLLDDM()
local text = tostring(Event.text)
local m = MESSAGE:New(string.format("Mark added at %s with text: %s",coordtext,text),10,"Info",false):ToAll()
end
local coalition = Event.MarkCoalition
-- decision
if Event.id==world.event.S_EVENT_MARK_ADDED then
@@ -170,8 +163,14 @@ function MARKEROPS_BASE:OnEventMark(Event)
local Eventtext = tostring(Event.text)
if Eventtext~=nil then
if self:_MatchTag(Eventtext) then
local matchtable = self:_MatchKeywords(Eventtext)
self:MarkAdded(Eventtext,matchtable,coord,Event.idx,coalition,Event.PlayerName,Event)
local coord=COORDINATE:NewFromVec3({y=Event.pos.y, x=Event.pos.x, z=Event.pos.z})
if self.debug then
local coordtext = coord:ToStringLLDDM()
local text = tostring(Event.text)
local m = MESSAGE:New(string.format("Mark added at %s with text: %s",coordtext,text),10,"Info",false):ToAll()
end
local matchtable = self:_MatchKeywords(Eventtext)
self:MarkAdded(Eventtext,matchtable,coord,Event.idx,coalition,Event.PlayerName,Event)
end
end
elseif Event.id==world.event.S_EVENT_MARK_CHANGE then
@@ -180,8 +179,14 @@ function MARKEROPS_BASE:OnEventMark(Event)
local Eventtext = tostring(Event.text)
if Eventtext~=nil then
if self:_MatchTag(Eventtext) then
local matchtable = self:_MatchKeywords(Eventtext)
self:MarkChanged(Eventtext,matchtable,coord,Event.idx,coalition,Event.PlayerName,Event)
local coord=COORDINATE:NewFromVec3({y=Event.pos.y, x=Event.pos.x, z=Event.pos.z})
if self.debug then
local coordtext = coord:ToStringLLDDM()
local text = tostring(Event.text)
local m = MESSAGE:New(string.format("Mark changed at %s with text: %s",coordtext,text),10,"Info",false):ToAll()
end
local matchtable = self:_MatchKeywords(Eventtext)
self:MarkChanged(Eventtext,matchtable,coord,Event.idx,coalition,Event.PlayerName,Event)
end
end
elseif Event.id==world.event.S_EVENT_MARK_REMOVED then
+7 -7
View File
@@ -452,7 +452,7 @@ end
_MESSAGESRS = {}
--- Set up MESSAGE generally to allow Text-To-Speech via SRS and TTS functions. `SetMSRS()` will try to use as many attributes configured with @{Sound.SRS#MSRS.LoadConfigFile}() as possible.
-- @param #string PathToSRS (optional) Path to SRS Folder, defaults to "C:\\\\Program Files\\\\DCS-SimpleRadio-Standalone" or your configuration file setting.
-- @param #string PathToSRS (optional) Path to SRS TTS Folder, defaults to "C:\\\\Program Files\\\\DCS-SimpleRadio-Standalone\\ExternalAudio" or your configuration file setting.
-- @param #number Port Port (optional) number of SRS, defaults to 5002 or your configuration file setting.
-- @param #string PathToCredentials (optional) Path to credentials file for Google.
-- @param #number Frequency Frequency in MHz. Can also be given as a #table of frequencies.
@@ -468,13 +468,13 @@ _MESSAGESRS = {}
-- @usage
-- -- Mind the dot here, not using the colon this time around!
-- -- Needed once only
-- MESSAGE.SetMSRS("D:\\Program Files\\DCS-SimpleRadio-Standalone",5012,nil,127,radio.modulation.FM,"female","en-US",nil,coalition.side.BLUE)
-- MESSAGE.SetMSRS("D:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio",5012,nil,127,radio.modulation.FM,"female","en-US",nil,coalition.side.BLUE)
-- -- later on in your code
-- MESSAGE:New("Test message!",15,"SPAWN"):ToSRS()
--
function MESSAGE.SetMSRS(PathToSRS,Port,PathToCredentials,Frequency,Modulation,Gender,Culture,Voice,Coalition,Volume,Label,Coordinate,Backend)
_MESSAGESRS.PathToSRS = PathToSRS or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone"
_MESSAGESRS.PathToSRS = PathToSRS or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio"
_MESSAGESRS.frequency = Frequency or MSRS.frequencies or 243
_MESSAGESRS.modulation = Modulation or MSRS.modulations or radio.modulation.AM
@@ -535,7 +535,7 @@ end
-- @usage
-- -- Mind the dot here, not using the colon this time around!
-- -- Needed once only
-- MESSAGE.SetMSRS("D:\\Program Files\\DCS-SimpleRadio-Standalone",5012,nil,127,radio.modulation.FM,"female","en-US",nil,coalition.side.BLUE)
-- MESSAGE.SetMSRS("D:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio",5012,nil,127,radio.modulation.FM,"female","en-US",nil,coalition.side.BLUE)
-- -- later on in your code
-- MESSAGE:New("Test message!",15,"SPAWN"):ToSRS()
--
@@ -567,7 +567,7 @@ end
-- @usage
-- -- Mind the dot here, not using the colon this time around!
-- -- Needed once only
-- MESSAGE.SetMSRS("D:\\Program Files\\DCS-SimpleRadio-Standalone",5012,nil,127,radio.modulation.FM,"female","en-US",nil,coalition.side.BLUE)
-- MESSAGE.SetMSRS("D:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio",5012,nil,127,radio.modulation.FM,"female","en-US",nil,coalition.side.BLUE)
-- -- later on in your code
-- MESSAGE:New("Test message!",15,"SPAWN"):ToSRSBlue()
--
@@ -589,7 +589,7 @@ end
-- @usage
-- -- Mind the dot here, not using the colon this time around!
-- -- Needed once only
-- MESSAGE.SetMSRS("D:\\Program Files\\DCS-SimpleRadio-Standalone",5012,nil,127,radio.modulation.FM,"female","en-US",nil,coalition.side.RED)
-- MESSAGE.SetMSRS("D:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio",5012,nil,127,radio.modulation.FM,"female","en-US",nil,coalition.side.RED)
-- -- later on in your code
-- MESSAGE:New("Test message!",15,"SPAWN"):ToSRSRed()
--
@@ -611,7 +611,7 @@ end
-- @usage
-- -- Mind the dot here, not using the colon this time around!
-- -- Needed once only
-- MESSAGE.SetMSRS("D:\\Program Files\\DCS-SimpleRadio-Standalone",5012,nil,127,radio.modulation.FM,"female","en-US",nil,coalition.side.NEUTRAL)
-- MESSAGE.SetMSRS("D:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio",5012,nil,127,radio.modulation.FM,"female","en-US",nil,coalition.side.NEUTRAL)
-- -- later on in your code
-- MESSAGE:New("Test message!",15,"SPAWN"):ToSRSAll()
--
+492 -53
View File
@@ -5,11 +5,13 @@
-- * Path from A to B
-- * Arbitrary number of points
-- * Automatically from lines drawtool
-- * Draw line or mark points on F10 map
-- * Find closest points to path
--
-- ===
--
-- ### Author: **funkyfranky**
--
--
-- ===
-- @module Core.Pathline
-- @image CORE_Pathline.png
@@ -21,6 +23,7 @@
-- @field #string lid Class id string for output to DCS log file.
-- @field #string name Name of the path line.
-- @field #table points List of 3D points defining the path.
-- @field #number counter Running number counting the point IDs.
-- @extends Core.Base#BASE
--- *The shortest distance between two points is a straight line.* -- Archimedes
@@ -28,30 +31,30 @@
-- ===
--
-- # The PATHLINE Concept
--
--
-- List of points defining a path from A to B. The pathline can consist of multiple points. Each point holds the information of its position, the surface type, the land height
-- and the water depth (if over sea).
--
--
-- Line drawings created in the mission editor are automatically registered as pathlines and stored in the MOOSE database.
-- They can be accessed with the @{#PATHLINE.FindByName) function.
--
--
-- # Constructor
--
--
-- The @{PATHLINE.New) function creates a new PATHLINE object. This does not hold any points. Points can be added with the @{#PATHLINE.AddPointFromVec2} and @{#PATHLINE.AddPointFromVec3}
--
--
-- For a given table of 2D or 3D positions, a new PATHLINE object can be created with the @{#PATHLINE.NewFromVec2Array} or @{#PATHLINE.NewFromVec3Array}, respectively.
--
--
-- # Line Drawings
--
--
-- The most convenient way to create a pathline is the draw panel feature in the DCS mission editor. You can select "Line" and then "Segments", "Segment" or "Free" to draw your lines.
-- These line drawings are then automatically added to the MOOSE database as PATHLINE objects and can be retrieved with the @{#PATHLINE.FindByName) function, where the name is the one
-- you specify in the draw panel.
--
--
-- # Mark on F10 map
--
-- The ponints of the PATHLINE can be marked on the F10 map with the @{#PATHLINE.MarkPoints}(`true`) function. The mark points contain information of the surface type, land height and
-- The points of the PATHLINE can be marked on the F10 map with the @{#PATHLINE.MarkPoints}(`true`) function. The mark points contain information of the surface type, land height and
-- water depth.
--
--
-- To remove the marks, use @{#PATHLINE.MarkPoints}(`false`).
--
-- @field #PATHLINE
@@ -59,27 +62,34 @@ PATHLINE = {
ClassName = "PATHLINE",
lid = nil,
points = {},
counter = 0,
}
--- Point of line.
-- @type PATHLINE.Point
-- @field #number uid Unique ID of this point.
-- @field #string mother Name of the pathline this point belongs to.
-- @field #string name Name of this point.
-- @field DCS#Vec3 vec3 3D position.
-- @field DCS#Vec2 vec2 2D position.
-- @field #number surfaceType Surface type.
-- @field #number landHeight Land height in meters.
-- @field #number depth Water depth in meters.
-- @field #number markerID Marker ID.
-- @field #number lineID Line marker ID.
--- PATHLINE class version.
-- @field #string version
PATHLINE.version="0.1.1"
PATHLINE.version="0.2.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: A lot...
-- TODO: Read/write to JSON file
-- TODO: Translate/rotate pathline
-- TODO: Add color.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
@@ -93,10 +103,10 @@ function PATHLINE:New(Name)
-- Inherit everything from INTEL class.
local self=BASE:Inherit(self, BASE:New()) --#PATHLINE
self.name=Name or "Unknown Path"
self.lid=string.format("PATHLINE %s | ", Name)
self.lid=string.format("PATHLINE %s | ", self.name)
return self
end
@@ -151,41 +161,70 @@ end
--- Add a point to the path from a given 2D position. The third dimension is determined from the land height.
-- @param #PATHLINE self
-- @param DCS#Vec2 Vec2 The 2D vector (x,y) to add.
-- @param #number Index Index to add this point, *e.g.* 1 for first point or 2 for second point. Default is at the end.
-- @param #PATHLINE.Point Point Add point after given point. Default is at the end or at given index.
-- @return #PATHLINE self
function PATHLINE:AddPointFromVec2(Vec2)
function PATHLINE:AddPointFromVec2(Vec2, Index, Point)
if Vec2 then
-- Create a new point.
local point=self:_CreatePoint(Vec2)
table.insert(self.points, point)
if Index then
-- Add at given index.
table.insert(self.points, Index, point)
else
if Point then
-- Get index of given point.
local i=self:_GetPointIndex(Point)
-- Add new point after given point.
table.insert(self.points, i+1, point)
else
-- Add add the end.
table.insert(self.points, point)
end
end
end
return self
end
--- Add a point to the path from a given 3D position.
-- @param #PATHLINE self
-- @param DCS#Vec3 Vec3 The 3D vector (x,y) to add.
-- @return #PATHLINE self
function PATHLINE:AddPointFromVec3(Vec3)
-- @param #number Index Index to add this point, *e.g.* 1 for first point or 2 for second point. Default is at the end.
-- @param #PATHLINE.Point Point Add point after given point. Default is at the end or at given index.
-- @return #PATHLINE.Point Point that was added.
function PATHLINE:AddPointFromVec3(Vec3, Index, Point)
if Vec3 then
local point=self:_CreatePoint(Vec3)
table.insert(self.points, point)
if Index then
-- Add add given index.
table.insert(self.points, Index, point)
else
if Point then
local i=self:_GetPointIndex(Point)
table.insert(self.points, i+1, point)
else
-- Add add the end.
table.insert(self.points, point)
end
end
return point
end
return self
return nil
end
--- Get name of pathline.
-- @param #PATHLINE self
-- @return #string Name of the pathline.
function PATHLINE:GetName()
function PATHLINE:GetName()
return self.name
end
@@ -199,18 +238,35 @@ end
--- Get points of pathline. Not that points are tables, that contain more information as just the 2D or 3D position but also the surface type etc.
-- @param #PATHLINE self
-- @return #list <#PATHLINE.Point> List of points.
function PATHLINE:GetPoints()
-- @return #list <Core.Pathline#PATHLINE.Point> List of points.
function PATHLINE:GetPoints()
return self.points
end
--- Get segments of pathline.
-- @param #PATHLINE self
-- @return #list <Core.Pathline#PATHLINE.Segment> List of points.
function PATHLINE:GetSetments()
local segments={}
for i=1,#self.points-1 do
local segment={} --#PATHLINE.Segment
segment.p1=self.points[i]
segment.p2=self.points[i+1]
table.insert(segments, segment)
end
return segments
end
--- Get 3D points of pathline.
-- @param #PATHLINE self
-- @return <DCS#Vec3> List of DCS#Vec3 points.
-- @return #list <DCS#Vec3> List of DCS#Vec3 points.
function PATHLINE:GetPoints3D()
local vecs={}
for _,_point in pairs(self.points) do
local point=_point --#PATHLINE.Point
table.insert(vecs, point.vec3)
@@ -221,11 +277,11 @@ end
--- Get 2D points of pathline.
-- @param #PATHLINE self
-- @return <DCS#Vec2> List of DCS#Vec2 points.
-- @return #list <DCS#Vec2> List of DCS#Vec2 points.
function PATHLINE:GetPoints2D()
local vecs={}
for _,_point in pairs(self.points) do
local point=_point --#PATHLINE.Point
table.insert(vecs, point.vec2)
@@ -240,11 +296,11 @@ end
function PATHLINE:GetCoordinates()
local vecs={}
for _,_point in pairs(self.points) do
local point=_point --#PATHLINE.Point
local coord=COORDINATE:NewFromVec3(point.vec3)
table.insert(vecs,coord)
table.insert(vecs, coord)
end
return vecs
@@ -257,11 +313,11 @@ end
function PATHLINE:GetPointFromIndex(n)
local N=self:GetNumberOfPoints()
n=n or 1
local point=nil --#PATHLINE.Point
if n>=1 and n<=N then
point=self.points[n]
else
@@ -278,11 +334,11 @@ end
function PATHLINE:GetPoint3DFromIndex(n)
local point=self:GetPointFromIndex(n)
if point then
return point.vec3
end
return nil
end
@@ -293,26 +349,52 @@ end
function PATHLINE:GetPoint2DFromIndex(n)
local point=self:GetPointFromIndex(n)
if point then
return point.vec2
end
return nil
end
--- Calculate the length of the line.
-- @param #PATHLINE self
-- @param #boolean Project2D Calculate 2D distance between points.
-- @return #number Length in meters.
function PATHLINE:GetLength(Project2D)
local l=0
local np=#self.points
for i=1,np-1 do
local p1=self.points[i] --#PATHLINE.Point
local p2=self.points[i+1] --#PATHLINE.Point
if Project2D then
l=l+UTILS.VecDist2D(p1.vec2, p2.vec2)
else
l=l+UTILS.VecDist3D(p1.vec3, p2.vec3)
end
end
return l
end
--- Mark points on F10 map.
-- @param #PATHLINE self
-- @param #boolean Switch If `true` or nil, set marks. If `false`, remove marks.
-- @return <DCS#Vec3> List of DCS#Vec3 points.
-- @return #PATHLINE self
function PATHLINE:MarkPoints(Switch)
for i,_point in pairs(self.points) do
local point=_point --#PATHLINE.Point
if Switch==false then
if point.markerID then
UTILS.RemoveMark(point.markerID, Delay)
UTILS.RemoveMark(point.markerID)
end
else
@@ -320,17 +402,350 @@ function PATHLINE:MarkPoints(Switch)
if point.markerID then
UTILS.RemoveMark(point.markerID)
end
else
if point.markerID then
UTILS.RemoveMark(point.markerID)
end
point.markerID=UTILS.GetMarkID()
local text=string.format("Pathline %s: Point #%d\nSurface Type=%d\nHeight=%.1f m\nDepth=%.1f m", self.name, i, point.surfaceType, point.landHeight, point.depth)
local text=string.format("Pathline %s: Point #%d [UID=%d]\nSurface Type=%d\nHeight=%.1f m\nDepth=%.1f m", self.name, i, point.uid, point.surfaceType, point.landHeight, point.depth)
trigger.action.markToAll(point.markerID, text, point.vec3, "")
end
end
return self
end
--- Draw line on F10 map.
-- @param #PATHLINE self
-- @param #number Recipient Recipent of the line: -1=All.
-- @param #table Color Color as RGB table plus alpha value. Default {1, 0, 0, 1.0}.
-- @param #number LineType Line type: 1=Solid (default).
-- @return #PATHLINE self
function PATHLINE:DrawLine(Recipient, Color, LineType)
-- Input
Recipient= Recipient or -1
Color= Color or {1,0,0, 1.0}
LineType=LineType or 1
local ReadOnly=false
local np=#self.points
for i=1,np-1 do
local p1=self.points[i] --#PATHLINE.Point
local p2=self.points[i+1] --#PATHLINE.Point
p1.lineID = UTILS.GetMarkID()
trigger.action.lineToAll(Recipient, p1.lineID, p1.vec3, p2.vec3, Color, LineType, ReadOnly, "")
end
return self
end
--- Remove line on F10 map.
-- @param #PATHLINE self
-- @param #number Delay Delay in seconds before line is removed.
-- @return #PATHLINE self
function PATHLINE:UnDrawLine(Delay)
local np=#self.points
for _,_point in pairs(self.points) do
local p=_point --#PATHLINE.Point
if p.lineID then
UTILS.RemoveMark(p.lineID, Delay)
end
end
return self
end
--- Draw line on F10 map.
-- @param #PATHLINE self
-- @param #boolean Switch If `true` or nil, draw pathline. If `false`, remove drawing.
-- @param #number Coalition Coalition side. Default -1 for all.
-- @param #table Color RGB color and alpha `{r, g, b, a}`. Default {0, 1, 0, 0.5}.
-- @param #number LineType Line type. Default 1=solid.
-- @return #PATHLINE self
function PATHLINE:Draw(Switch, Coalition, Color, LineType)
Coalition=Coalition or -1
Color=Color or {0, 1, 0, 0.5}
LineType=LineType or 1
if Switch==false then
for i,_point in pairs(self.points) do
local point=_point --#PATHLINE.Point
if point.lineID then
UTILS.RemoveMark(point.lineID)
end
end
else
for i=2,#self.points do
local p1=self.points[i-1] --#PATHLINE.Point
local p2=self.points[i] --#PATHLINE.Point
if p2.lineID then
UTILS.RemoveMark(p2.lineID)
end
p2.lineID=UTILS.GetMarkID()
trigger.action.lineToAll(Coalition, p2.lineID, p1.vec3, p2.vec3, Color, LineType)
end
end
return self
end
--- Get the closest point on the pathline for a given reference point.
-- @param #PATHLINE self
-- @param DCS#Vec2 Vec2 Reference Point in 2D.
-- @return DCS#Vec2 Cloest point on pathline.
-- @return #number Distance from closest point to ref point in meters.
-- @return #PATHLINE.Segment Closest segment of ref point.
function PATHLINE:GetClosestPoint2D(Vec2)
local P=nil --DCS#Vec2
local D=math.huge
local S={} --#PATHLINE.Segment
for i=2,#self.points do
local A=self.points[i-1] --#PATHLINE.Point
local B=self.points[i] --#PATHLINE.Point
local a=A.vec2
local b=B.vec2
local ab=UTILS.Vec2Substract(b, a)
local ap=UTILS.Vec2Substract(Vec2, a)
local proj=UTILS.Vec2Dot(ap, ab)
local lab=UTILS.Vec2Norm(ab)
local f=proj/lab/lab
-- Debug info.
local text=string.format("FF Proj=%.1f, |ab|=%.1f, f=%.1f", proj, lab, f)
self:T(self.lid..text)
-- Cases for finite segment.
local p=nil --DCS#Vec2
if f<0 then
p=a
elseif f>1 then
p=b
else
local r=UTILS.Vec2Mult(ab, f)
p=UTILS.Vec2Add(a, r)
end
-- Distance.
local d=UTILS.VecDist2D(p, Vec2)
if d<=D then
D=d
P=p
S.p1=A
S.p2=B
end
end
return P, D, S
end
--- Get the closest point on the pathline for a given reference point.
-- This point does not necessarily is a node of the pathline. In general it will be somewhere in between the nodes defining the pathline.
-- @param #PATHLINE self
-- @param DCS#Vec3 Vec3 Reference Point in 3D. Can also be a `COORDINATE`.
-- @return DCS#Vec3 Closest point on pathline.
-- @return #number Distance from closest point to ref point in meters.
-- @return #PATHLINE.Segment Closest segment of ref point.
function PATHLINE:GetClosestPoint3D(Vec3)
local P=nil --DCS#Vec3
local D=math.huge
local S={} --#PATHLINE.Segment
if not Vec3 then
self:E(self.lid.."ERROR: input Vec3 is nil!")
return nil, nil, nil
end
for i=2,#self.points do
local A=self.points[i-1] --#PATHLINE.Point
local B=self.points[i] --#PATHLINE.Point
local a=A.vec3
local b=B.vec3
local ab=UTILS.VecSubstract(b, a)
local ap=UTILS.VecSubstract(Vec3, a)
local proj=UTILS.VecDot(ap, ab)
local lab=UTILS.VecNorm(ab)
local f=proj/lab/lab
-- Debug info.
self:T(self.lid..string.format("Proj=%.1f, |ab|=%.1f, f=%.1f", proj, lab, f))
-- Cases for finite segment.
local p=nil --DCS#Vec2
if f<0 then
p=a
elseif f>1 then
p=b
else
local r=UTILS.VecMult(ab, f)
p=UTILS.VecAdd(a, r)
end
-- Distance.
local d=UTILS.VecDist3D(p, Vec3)
if d<=D then
D=d
P=p
S.p1=A
S.p2=B
end
end
return P, D, S
end
--- Write PATHLINE to JSON file.
-- **NOTE**: Requires `io` and `lfs` to be de-sanitized!
-- @param #PATHLINE self
-- @param #string FileName Name of the file. Default is the name of the pathline.
-- @return #PATHLINE self
function PATHLINE:WriteJSON(FileName)
if io and lfs then
-- JSON script.
local json=loadfile("Scripts\\JSON.lua")()
local data={}
-- We store the name and the points.
data.name=self.name
data.points=self.points
for i,_point in pairs(self.points) do
local point=_point --#PATHLINE.Point
--point.markerID=nil
end
-- Encode data to raw JSON. Encode converts a lua table into JSON string that can be written to file.
local raw_json=json:encode(data)
-- Debug data.
self:T(data)
-- Write in "User/Saved Games/" Folder.
local filepath=lfs.writedir() .. FileName
-- Open file for writing.
local f = io.open(filepath, "wb")
if f then
f:write(raw_json)
f:close()
self:T(self.lid .. string.format("Saving PATHLINE %s file %s", self.name, tostring(filepath)))
else
self:E(self.lid .. string.format( "ERROR: Could not save PATHLINE to file %s", tostring(filepath)))
end
else
self:E(self.lid .. string.format( "ERROR: Could not save results because IO and/or LFS are not de-sanitized!"))
end
end
--- Read PATHLINE from JSON file.
-- **NOTE**: Requires `io` and `lfs` to be de-sanitized!
-- @param #PATHLINE self
-- @param #string FileName Name of the file.
-- @return #PATHLINE self
function PATHLINE:NewFromJSON(FileName)
if io and lfs then
-- JSON script.
local json=loadfile("Scripts\\JSON.lua")()
local data={}
-- Write in "User/Saved Games/" Folder.
local filepath=lfs.writedir() .. FileName
--env.info(filepath)
-- Open file in binary mode for reading.
local f = io.open(filepath, "rb")
if f then
data = f:read("*all")
f:close()
else
env.info(string.format("WARNING: Could not load PATHLINE from file %s!", tostring(filepath)))
return nil
end
-- Decode JSON data to get a lua table.
local data=json:decode(data)
if data and data.name then
-- Create a new pathline instance.
local self=PATHLINE:New(data.name)
for i=1,#data.points do
local point=data.points[i] --#PATHLINE.Point
-- Create new point from data.
local p=self:AddPointFromVec3(point.vec3)
-- Set name.
p.name=point.name
-- Remove marker ID.
p.markerID=nil
end
return self
else
BASE:E("ERROR: Cannot find pathline name in data from JSON file. File may be corrupted!")
end
else
BASE:E("ERROR: IO and/or LFS not de-sanitized! Cannot read file.")
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Private functions
@@ -339,32 +754,56 @@ end
--- Get 3D points of pathline.
-- @param #PATHLINE self
-- @param DCS#Vec3 Vec Position vector. Can also be a DCS#Vec2 in which case the altitude at landheight is taken.
-- @return #PATHLINE.Point
-- @return #PATHLINE.Point Pathline Point.
function PATHLINE:_CreatePoint(Vec)
local point={} --#PATHLINE.Point
self.counter=self.counter+1
point.uid=self.counter
point.mother=self.name
point.name=string.format("%s #%d", self.name, point.uid)
if Vec.z then
-- Given vec is 3D
point.vec3=UTILS.DeepCopy(Vec)
point.vec2={x=Vec.x, y=Vec.z}
else
-- Given vec is 2D
-- Given vec is 2D
point.vec2=UTILS.DeepCopy(Vec)
point.vec3={x=Vec.x, y=land.getHeight(Vec), z=Vec.y}
end
-- Get surface type.
point.surfaceType=land.getSurfaceType(point.vec2)
-- Get land height and depth.
point.landHeight, point.depth=land.getSurfaceHeightWithSeabed(point.vec2)
point.markerID=nil
return point
end
--- Get index of point in the lua table.
-- @param #PATHLINE self
-- @param #PATHLINE.Point Point Given point.
-- @return #number index
function PATHLINE:_GetPointIndex(Point)
for i,_point in pairs(self.points) do
local point=_point --#PATHLINE.Point
if point.uid==Point.uid then
return i
end
end
return nil
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+177 -173
View File
@@ -3,8 +3,6 @@
-- ## Features:
--
-- * Provides a COORDINATE class, which allows to manage points in 3D space and perform various operations on it.
-- * Provides a POINT\_VEC2 class, which is derived from COORDINATE, and allows to manage points in 3D space, but from a Lat/Lon and Altitude perspective.
-- * Provides a POINT\_VEC3 class, which is derived from COORDINATE, and allows to manage points in 3D space, but from a X, Z and Y vector perspective.
--
-- ===
--
@@ -59,6 +57,10 @@ do -- COORDINATE
-- * @{#COORDINATE.SmokeOrange}(): To smoke the point in orange.
-- * @{#COORDINATE.SmokeWhite}(): To smoke the point in white.
-- * @{#COORDINATE.SmokeGreen}(): To smoke the point in green.
-- * @{#COORDINATE.SetSmokeOffsetDirection}(): To set an offset point direction for smoke.
-- * @{#COORDINATE.SetSmokeOffsetDistance}(): To set an offset point distance for smoke.
-- * @{#COORDINATE.SwitchSmokeOffsetOn}(): To set an offset point for smoke to on.
-- * @{#COORDINATE.SwitchSmokeOffsetOff}(): To set an offset point for smoke to off.
--
-- ## 2.2) Flare
--
@@ -453,6 +455,23 @@ do -- COORDINATE
end
--- Returns the coordinate from the latitude and longitude given in degrees, minutes and seconds (DMS).
-- @param #COORDINATE self
-- @param #string Latitude Latitude in DMS as string, e.g. "`42° 24' 14.3"`". Not that the characters `°`, `'` and `"` are important.
-- @param #string Longitude Longitude in DMS as string, e.g. "`42° 24' 14.3"`". Not that the characters `°`, `'` and `"` are important.
-- @param #number Altitude (Optional) Altitude in meters. Default is the land height at the coordinate.
-- @return #COORDINATE
function COORDINATE:NewFromLLDMS(Latitude, Longitude, Altitude)
local lat=UTILS.LLDMSstringToDD(Latitude)
local lon=UTILS.LLDMSstringToDD(Longitude)
self=COORDINATE:NewFromLLDD(lat, lon, Altitude)
return self
end
--- Returns if the 2 coordinates are at the same 2D position.
-- @param #COORDINATE self
-- @param #COORDINATE Coordinate
@@ -661,7 +680,7 @@ do -- COORDINATE
local _,_,_,_,_,scenerys=self:ScanObjects(radius, false, false, true)
local set={}
for _,_scenery in pairs(scenerys) do
local scenery=_scenery --DCS#Object
@@ -773,7 +792,9 @@ do -- COORDINATE
-- @return DCS#Vec2 Vec2
function COORDINATE:GetRandomVec2InRadius( OuterRadius, InnerRadius )
self:F2( { OuterRadius, InnerRadius } )
math.random()
math.random()
math.random()
local Theta = 2 * math.pi * math.random()
local Radials = math.random() + math.random()
if Radials > 1 then
@@ -833,6 +854,26 @@ do -- COORDINATE
return land.getHeight( Vec2 )
end
--- Returns a table of DCS#Vec3 points representing the terrain profile between two points.
-- @param #COORDINATE self
-- @param Destination DCS#Vec3 Ending point of the profile.
-- @return #table DCS#Vec3 table of the profile
function COORDINATE:GetLandProfileVec3(Destination)
return land.profile(self:GetVec3(), Destination)
end
--- Returns a table of #COORDINATE representing the terrain profile between two points.
-- @param #COORDINATE self
-- @param Destination #COORDINATE Ending coordinate of the profile.
-- @return #table #COORDINATE table of the profile
function COORDINATE:GetLandProfileCoordinates(Destination)
local points = self:GetLandProfileVec3(Destination:GetVec3())
local coords = {}
for _, point in ipairs(points) do
table.insert(coords, COORDINATE:NewFromVec3(point))
end
return coords
end
--- Set the heading of the coordinate, if applicable.
-- @param #COORDINATE self
@@ -1611,6 +1652,7 @@ do -- COORDINATE
if AirbaseCategory == Airbase.Category.SHIP or AirbaseCategory == Airbase.Category.HELIPAD then
RoutePoint.linkUnit = AirbaseID
RoutePoint.helipadId = AirbaseID
RoutePoint.airdromeId = airbase:IsAirdrome() and AirbaseID or nil
elseif AirbaseCategory == Airbase.Category.AIRDROME then
RoutePoint.airdromeId = AirbaseID
else
@@ -2033,6 +2075,40 @@ do -- COORDINATE
return Path, Way, GotPath
end
--- Returns a table of coordinates to a destination using only roads or railroads.
-- The first point is the closest point on road of the given coordinate.
-- By default, the last point is the closest point on road of the ToCoord. Hence, the coordinate itself and the final ToCoord are not necessarily included in the path.
-- @param #COORDINATE self
-- @param #COORDINATE ToCoord Coordinate of destination.
-- @param #boolean IncludeEndpoints (Optional) Include the coordinate itself and the ToCoordinate in the path.
-- @param #boolean Railroad (Optional) If true, path on railroad is returned. Default false.
-- @return Core.Pathline#PATHLINE Pathline containing the points on road. If no path on road can be found, nil is returned or just the endpoints.
function COORDINATE:GetPathlineOnRoad(ToCoord, IncludeEndpoints, Railroad)
-- Set road type.
local RoadType="roads"
if Railroad==true then
RoadType="railroads"
end
-- DCS API function returning a table of vec2.
local path = land.findPathOnRoads(RoadType, self.x, self.z, ToCoord.x, ToCoord.z)
if IncludeEndpoints then
path=path or {}
table.insert(path, 1, self:GetVec2())
table.insert(path, ToCoord:GetVec2())
end
local pathline=nil
if path then
pathline=PATHLINE:NewFromVec2Array(RoadType, path)
end
return pathline
end
--- Gets the surface type at the coordinate.
-- @param #COORDINATE self
@@ -2124,21 +2200,32 @@ do -- COORDINATE
-- @param #number Duration (Optional) Duration of the smoke in seconds. DCS stopps the smoke automatically after 5 min.
-- @param #number Delay (Optional) Delay before the smoke is started in seconds.
-- @param #string Name (Optional) Name if you want to stop the smoke early (normal duration: 5mins)
-- @param #boolean Offset (Optional) If true, offset the smokle a bit.
-- @param #number Direction (Optional) If Offset is true this is the direction of the offset, 1-359 (degrees). Default random.
-- @param #number Distance (Optional) If Offset is true this is the distance of the offset in meters. Default random 10-20.
-- @return #COORDINATE self
function COORDINATE:Smoke( SmokeColor, Duration, Delay, Name)
self:F2( { SmokeColor, Name, Duration, Delay } )
function COORDINATE:Smoke( SmokeColor, Duration, Delay, Name, Offset,Direction,Distance)
self:F2( { SmokeColor, Name, Duration, Delay, Offset } )
SmokeColor=SmokeColor or SMOKECOLOR.Green
if Delay and Delay>0 then
self:ScheduleOnce(Delay, COORDINATE.Smoke, self, SmokeColor, Duration, 0, Name)
self:ScheduleOnce(Delay, COORDINATE.Smoke, self, SmokeColor, Duration, 0, Name, Direction,Distance)
else
-- Create a name which is used to stop the smoke manually
self.firename = Name or "Smoke-"..math.random(1,100000)
-- Create smoke
trigger.action.smoke( self:GetVec3(), SmokeColor, self.firename )
if Offset or self.SmokeOffset then
local Angle = Direction or self:GetSmokeOffsetDirection()
local Distance = Distance or self:GetSmokeOffsetDistance()
local newpos = self:Translate(Distance,Angle,true,false)
local newvec3 = newpos:GetVec3()
trigger.action.smoke( newvec3, SmokeColor, self.firename )
else
trigger.action.smoke( self:GetVec3(), SmokeColor, self.firename )
end
-- Stop smoke
if Duration and Duration>0 then
@@ -2148,6 +2235,72 @@ do -- COORDINATE
return self
end
--- Get the offset direction when using `COORDINATE:Smoke()`.
-- @param #COORDINATE self
-- @return #number Direction in degrees.
function COORDINATE:GetSmokeOffsetDirection()
local direction = self.SmokeOffsetDirection or math.random(1,359)
return direction
end
--- Set the offset direction when using `COORDINATE:Smoke()`.
-- @param #COORDINATE self
-- @param #number Direction (Optional) This is the direction of the offset, 1-359 (degrees). Default random.
-- @return #COORDINATE self
function COORDINATE:SetSmokeOffsetDirection(Direction)
if self then
self.SmokeOffsetDirection = Direction or math.random(1,359)
return self
else
COORDINATE.SmokeOffsetDirection = Direction or math.random(1,359)
end
end
--- Get the offset distance when using `COORDINATE:Smoke()`.
-- @param #COORDINATE self
-- @return #number Distance Distance in meters.
function COORDINATE:GetSmokeOffsetDistance()
local distance = self.SmokeOffsetDistance or math.random(10,20)
return distance
end
--- Set the offset distance when using `COORDINATE:Smoke()`.
-- @param #COORDINATE self
-- @param #number Distance (Optional) This is the distance of the offset in meters. Default random 10-20.
-- @return #COORDINATE self
function COORDINATE:SetSmokeOffsetDistance(Distance)
if self then
self.SmokeOffsetDistance = Distance or math.random(10,20)
return self
else
COORDINATE.SmokeOffsetDistance = Distance or math.random(10,20)
end
end
--- Set the offset on when using `COORDINATE:Smoke()`.
-- @param #COORDINATE self
-- @return #COORDINATE self
function COORDINATE:SwitchSmokeOffsetOn()
if self then
self.SmokeOffset = true
return self
else
COORDINATE.SmokeOffset = true
end
end
--- Set the offset off when using `COORDINATE:Smoke()`.
-- @param #COORDINATE self
-- @return #COORDINATE self
function COORDINATE:SwitchSmokeOffsetOff()
if self then
self.SmokeOffset = false
return self
else
COORDINATE.SmokeOffset = false
end
end
--- Stops smoking the point in a color.
-- @param #COORDINATE self
@@ -3716,173 +3869,24 @@ do -- COORDINATE
function COORDINATE:GetRandomPointVec3InRadius( OuterRadius, InnerRadius )
return COORDINATE:NewFromVec3( self:GetRandomVec3InRadius( OuterRadius, InnerRadius ) )
end
end
do
--- The POINT_VEC3 class
-- @type POINT_VEC3
-- @field #number x The x coordinate in 3D space.
-- @field #number y The y coordinate in 3D space.
-- @field #number z The z COORDINATE in 3D space.
-- @field Utilities.Utils#SMOKECOLOR SmokeColor
-- @field Utilities.Utils#FLARECOLOR FlareColor
-- @field #POINT_VEC3.RoutePointAltType RoutePointAltType
-- @field #POINT_VEC3.RoutePointType RoutePointType
-- @field #POINT_VEC3.RoutePointAction RoutePointAction
-- @extends #COORDINATE
--- Defines a 3D point in the simulator and with its methods, you can use or manipulate the point in 3D space.
--
-- **DEPRECATED - PLEASE USE COORDINATE!**
--
-- **Important Note:** Most of the functions in this section were taken from MIST, and reworked to OO concepts.
-- In order to keep the credibility of the the author,
-- I want to emphasize that the formulas embedded in the MIST framework were created by Grimes or previous authors,
-- who you can find on the Eagle Dynamics Forums.
--
--
-- ## POINT_VEC3 constructor
--
-- A new POINT_VEC3 object can be created with:
--
-- * @{#POINT_VEC3.New}(): a 3D point.
-- * @{#POINT_VEC3.NewFromVec3}(): a 3D point created from a @{DCS#Vec3}.
--
--
-- ## Manupulate the X, Y, Z coordinates of the POINT_VEC3
--
-- A POINT_VEC3 class works in 3D space. It contains internally an X, Y, Z coordinate.
-- Methods exist to manupulate these coordinates.
--
-- The current X, Y, Z axis can be retrieved with the methods @{#POINT_VEC3.GetX}(), @{#POINT_VEC3.GetY}(), @{#POINT_VEC3.GetZ}() respectively.
-- The methods @{#POINT_VEC3.SetX}(), @{#POINT_VEC3.SetY}(), @{#POINT_VEC3.SetZ}() change the respective axis with a new value.
-- The current axis values can be changed by using the methods @{#POINT_VEC3.AddX}(), @{#POINT_VEC3.AddY}(), @{#POINT_VEC3.AddZ}()
-- to add or substract a value from the current respective axis value.
-- Note that the Set and Add methods return the current POINT_VEC3 object, so these manipulation methods can be chained... For example:
--
-- local Vec3 = PointVec3:AddX( 100 ):AddZ( 150 ):GetVec3()
--
--
-- ## 3D calculation methods
--
-- Various calculation methods exist to use or manipulate 3D space. Find below a short description of each method:
--
--
-- ## Point Randomization
--
-- Various methods exist to calculate random locations around a given 3D point.
--
-- * @{#POINT_VEC3.GetRandomPointVec3InRadius}(): Provides a random 3D point around the current 3D point, in the given inner to outer band.
--
--
-- @field #POINT_VEC3
POINT_VEC3 = {
ClassName = "POINT_VEC3",
Metric = true,
RoutePointAltType = {
BARO = "BARO",
},
RoutePointType = {
TakeOffParking = "TakeOffParking",
TurningPoint = "Turning Point",
},
RoutePointAction = {
FromParkingArea = "From Parking Area",
TurningPoint = "Turning Point",
},
}
--- RoutePoint AltTypes
-- @type POINT_VEC3.RoutePointAltType
-- @field BARO "BARO"
--- RoutePoint Types
-- @type POINT_VEC3.RoutePointType
-- @field TakeOffParking "TakeOffParking"
-- @field TurningPoint "Turning Point"
--- RoutePoint Actions
-- @type POINT_VEC3.RoutePointAction
-- @field FromParkingArea "From Parking Area"
-- @field TurningPoint "Turning Point"
-- Constructor.
--- Create a new POINT_VEC3 object.
-- @param #POINT_VEC3 self
-- @param DCS#Distance x The x coordinate of the Vec3 point, pointing to the North.
-- @param DCS#Distance y The y coordinate of the Vec3 point, pointing Upwards.
-- @param DCS#Distance z The z coordinate of the Vec3 point, pointing to the Right.
-- @return Core.Point#POINT_VEC3
function POINT_VEC3:New( x, y, z )
local self = BASE:Inherit( self, COORDINATE:New( x, y, z ) ) -- Core.Point#POINT_VEC3
self:F2( self )
return self
end
end
do
--- @type POINT_VEC2
-- @field DCS#Distance x The x coordinate in meters.
-- @field DCS#Distance y the y coordinate in meters.
-- @extends Core.Point#COORDINATE
--- Defines a 2D point in the simulator. The height coordinate (if needed) will be the land height + an optional added height specified.
--
-- **DEPRECATED - PLEASE USE COORDINATE!**
--
-- ## POINT_VEC2 constructor
--
-- A new POINT_VEC2 instance can be created with:
--
-- * @{Core.Point#POINT_VEC2.New}(): a 2D point, taking an additional height parameter.
-- * @{Core.Point#POINT_VEC2.NewFromVec2}(): a 2D point created from a @{DCS#Vec2}.
--
-- ## Manupulate the X, Altitude, Y coordinates of the 2D point
--
-- A POINT_VEC2 class works in 2D space, with an altitude setting. It contains internally an X, Altitude, Y coordinate.
-- Methods exist to manupulate these coordinates.
--
-- The current X, Altitude, Y axis can be retrieved with the methods @{#POINT_VEC2.GetX}(), @{#POINT_VEC2.GetAlt}(), @{#POINT_VEC2.GetY}() respectively.
-- The methods @{#POINT_VEC2.SetX}(), @{#POINT_VEC2.SetAlt}(), @{#POINT_VEC2.SetY}() change the respective axis with a new value.
-- The current Lat(itude), Alt(itude), Lon(gitude) values can also be retrieved with the methods @{#POINT_VEC2.GetLat}(), @{#POINT_VEC2.GetAlt}(), @{#POINT_VEC2.GetLon}() respectively.
-- The current axis values can be changed by using the methods @{#POINT_VEC2.AddX}(), @{#POINT_VEC2.AddAlt}(), @{#POINT_VEC2.AddY}()
-- to add or substract a value from the current respective axis value.
-- Note that the Set and Add methods return the current POINT_VEC2 object, so these manipulation methods can be chained... For example:
--
-- local Vec2 = PointVec2:AddX( 100 ):AddY( 2000 ):GetVec2()
--
-- @field #POINT_VEC2
POINT_VEC2 = {
ClassName = "POINT_VEC2",
}
--- POINT_VEC2 constructor.
-- @param #POINT_VEC2 self
-- @param DCS#Distance x The x coordinate of the Vec3 point, pointing to the North.
-- @param DCS#Distance y The y coordinate of the Vec3 point, pointing to the Right.
-- @param DCS#Distance LandHeightAdd (optional) The default height if required to be evaluated will be the land height of the x, y coordinate. You can specify an extra height to be added to the land height.
-- @return Core.Point#POINT_VEC2
function POINT_VEC2:New( x, y, LandHeightAdd )
local LandHeight = land.getHeight( { ["x"] = x, ["y"] = y } )
LandHeightAdd = LandHeightAdd or 0
LandHeight = LandHeight + LandHeightAdd
local self = BASE:Inherit( self, COORDINATE:New( x, LandHeight, y ) ) -- Core.Point#POINT_VEC2
self:F2( self )
return self
--- Search for clear zones in a given area. A powerful and efficient function using Disposition to find clear areas for spawning ground units avoiding trees, water and map scenery.
-- @param #number SearchRadius Radius of the search area.
-- @param #number PosRadius Required clear radius around each position.
-- @param #number NumPositions Number of positions to find.
-- @return #table A table of Core.Point#COORDINATE that are clear of map objects within the given PosRadius. nil if no positions are found.
function COORDINATE:GetSimpleZones(SearchRadius, PosRadius, NumPositions)
local clearPositions = UTILS.GetSimpleZones(self:GetVec3(), SearchRadius, PosRadius, NumPositions)
if clearPositions and #clearPositions > 0 then
local coords = {}
for _, pos in pairs(clearPositions) do
local coord = COORDINATE:NewFromVec2(pos)
table.insert(coords, coord)
end
return coords
end
return nil
end
end
@@ -175,7 +175,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
local Name = Info.name or "?"
local ErrorHandler = function( errmsg )
env.info( "Error in timer function: " .. errmsg )
env.info( "Error in timer function: " .. errmsg or "" )
if BASE.Debug ~= nil then
env.info( BASE.Debug.traceback() )
end
@@ -326,7 +326,7 @@ function SCHEDULEDISPATCHER:Stop( Scheduler, CallID )
local Schedule = self.Schedule[Scheduler][CallID] -- #SCHEDULEDISPATCHER.ScheduleData
-- Only stop when there is a ScheduleID defined for the CallID. So, when the scheduler was stopped before, do nothing.
if Schedule.ScheduleID then
if Schedule and Schedule.ScheduleID then
self:T( string.format( "SCHEDULEDISPATCHER stopping scheduler CallID=%s, ScheduleID=%s", tostring( CallID ), tostring( Schedule.ScheduleID ) ) )
File diff suppressed because it is too large Load Diff
+24 -1
View File
@@ -1049,6 +1049,23 @@ function SPAWN:InitSetUnitAbsolutePositions(Positions)
return self
end
--- Uses Disposition and other fallback logic to find better ground positions for ground units.
--- NOTE: This is not a spawn randomizer.
--- It will try to find clear ground locations avoiding trees, water, roads, runways, map scenery, statics and other units in the area.
--- Maintains the original layout and unit positions as close as possible by searching for the next closest valid position to each unit.
-- @param #SPAWN self
-- @param #boolean OnOff Enable/disable the feature.
-- @param #number MaxRadius (Optional) Max radius to search for valid ground locations in meters. Default is double the max radius of the units.
-- @param #number Spacing (Optional) Minimum spacing between units in meters. Default is 5% of the search radius or 5 meters, whichever is larger.
-- @return #SPAWN
function SPAWN:InitValidateAndRepositionGroundUnits(OnOff, MaxRadius, Spacing)
self.SpawnValidateAndRepositionGroundUnits = OnOff
self.SpawnValidateAndRepositionGroundUnitsRadius = MaxRadius
self.SpawnValidateAndRepositionGroundUnitsSpacing = Spacing
return self
end
--- This method is rather complicated to understand. But I'll try to explain.
-- This method becomes useful when you need to spawn groups with random templates of groups defined within the mission editor,
-- but they will all follow the same Template route and have the same prefix name.
@@ -1829,7 +1846,13 @@ function SPAWN:SpawnWithIndex( SpawnIndex, NoBirth )
if self.SpawnHiddenOnMap then
SpawnTemplate.hidden=self.SpawnHiddenOnMap
end
if self.SpawnValidateAndRepositionGroundUnits then
local units = SpawnTemplate.units
local gPos = { x = SpawnTemplate.x, y = SpawnTemplate.y }
UTILS.ValidateAndRepositionGroundUnits(units, gPos, self.SpawnValidateAndRepositionGroundUnitsRadius, self.SpawnValidateAndRepositionGroundUnitsSpacing)
end
-- Set country, coalition and category.
SpawnTemplate.CategoryID = self.SpawnInitCategory or SpawnTemplate.CategoryID
SpawnTemplate.CountryID = self.SpawnInitCountry or SpawnTemplate.CountryID
+52 -4
View File
@@ -149,6 +149,7 @@ function SPAWNSTATIC:NewFromStatic(SpawnTemplateName, SpawnCountryID)
self.CategoryID = CategoryID
self.CoalitionID = CoalitionID
self.SpawnIndex = 0
self.StaticCopyFrom = SpawnTemplateName
else
error( "SPAWNSTATIC:New: There is no static declared in the mission editor with SpawnTemplatePrefix = '" .. tostring(SpawnTemplateName) .. "'" )
end
@@ -302,12 +303,16 @@ end
-- @param #number CallsignID Callsign ID. Default 1 (="London").
-- @param #number Frequency Frequency in MHz. Default 127.5 MHz.
-- @param #number Modulation Modulation 0=AM, 1=FM.
-- @param #boolean DynamicSpawns If true, allow Dynamic Spawns
-- @param #boolean DynamicHotStarts If true, and DynamicSpawns is true, then allow Dynamic Spawns with hot starts.
-- @return #SPAWNSTATIC self
function SPAWNSTATIC:InitFARP(CallsignID, Frequency, Modulation)
function SPAWNSTATIC:InitFARP(CallsignID, Frequency, Modulation, DynamicSpawns,DynamicHotStarts)
self.InitFarp=true
self.InitFarpCallsignID=CallsignID or 1
self.InitFarpFreq=Frequency or 127.5
self.InitFarpModu=Modulation or 0
self.InitFarpDynamicSpawns = DynamicSpawns
self.InitFarpDynamicHotStarts = (DynamicSpawns == true and DynamicHotStarts == true) and true or nil
return self
end
@@ -373,6 +378,20 @@ function SPAWNSTATIC:InitLinkToUnit(Unit, OffsetX, OffsetY, OffsetAngle)
return self
end
--- Uses Disposition and other fallback logic to find a better and valid ground spawn position.
--- NOTE: This is not a spawn randomizer.
--- It will try to a find clear ground location avoiding trees, water, roads, runways, map scenery, other statics and other units in the area.
--- Uses the initial position if it's a valid location.
-- @param #SPAWNSTATIC self
-- @param #boolean OnOff Enable/disable the feature.
-- @param #number MaxRadius (Optional) Max radius to search for a valid ground location in meters. Default is 10 times the max radius of the static.
-- @return #SPAWNSTATIC self
function SPAWNSTATIC:InitValidateAndRepositionStatic(OnOff, MaxRadius)
self.ValidateAndRepositionStatic = OnOff
self.ValidateAndRepositionStaticMaxRadius = MaxRadius
return self
end
--- Allows to place a CallFunction hook when a new static spawns.
-- The provided method will be called when a new group is spawned, including its given parameters.
-- The first parameter of the SpawnFunction is the @{Wrapper.Static#STATIC} that was spawned.
@@ -459,8 +478,9 @@ end
function SPAWNSTATIC:SpawnFromZone(Zone, Heading, NewName)
-- Spawn the new static at the center of the zone.
local Static = self:SpawnFromPointVec2( Zone:GetPointVec2(), Heading, NewName )
--local Static = self:SpawnFromPointVec2( Zone:GetPointVec2(), Heading, NewName )
local Static = self:SpawnFromCoordinate(Zone:GetCoordinate(), Heading, NewName)
return Static
end
@@ -538,6 +558,14 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
-- Add static to the game.
local Static=nil --DCS#StaticObject
if self.ValidateAndRepositionStatic then
local validPos = UTILS.ValidateAndRepositionStatic(CountryID, Template.category, Template.type, Template, Template.shape_name, self.ValidateAndRepositionStaticMaxRadius)
if validPos then
Template.x = validPos.x
Template.y = validPos.y
end
end
if self.InitFarp then
local TemplateGroup={}
@@ -549,6 +577,13 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
TemplateGroup.x=Template.x
TemplateGroup.y=Template.y
TemplateGroup.name=Template.name
if self.InitFarpDynamicSpawns == true then
TemplateGroup.units[1].dynamicSpawn = true
if self.InitFarpDynamicHotStarts == true then
TemplateGroup.units[1].allowHotStart = true
end
end
self:T("Spawning FARP")
self:T({Template=Template})
@@ -556,7 +591,8 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
-- ED's dirty way to spawn FARPS.
Static=coalition.addGroup(CountryID, -1, TemplateGroup)
--Static=coalition.addStaticObject(CountryID, Template)
-- Currently DCS 2.8 does not trigger birth events if FARPS are spawned!
-- We create such an event. The airbase is registered in Core.Event
local Event = {
@@ -594,6 +630,18 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
-- delay calling this for .3 seconds so that it hopefully comes after the BIRTH event of the group.
self:ScheduleOnce(0.3, self.SpawnFunctionHook, mystatic, unpack(self.SpawnFunctionArguments))
end
if self.StaticCopyFrom ~= nil then
mystatic.StaticCopyFrom = self.StaticCopyFrom
end
local TemplateGroup={}
TemplateGroup.units={}
TemplateGroup.units[1]=Template
TemplateGroup.x=Template.x
TemplateGroup.y=Template.y
TemplateGroup.name=Template.name
_DATABASE:_RegisterStaticTemplate( TemplateGroup, self.CoalitionID, self.CategoryID, CountryID )
return mystatic
end
File diff suppressed because it is too large Load Diff
+131 -15
View File
@@ -70,6 +70,7 @@
-- @field #table Table of any trigger zone properties from the ME. The key is the Name of the property, and the value is the property's Value.
-- @field #number Surface Type of surface. Only determined at the center of the zone!
-- @field #number Checktime Check every Checktime seconds, used for ZONE:Trigger()
-- @field #boolean PartlyInside When called, a GROUP is considered inside as soon as any of its units enters the zone even if they are far apart.
-- @extends Core.Fsm#FSM
@@ -305,6 +306,20 @@ function ZONE_BASE:GetCoordinate( Height ) --R2.1
return self.Coordinate
end
--- Returns the @{Core.Vector#VECTOR} of the zone.
-- @param #ZONE_BASE self
-- @param DCS#Distance Height The height in meters to add to the land height where the center of the zone is located.
-- @return Core.Vector#VECTOR The vector of the zone.
function ZONE_BASE:GetVector( Height )
self:F2(self.ZoneName)
local Vec3 = self:GetVec3( Height )
local vector=VECTOR:NewFromVec(Vec3)
return vector
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.
@@ -534,6 +549,19 @@ function ZONE_BASE:GetZoneProbability()
return self.ZoneProbability
end
--- Get the coordinate on the radius of the zone nearest to Outsidecoordinate. Useto e.g. find an ingress point.
-- @param #ZONE_BASE self
-- @param Core.Point#COORDINATE Outsidecoordinate The coordinate outside of the zone from where to look.
-- @return Core.Point#COORDINATE CoordinateOnRadius
function ZONE_BASE:FindNearestCoordinateOnRadius(Outsidecoordinate)
local Vec1 = self:GetVec2()
local Radius = self:GetRadius()
local Vec2 = Outsidecoordinate:GetVec2()
local Point = UTILS.FindNearestPointOnCircle(Vec1,Radius,Vec2)
local rc = COORDINATE:NewFromVec2(Point)
return rc
end
--- Get the zone taking into account the randomization probability of a zone to be selected.
-- @param #ZONE_BASE self
-- @return #ZONE_BASE The zone is selected taking into account the randomization probability factor.
@@ -599,6 +627,8 @@ end
--
-- -- Stop watching the zone after 1 hour
-- triggerzone:__TriggerStop(3600)
-- -- Call :SetPartlyInside() if you use SET_GROUP to count as inside when any of their units enters even when they are far apart.
-- -- Make sure to call :SetPartlyInside() before :Trigger()!
function ZONE_BASE:Trigger(Objects)
--self:I("Added Zone Trigger")
self:SetStartState("TriggerStopped")
@@ -667,6 +697,16 @@ function ZONE_BASE:Trigger(Objects)
end
--- Toggle “partly-inside” handling for this zone. To be used before :Trigger().
-- * Default:* flag is **false** until you call the method.
-- * Call with no argument or with **true** → enable.
-- * Call with **false** → disable again (handy if it was enabled before).
-- @param #ZONE_BASE self
-- @return #ZONE_BASE self
function ZONE_BASE:SetPartlyInside(state)
self.PartlyInside = state or not ( state == false )
return self
end
--- (Internal) Check the assigned objects for being in/out of the zone
-- @param #ZONE_BASE self
-- @param #boolean fromstart If true, do the init of the objects
@@ -705,7 +745,12 @@ function ZONE_BASE:_TriggerCheck(fromstart)
obj.TriggerInZone[self.ZoneName] = false
end
-- is obj in zone?
local inzone = self:IsCoordinateInZone(obj:GetCoordinate())
local inzone
if self.PartlyInside and obj.ClassName == "GROUP" then
inzone = obj:IsAnyInZone(self) -- TRUE if any unit is inside
else
inzone = self:IsCoordinateInZone(obj:GetCoordinate()) -- original barycentre test
end
--self:I("Object "..obj:GetName().." is in zone: "..tostring(inzone))
if inzone and obj.TriggerInZone[self.ZoneName] then
-- just count
@@ -1134,7 +1179,7 @@ function ZONE_RADIUS:Scan( ObjectCategories, UnitCategories )
self.ScanData.SceneryTable = {}
self.ScanData.Units = {}
local ZoneCoord = self:GetCoordinate()
local ZoneCoord = self:GetCoordinate():SetAlt()
local ZoneRadius = self:GetRadius()
--self:F({ZoneCoord = ZoneCoord, ZoneRadius = ZoneRadius, ZoneCoordLL = ZoneCoord:ToStringLLDMS()})
@@ -1149,15 +1194,13 @@ function ZONE_RADIUS:Scan( ObjectCategories, UnitCategories )
local function EvaluateZone( ZoneObject )
--if ZoneObject:isExist() then --FF: isExist always returns false for SCENERY objects since DCS 2.2 and still in DCS 2.5
if ZoneObject then
if ZoneObject and self:IsVec3InZone(ZoneObject:getPoint()) then
-- Get object category.
local ObjectCategory = Object.getCategory(ZoneObject)
if ( ObjectCategory == Object.Category.UNIT and ZoneObject:isExist() and ZoneObject:isActive() ) or (ObjectCategory == Object.Category.STATIC and ZoneObject:isExist()) then
local CoalitionDCSUnit = ZoneObject:getCoalition()
local Include = false
if not UnitCategories then
-- Anything found is included.
@@ -1509,6 +1552,26 @@ function ZONE_RADIUS:IsVec3InZone( Vec3 )
return InZone
end
--- Search for clear ground spawn zones within this zone. A powerful and efficient function using Disposition to find clear areas for spawning ground units avoiding trees, water and map scenery.
-- @param #ZONE_RADIUS self
-- @param #number PosRadius Required clear radius around each position.
-- @param #number NumPositions Number of positions to find.
-- @return #table A table of DCS#Vec2 positions that are clear of map objects within the given PosRadius. nil if no clear positions are found.
function ZONE_RADIUS:GetClearZonePositions(PosRadius, NumPositions)
return UTILS.GetClearZonePositions(self, PosRadius, NumPositions)
end
--- Search for a random clear ground spawn coordinate within this zone. A powerful and efficient function using Disposition to find clear areas for spawning ground units avoiding trees, water and map scenery.
-- @param #ZONE_RADIUS self
-- @param #number PosRadius (Optional) Required clear radius around each position. (Default is math.min(Radius/10, 200))
-- @param #number NumPositions (Optional) Number of positions to find. (Default 50)
-- @return Core.Point#COORDINATE A random coordinate for a clear zone. nil if no clear positions are found.
-- @return #number Assigned radius for the found zones. nil if no clear positions are found.
function ZONE_RADIUS:GetRandomClearZoneCoordinate(PosRadius, NumPositions)
return UTILS.GetRandomClearZoneCoordinate(self, PosRadius, NumPositions)
end
--- Returns a random Vec2 location within the zone.
-- @param #ZONE_RADIUS self
-- @param #number inner (Optional) Minimal distance from the center of the zone. Default is 0.
@@ -1520,6 +1583,10 @@ function ZONE_RADIUS:GetRandomVec2(inner, outer, surfacetypes)
local Vec2 = self:GetVec2()
local _inner = inner or 0
local _outer = outer or self:GetRadius()
math.random()
math.random()
math.random()
if surfacetypes and type(surfacetypes)~="table" then
surfacetypes={surfacetypes}
@@ -1881,6 +1948,21 @@ function ZONE_UNIT:New( ZoneName, ZoneUNIT, Radius, Offset)
return self
end
--- Updates the current location from a @{Wrapper.Group}.
-- @param #ZONE_UNIT self
-- @param Wrapper.Group#GROUP Group (optional) Update from this Unit, if nil, update from the UNIT this zone is based on.
-- @return self
function ZONE_UNIT:UpdateFromUnit(Unit)
if Unit and Unit:IsAlive() then
local vec2 = Unit:GetVec2()
self.LastVec2 = vec2
elseif self.ZoneUNIT and self.ZoneUNIT:IsAlive() then
local ZoneVec2 = self.ZoneUNIT:GetVec2()
self.LastVec2 = ZoneVec2
end
return self
end
--- Returns the current location of the @{Wrapper.Unit#UNIT}.
-- @param #ZONE_UNIT self
@@ -2018,6 +2100,22 @@ function ZONE_GROUP:GetVec2()
return ZoneVec2
end
--- Updates the current location from a @{Wrapper.Group}.
-- @param #ZONE_GROUP self
-- @param Wrapper.Group#GROUP Group (optional) Update from this Group, if nil, update from the GROUP this zone is based on.
-- @return self
function ZONE_GROUP:UpdateFromGroup(Group)
if Group and Group:IsAlive() then
local vec2 = Group:GetVec2()
self.Vec2 = vec2
elseif self._.ZoneGROUP and self._.ZoneGROUP:IsAlive() then
local ZoneVec2 = self._.ZoneGROUP:GetVec2()
self.Vec2 = ZoneVec2
self._.ZoneVec2Cache = ZoneVec2
end
return self
end
--- Returns a random location within the zone of the @{Wrapper.Group}.
-- @param #ZONE_GROUP self
-- @return DCS#Vec2 The random location of the zone based on the @{Wrapper.Group} location.
@@ -2487,6 +2585,26 @@ function ZONE_POLYGON_BASE:Flush()
return self
end
--- Search for clear ground spawn zones within this zone. A powerful and efficient function using Disposition to find clear areas for spawning ground units avoiding trees, water and map scenery.
-- @param #ZONE_POLYGON_BASE self
-- @param #number PosRadius Required clear radius around each position.
-- @param #number NumPositions Number of positions to find.
-- @return #table A table of DCS#Vec2 positions that are clear of map objects within the given PosRadius. nil if no clear positions are found.
function ZONE_POLYGON_BASE:GetClearZonePositions(PosRadius, NumPositions)
return UTILS.GetClearZonePositions(self, PosRadius, NumPositions)
end
--- Search for a random clear ground spawn coordinate within this zone. A powerful and efficient function using Disposition to find clear areas for spawning ground units avoiding trees, water and map scenery.
-- @param #ZONE_POLYGON_BASE self
-- @param #number PosRadius (Optional) Required clear radius around each position. (Default is math.min(Radius/10, 200))
-- @param #number NumPositions (Optional) Number of positions to find. (Default 50)
-- @return Core.Point#COORDINATE A random coordinate for a clear zone. nil if no clear positions are found.
-- @return #number Assigned radius for the found zones. nil if no clear positions are found.
function ZONE_POLYGON_BASE:GetRandomClearZoneCoordinate(PosRadius, NumPositions)
return UTILS.GetRandomClearZoneCoordinate(self, PosRadius, NumPositions)
end
--- Smokes the zone boundaries in a color.
-- @param #ZONE_POLYGON_BASE self
-- @param #boolean UnBound If true, the tyres will be destroyed.
@@ -2865,6 +2983,11 @@ end
function ZONE_POLYGON_BASE:GetRandomVec2()
-- make sure we assign weights to the triangles based on their surface area, otherwise
-- we'll be more likely to generate random points in smaller triangles
math.random()
math.random()
math.random()
local weights = {}
for _, triangle in pairs(self._Triangles) do
weights[triangle] = triangle.SurfaceArea / self.SurfaceArea
@@ -3204,12 +3327,7 @@ function ZONE_POLYGON:Scan( ObjectCategories, UnitCategories )
local vectors = self:GetBoundingSquare()
local minVec3 = {x=vectors.x1, y=0, z=vectors.y1}
local maxVec3 = {x=vectors.x2, y=0, z=vectors.y2}
local minmarkcoord = COORDINATE:NewFromVec3(minVec3)
local maxmarkcoord = COORDINATE:NewFromVec3(maxVec3)
local ZoneRadius = minmarkcoord:Get2DDistance(maxmarkcoord)/2
local ZoneRadius = UTILS.VecDist2D({x=vectors.x1, y=vectors.y1}, {x=vectors.x2, y=vectors.y2})/2
-- self:I("Scan Radius:" ..ZoneRadius)
local CenterVec3 = self:GetCoordinate():GetVec3()
@@ -3233,14 +3351,12 @@ function ZONE_POLYGON:Scan( ObjectCategories, UnitCategories )
local function EvaluateZone( ZoneObject )
if ZoneObject then
if ZoneObject and self:IsVec3InZone(ZoneObject:getPoint()) then
local ObjectCategory = Object.getCategory(ZoneObject)
if ( ObjectCategory == Object.Category.UNIT and ZoneObject:isExist() and ZoneObject:isActive() ) or (ObjectCategory == Object.Category.STATIC and ZoneObject:isExist()) then
local CoalitionDCSUnit = ZoneObject:getCoalition()
local Include = false
if not UnitCategories then
-- Anything found is included.
@@ -3272,7 +3388,7 @@ function ZONE_POLYGON:Scan( ObjectCategories, UnitCategories )
end
-- trying with box search
if ObjectCategory == Object.Category.SCENERY and self:IsVec3InZone(ZoneObject:getPoint()) then
if ObjectCategory == Object.Category.SCENERY then
local SceneryType = ZoneObject:getTypeName()
local SceneryName = ZoneObject:getName()
self.ScanData.Scenery[SceneryType] = self.ScanData.Scenery[SceneryType] or {}
+52 -18
View File
@@ -22,7 +22,7 @@
-- ===
--
-- ### Author: **Applevangelist**
-- Last Update Sept 2023
-- Last Update July 2025
--
-- ===
-- @module Functional.AICSAR
@@ -57,6 +57,8 @@
-- @field #number Speed Default speed setting for the helicopter FLIGHTGROUP is 100kn.
-- @field #boolean UseEventEject In case Event LandingAfterEjection isn't working, use set this to true.
-- @field #number Delay In case of UseEventEject wait this long until we spawn a landed pilot.
-- @field #boolean UseRescueZone If true, use a rescue zone and not the max distance to FARP/MASH
-- @field Core.Zone#ZONE_RADIUS RescueZone Use this zone as operational area for the AICSAR instance.
-- @extends Core.Fsm#FSM
@@ -153,10 +155,10 @@
-- To set up AICSAR for SRS TTS output, add e.g. the following to your script:
--
-- -- setup for google TTS, radio 243 AM, SRS server port 5002 with a google standard-quality voice (google cloud account required)
-- my_aicsar:SetSRSTTSRadio(true,"C:\\Program Files\\DCS-SimpleRadio-Standalone",243,radio.modulation.AM,5002,MSRS.Voices.Google.Standard.en_US_Standard_D,"en-US","female","C:\\Program Files\\DCS-SimpleRadio-Standalone\\google.json")
-- my_aicsar:SetSRSTTSRadio(true,"C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio",243,radio.modulation.AM,5002,MSRS.Voices.Google.Standard.en_US_Standard_D,"en-US","female","C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio\\google.json")
--
-- -- alternatively for MS Desktop TTS (voices need to be installed locally first!)
-- my_aicsar:SetSRSTTSRadio(true,"C:\\Program Files\\DCS-SimpleRadio-Standalone",243,radio.modulation.AM,5002,MSRS.Voices.Microsoft.Hazel,"en-GB","female")
-- my_aicsar:SetSRSTTSRadio(true,"C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio",243,radio.modulation.AM,5002,MSRS.Voices.Microsoft.Hazel,"en-GB","female")
--
-- -- define a different voice for the downed pilot(s)
-- my_aicsar:SetPilotTTSVoice(MSRS.Voices.Google.Standard.en_AU_Standard_D,"en-AU","male")
@@ -177,7 +179,7 @@
--
-- Switch on radio transmissions via **either** SRS **or** "normal" DCS radio e.g. like so:
--
-- my_aicsar:SetSRSRadio(true,"C:\\Program Files\\DCS-SimpleRadio-Standalone",270,radio.modulation.AM,nil,5002)
-- my_aicsar:SetSRSRadio(true,"C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio",270,radio.modulation.AM,nil,5002)
--
-- or
--
@@ -191,7 +193,7 @@
-- @field #AICSAR
AICSAR = {
ClassName = "AICSAR",
version = "0.1.16",
version = "0.1.18",
lid = "",
coalition = coalition.side.BLUE,
template = "",
@@ -236,6 +238,8 @@ AICSAR = {
Altitude = 1500,
UseEventEject = false,
Delay = 100,
UseRescueZone = false,
RescueZone = nil,
}
-- TODO Messages
@@ -304,8 +308,9 @@ AICSAR.RadioLength = {
-- @param #string Helotemplate Helicopter template name.
-- @param Wrapper.Airbase#AIRBASE FARP FARP object or Airbase from where to start.
-- @param Core.Zone#ZONE MASHZone Zone where to drop pilots after rescue.
-- @param #number Helonumber Max number of alive Ai Helos at the same time. Defaults to three.
-- @return #AICSAR self
function AICSAR:New(Alias,Coalition,Pilottemplate,Helotemplate,FARP,MASHZone)
function AICSAR:New(Alias,Coalition,Pilottemplate,Helotemplate,FARP,MASHZone,Helonumber)
-- Inherit everything from FSM class.
local self=BASE:Inherit(self, FSM:New())
@@ -373,7 +378,7 @@ function AICSAR:New(Alias,Coalition,Pilottemplate,Helotemplate,FARP,MASHZone)
-- limit number of available helos at the same time
self.limithelos = true
self.helonumber = 3
self.helonumber = Helonumber or 3
-- localization
self:InitLocalization()
@@ -524,10 +529,20 @@ function AICSAR:InitLocalization()
return self
end
--- [User] Use a defined zone as area of operation and not the distance to FARP.
-- @param #AICSAR self
-- @param Core.Zone#ZONE Zone The operational zone to use. Downed pilots in this area will be rescued. Can be any known #ZONE type.
-- @return #AICSAR self
function AICSAR:SetUsingRescueZone(Zone)
self.UseRescueZone = true
self.RescueZone = Zone
return self
end
--- [User] Switch sound output on and use SRS output for sound files.
-- @param #AICSAR self
-- @param #boolean OnOff Switch on (true) or off (false).
-- @param #string Path Path to your SRS Server Component, e.g. "C:\\\\Program Files\\\\DCS-SimpleRadio-Standalone"
-- @param #string Path Path to your SRS Server External Audio Component, e.g. "C:\\\\Program Files\\\\DCS-SimpleRadio-Standalone\\\\ExternalAudio"
-- @param #number Frequency Defaults to 243 (guard)
-- @param #number Modulation Radio modulation. Defaults to radio.modulation.AM
-- @param #string SoundPath Where to find the audio files. Defaults to nil, i.e. add messages via "Sound to..." in the Mission Editor.
@@ -538,7 +553,7 @@ function AICSAR:SetSRSRadio(OnOff,Path,Frequency,Modulation,SoundPath,Port)
self.SRSRadio = OnOff and true
self.SRSTTSRadio = false
self.SRSFrequency = Frequency or 243
self.SRSPath = Path or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone"
self.SRSPath = Path or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio"
self.SRS:SetLabel("ACSR")
self.SRS:SetCoalition(self.coalition)
self.SRSModulation = Modulation or radio.modulation.AM
@@ -556,7 +571,7 @@ end
-- See `AICSAR:SetPilotTTSVoice()` and `AICSAR:SetOperatorTTSVoice()`
-- @param #AICSAR self
-- @param #boolean OnOff Switch on (true) or off (false).
-- @param #string Path Path to your SRS Server Component, e.g. "E:\\\\Program Files\\\\DCS-SimpleRadio-Standalone"
-- @param #string Path Path to your SRS Server Component, e.g. "E:\\\\Program Files\\\\DCS-SimpleRadio-Standalone\\ExternalAudio"
-- @param #number Frequency (Optional) Defaults to 243 (guard)
-- @param #number Modulation (Optional) Radio modulation. Defaults to radio.modulation.AM
-- @param #number Port (Optional) Port of the SRS, defaults to 5002.
@@ -570,7 +585,7 @@ function AICSAR:SetSRSTTSRadio(OnOff,Path,Frequency,Modulation,Port,Voice,Cultur
self.SRSTTSRadio = OnOff and true
self.SRSRadio = false
self.SRSFrequency = Frequency or 243
self.SRSPath = Path or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone"
self.SRSPath = Path or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio"
self.SRSModulation = Modulation or radio.modulation.AM
self.SRSPort = Port or MSRS.port or 5002
if OnOff then
@@ -693,7 +708,7 @@ function AICSAR:_EjectEventHandler(EventData)
local _LandingPos = COORDINATE:NewFromVec3(_event.initiator:getPosition().p)
local _country = _event.initiator:getCountry()
local _coalition = coalition.getCountryCoalition( _country )
local data = UTILS.DeepCopy(EventData)
--local data = UTILS.DeepCopy(EventData)
Unit.destroy(_event.initiator) -- shagrat remove static Pilot model
self:ScheduleOnce(self.Delay,self._DelayedSpawnPilot,self,_LandingPos,_coalition)
end
@@ -708,7 +723,14 @@ end
-- @return #AICSAR self
function AICSAR:_DelayedSpawnPilot(_LandingPos,_coalition)
local distancetofarp = _LandingPos:Get2DDistance(self.farp:GetCoordinate())
local distancetofarp = _LandingPos:Get2DDistance(self.farp:GetCoordinate())
if self.UseRescueZone == true and self.RescueZone ~= nil then
if self.RescueZone:IsCoordinateInZone(_LandingPos) then
distancetofarp = self.maxdistance - 10
else
distancetofarp = self.maxdistance + 10
end
end
-- Mayday Message
local Text,Soundfile,Soundlength,Subtitle = self.gettext:GetEntry("PILOTDOWN",self.locale)
local text = ""
@@ -795,7 +817,13 @@ function AICSAR:_EventHandler(EventData, FromEject)
-- DONE: add distance check
local distancetofarp = _LandingPos:Get2DDistance(self.farp:GetCoordinate())
if self.UseRescueZone == true and self.RescueZone ~= nil then
if self.RescueZone:IsCoordinateInZone(_LandingPos) then
distancetofarp = self.maxdistance - 10
else
distancetofarp = self.maxdistance + 10
end
end
-- Mayday Message
local Text,Soundfile,Soundlength,Subtitle = self.gettext:GetEntry("PILOTDOWN",self.locale)
local text = ""
@@ -817,7 +845,6 @@ function AICSAR:_EventHandler(EventData, FromEject)
if _coalition == self.coalition then
if self.verbose then
MESSAGE:New(msgtxt,15,"AICSAR"):ToCoalition(self.coalition)
-- MESSAGE:New(msgtxt,15,"AICSAR"):ToLog()
end
if self.SRSRadio then
local sound = SOUNDFILE:New(Soundfile,self.SRSSoundPath,Soundlength)
@@ -869,6 +896,7 @@ function AICSAR:_GetFlight()
:InitUnControlled(true)
:OnSpawnGroup(
function(Group)
Group:OptionPreferVerticalLanding()
self:__HeloOnDuty(1,Group)
end
)
@@ -892,7 +920,7 @@ function AICSAR:_InitMission(Pilot,Index)
--local pilotset = SET_GROUP:New()
--pilotset:AddGroup(Pilot)
-- Cargo transport assignment.
-- Cargo transport assignment.
local opstransport=OPSTRANSPORT:New(Pilot, pickupzone, self.farpzone)
--opstransport:SetVerbosity(3)
@@ -934,6 +962,10 @@ function AICSAR:_InitMission(Pilot,Index)
helo:__UnloadingDone(5)
end
function helo:OnAfterLandAtAirbase(From,Event,To,airbase)
helo:Despawn(2)
end
self.helos[Index] = helo
return self
@@ -984,7 +1016,9 @@ function AICSAR:_CheckHelos()
local name = helo:GetName()
self:T("Helo group "..name.." in state "..state)
if state == "Arrived" then
helo:__Stop(5)
--helo:__Stop(5)
helo.OnAfterDead = nil
helo:Despawn(35)
self.helos[_index] = nil
end
else
@@ -1025,7 +1059,7 @@ function AICSAR:_CheckQueue(OpsGroup)
if self:_CheckInMashZone(_pilot) then
self:T("Pilot" .. _pilot.GroupName .. " rescued!")
if OpsGroup then
OpsGroup:Despawn(10)
--OpsGroup:Despawn(10)
else
_pilot:Destroy(true,10)
end
@@ -105,7 +105,7 @@ AUTOLASE = {
debug = false,
smokemenu = true,
RoundingPrecision = 0,
increasegroundawareness = true,
increasegroundawareness = false,
MonitorFrequency = 30,
}
@@ -216,7 +216,7 @@ function AUTOLASE:New(RecceSet, Coalition, Alias, PilotSet)
self.smokemenu = true
self.threatmenu = true
self.RoundingPrecision = 0
self.increasegroundawareness = true
self.increasegroundawareness = false
self.MonitorFrequency = 30
self:EnableSmokeMenu({Angle=math.random(0,359),Distance=math.random(10,20)})
@@ -493,7 +493,7 @@ end
--- (User) Function enable sending messages via SRS.
-- @param #AUTOLASE self
-- @param #boolean OnOff Switch usage on and off
-- @param #string Path Path to SRS directory, e.g. C:\\Program Files\\DCS-SimpleRadio-Standalone
-- @param #string Path Path to SRS TTS directory, e.g. C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio
-- @param #number Frequency Frequency to send, e.g. 243
-- @param #number Modulation Modulation i.e. radio.modulation.AM or radio.modulation.FM
-- @param #string Label (Optional) Short label to be used on the SRS Client Overlay
@@ -508,7 +508,7 @@ end
function AUTOLASE:SetUsingSRS(OnOff,Path,Frequency,Modulation,Label,Gender,Culture,Port,Voice,Volume,PathToGoogleKey)
if OnOff then
self.useSRS = true
self.SRSPath = Path or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone"
self.SRSPath = Path or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio"
self.SRSFreq = Frequency or 271
self.SRSMod = Modulation or radio.modulation.AM
self.Gender = Gender or MSRS.gender or "male"
@@ -1020,7 +1020,7 @@ function AUTOLASE:_Prescient()
self:T(self.lid.."Checking possibly visible STATICs for Recce "..unit:GetName())
for _,_static in pairs(Statics) do -- DCS static object here
local static = STATIC:Find(_static)
if static and static:GetCoalition() ~= self.coalition then
if static and static:GetCoalition() ~= self.coalition and static:GetCoordinate() then
local IsLOS = position:IsLOS(static:GetCoordinate())
if IsLOS then
unit:KnowUnit(static,true,true)
@@ -0,0 +1,999 @@
--- **Functional** - Build large airborne formations of aircraft.
--
-- **Features:**
--
-- * Build in-air formations consisting of more than 40 aircraft as one group.
-- * Build different formation types.
-- * Assign a group leader that will guide the large formation path.
--
-- ===
--
-- ## Additional Material:
--
-- * **Demo Missions:** [GitHub](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/FORMATION)
-- * **Guides:** None
--
-- ===
--
-- ### Author: **FlightControl**
-- ### Contributions: **Applevangelist** (refactor)
--
-- ===
--
-- @module Functional.FORMATION
-- @image AI_Large_Formations.JPG
--- FORMATION class
-- @type FORMATION
-- @extends Core.Fsm#FSM_SET
-- @field Wrapper.Unit#UNIT FollowUnit
-- @field Core.Set#SET_GROUP FollowGroupSet
-- @field #string FollowName
-- @field Core.Scheduler#SCHEDULER FollowScheduler The instance of the SCHEDULER class.
-- @field #number FollowDistance The current follow distance.
-- @field DCS#AI.Option.Air.val.ROE OptionROE Which ROE is set to the FollowGroup.
-- @field DCS#AI.Option.Air.val.REACTION_ON_THREAT OptionReactionOnThreat Which REACTION_ON_THREAT is set to the FollowGroup.
-- @field #number dtFollow Time step between position updates.
--- Build (large) formations, make AI follow a @{Wrapper.Client#CLIENT} (player) leader or a @{Wrapper.Unit#UNIT} (AI) leader.
--
-- FORMATION makes AI @{Wrapper.Group#GROUP}s fly in formation of various compositions.
-- The FORMATION class models formations in a different manner than the internal DCS formation logic!!!
-- The purpose of the class is to:
--
-- * Make formation building a process that can be managed while in flight, rather than a task.
-- * Human players can guide formations, consisting of larget planes.
-- * Build large formations (like a large bomber field).
-- * Form formations that DCS does not support off-the-shelve.
--
-- A few remarks:
--
-- * Depending on the type of plane, the change in direction by the leader may result in the formation getting disentangled while in flight and needs to be rebuild.
-- * Formations are vulnerable to collissions, but is depending on the type of plane, the distance between the planes and the speed and angle executed by the leader.
-- * Formations may take a while to build up.
--
-- As a result, the FORMATION is not perfect, but is very useful to:
--
-- * Model large formations when flying straight line. You can build close formations when doing this.
-- * Make humans guide a large formation, when the planes are wide from each other.
--
-- ## FORMATION construction
--
-- Create a new SPAWN object with the @{#FORMATION.New} method:
--
-- * @{#FORMATION.New}(): Creates a new FORMATION object from a @{Wrapper.Group#GROUP} for a @{Wrapper.Client#CLIENT} or a @{Wrapper.Unit#UNIT}, with an optional briefing text.
--
-- ## Formation methods
--
-- The following methods can be used to set or change the formation:
--
-- * @{#FORMATION.FormationLine}(): Form a line formation (core formation function).
-- * @{#FORMATION.FormationTrail}(): Form a trail formation.
-- * @{#FORMATION.FormationLeftLine}(): Form a left line formation.
-- * @{#FORMATION.FormationRightLine}(): Form a right line formation.
-- * @{#FORMATION.FormationRightWing}(): Form a right wing formation.
-- * @{#FORMATION.FormationLeftWing}(): Form a left wing formation.
-- * @{#FORMATION.FormationCenterWing}(): Form a center wing formation.
-- * @{#FORMATION.FormationCenterVic}(): Form a Vic formation (same as CenterWing).
-- * @{#FORMATION.FormationCenterBoxed}(): Form a center boxed formation.
--
-- ## Randomization
--
-- Use the method @{FORMATION#FORMATION.SetFlightRandomization}() to simulate the formation flying errors that pilots make while in formation. Is a range set in meters.
--
-- @usage
-- local FollowGroupSet = SET_GROUP:New():FilterCategories("plane"):FilterCoalitions("blue"):FilterPrefixes("Follow"):FilterStart()
-- local LeaderUnit = UNIT:FindByName( "Leader" )
-- local LargeFormation = FORMATION:New( LeaderUnit, FollowGroupSet, "Center Wing Formation", "Briefing" )
-- LargeFormation:FormationCenterWing( 500, 50, 0, 250, 250 )
-- LargeFormation:__Start( 1 )
--
-- @field #FORMATION
FORMATION = {
ClassName = "FORMATION",
FollowName = nil, -- The Follow Name
FollowUnit = nil,
FollowGroupSet = nil,
FollowScheduler = nil,
OptionROE = AI.Option.Air.val.ROE.OPEN_FIRE,
OptionReactionOnThreat = AI.Option.Air.val.REACTION_ON_THREAT.ALLOW_ABORT_MISSION,
dtFollow = 0.5,
}
--- @type FORMATION.Formation
-- @field #number None
-- @field #number Line
-- @field #number Trail
-- @field #number Stack
-- @field #number LeftLine
-- @field #number RightLine
-- @field #number LeftWing
-- @field #number RightWing
-- @field #number Vic
-- @field #number Box
FORMATION.Formation = {
None = 1,
Line = 2,
Trail = 3,
Stack = 4,
LeftLine = 5,
RightLine = 6,
LeftWing = 7,
RightWing = 8,
Vic = 9,
Box = 10,
}
--- FORMATION class constructor for an AI group
-- @param #FORMATION self
-- @param Wrapper.Unit#UNIT FollowUnit The UNIT leading the FolllowGroupSet.
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
-- @param #string FollowName Name of the escort.
-- @return #FORMATION self
function FORMATION:New( FollowUnit, FollowGroupSet, FollowName )
local self = BASE:Inherit( self, FSM_SET:New( FollowGroupSet ) )
self:F( { FollowUnit, FollowGroupSet, FollowName } )
self.FollowUnit = FollowUnit -- Wrapper.Unit#UNIT
self.FollowGroupSet = FollowGroupSet -- Core.Set#SET_GROUP
self:SetFlightRandomization( 2 )
self:SetStartState( "None" )
self:AddTransition( "*", "Stop", "Stopped" )
self:AddTransition( {"None", "Stopped"}, "Start", "Following" )
self:AddTransition( "*", "FormationLine", "*" )
--- Start Trigger for FORMATION
-- @function [parent=#FORMATION] Start
-- @param #FORMATION self
--- Start Asynchronous Trigger for FORMATION
-- @function [parent=#FORMATION] __Start
-- @param #FORMATION self
-- @param #number Delay
--- Stop Trigger for FORMATION
-- @function [parent=#FORMATION] Stop
-- @param #FORMATION self
--- Stop Asynchronous Trigger for FORMATION
-- @function [parent=#FORMATION] __Stop
-- @param #FORMATION self
-- @param #number Delay
--- FormationLine Handler OnAfter for FORMATION
-- @function [parent=#FORMATION] OnAfterFormationLine
-- @param #FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
--- FormationLine Trigger for FORMATION
-- @function [parent=#FORMATION] FormationLine
-- @param #FORMATION self
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
--- FormationLine Asynchronous Trigger for FORMATION
-- @function [parent=#FORMATION] __FormationLine
-- @param #FORMATION self
-- @param #number Delay
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
self:AddTransition( "*", "FormationTrail", "*" )
--- FormationTrail Handler OnAfter for FORMATION
-- @function [parent=#FORMATION] OnAfterFormationTrail
-- @param #FORMATION self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
--- FormationTrail Trigger for FORMATION
-- @function [parent=#FORMATION] FormationTrail
-- @param #FORMATION self
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
--- FormationTrail Asynchronous Trigger for FORMATION
-- @function [parent=#FORMATION] __FormationTrail
-- @param #FORMATION self
-- @param #number Delay
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
self:AddTransition( "*", "FormationStack", "*" )
--- FormationStack Handler OnAfter for FORMATION
-- @function [parent=#FORMATION] OnAfterFormationStack
-- @param #FORMATION self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
--- FormationStack Trigger for FORMATION
-- @function [parent=#FORMATION] FormationStack
-- @param #FORMATION self
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
--- FormationStack Asynchronous Trigger for FORMATION
-- @function [parent=#FORMATION] __FormationStack
-- @param #FORMATION self
-- @param #number Delay
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
self:AddTransition( "*", "FormationLeftLine", "*" )
--- FormationLeftLine Handler OnAfter for FORMATION
-- @function [parent=#FORMATION] OnAfterFormationLeftLine
-- @param #FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
--- FormationLeftLine Trigger for FORMATION
-- @function [parent=#FORMATION] FormationLeftLine
-- @param #FORMATION self
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
--- FormationLeftLine Asynchronous Trigger for FORMATION
-- @function [parent=#FORMATION] __FormationLeftLine
-- @param #FORMATION self
-- @param #number Delay
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
self:AddTransition( "*", "FormationRightLine", "*" )
--- FormationRightLine Handler OnAfter for FORMATION
-- @function [parent=#FORMATION] OnAfterFormationRightLine
-- @param #FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
--- FormationRightLine Trigger for FORMATION
-- @function [parent=#FORMATION] FormationRightLine
-- @param #FORMATION self
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
--- FormationRightLine Asynchronous Trigger for FORMATION
-- @function [parent=#FORMATION] __FormationRightLine
-- @param #FORMATION self
-- @param #number Delay
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
self:AddTransition( "*", "FormationLeftWing", "*" )
--- FormationLeftWing Handler OnAfter for FORMATION
-- @function [parent=#FORMATION] OnAfterFormationLeftWing
-- @param #FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
--- FormationLeftWing Trigger for FORMATION
-- @function [parent=#FORMATION] FormationLeftWing
-- @param #FORMATION self
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
--- FormationLeftWing Asynchronous Trigger for FORMATION
-- @function [parent=#FORMATION] __FormationLeftWing
-- @param #FORMATION self
-- @param #number Delay
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
self:AddTransition( "*", "FormationRightWing", "*" )
--- FormationRightWing Handler OnAfter for FORMATION
-- @function [parent=#FORMATION] OnAfterFormationRightWing
-- @param #FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
--- FormationRightWing Trigger for FORMATION
-- @function [parent=#FORMATION] FormationRightWing
-- @param #FORMATION self
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
--- FormationRightWing Asynchronous Trigger for FORMATION
-- @function [parent=#FORMATION] __FormationRightWing
-- @param #FORMATION self
-- @param #number Delay
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
self:AddTransition( "*", "FormationCenterWing", "*" )
--- FormationCenterWing Handler OnAfter for FORMATION
-- @function [parent=#FORMATION] OnAfterFormationCenterWing
-- @param #FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
--- FormationCenterWing Trigger for FORMATION
-- @function [parent=#FORMATION] FormationCenterWing
-- @param #FORMATION self
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
--- FormationCenterWing Asynchronous Trigger for FORMATION
-- @function [parent=#FORMATION] __FormationCenterWing
-- @param #FORMATION self
-- @param #number Delay
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
self:AddTransition( "*", "FormationVic", "*" )
--- FormationVic Handler OnAfter for FORMATION
-- @function [parent=#FORMATION] OnAfterFormationVic
-- @param #FORMATION self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
--- FormationVic Trigger for FORMATION
-- @function [parent=#FORMATION] FormationVic
-- @param #FORMATION self
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
--- FormationVic Asynchronous Trigger for FORMATION
-- @function [parent=#FORMATION] __FormationVic
-- @param #FORMATION self
-- @param #number Delay
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
self:AddTransition( "*", "FormationBox", "*" )
--- FormationBox Handler OnAfter for FORMATION
-- @function [parent=#FORMATION] OnAfterFormationBox
-- @param #FORMATION self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
-- @param #number ZLevels The amount of levels on the Z-axis.
--- FormationBox Trigger for FORMATION
-- @function [parent=#FORMATION] FormationBox
-- @param #FORMATION self
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
-- @param #number ZLevels The amount of levels on the Z-axis.
--- FormationBox Asynchronous Trigger for FORMATION
-- @function [parent=#FORMATION] __FormationBox
-- @param #FORMATION self
-- @param #number Delay
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
-- @param #number ZLevels The amount of levels on the Z-axis.
self:AddTransition( "*", "Follow", "Following" )
self:FormationLeftLine( 500, 0, 250, 250 )
self.FollowName = FollowName
self.CT1 = 0
self.GT1 = 0
return self
end
--- Set time interval between updates of the formation.
-- @param #FORMATION self
-- @param #number dt Time step in seconds between formation updates. Default is every 0.5 seconds.
-- @return #FORMATION
function FORMATION:SetFollowTimeInterval(dt)
self.dtFollow=dt or 0.5
return self
end
--- This function is for test, it will put on the frequency of the FollowScheduler a red smoke at the direction vector calculated for the escort to fly to.
-- This allows to visualize where the escort is flying to.
-- @param #FORMATION self
-- @param #boolean SmokeDirection If true, then the direction vector will be smoked.
-- @return #FORMATION
function FORMATION:TestSmokeDirectionVector( SmokeDirection )
self.SmokeDirectionVector = ( SmokeDirection == true ) and true or false
return self
end
--- FormationLine Handler OnAfter for FORMATION
-- @param #FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
-- @return #FORMATION
function FORMATION:onafterFormationLine( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace, Formation )
self:F( { FollowGroupSet, From , Event ,To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace, Formation } )
XStart = XStart or self.XStart
XSpace = XSpace or self.XSpace
YStart = YStart or self.YStart
YSpace = YSpace or self.YSpace
ZStart = ZStart or self.ZStart
ZSpace = ZSpace or self.ZSpace
local FollowSet = FollowGroupSet:GetSet()
local i = 1 --FF i=0 caused first unit to have no XSpace! Probably needs further adjustments. This is just a quick work around.
for FollowID, FollowGroup in pairs( FollowSet ) do
local PointVec3 = COORDINATE:New()
PointVec3:SetX( XStart + i * XSpace )
PointVec3:SetY( YStart + i * YSpace )
PointVec3:SetZ( ZStart + i * ZSpace )
local Vec3 = PointVec3:GetVec3()
FollowGroup:SetState( self, "FormationVec3", Vec3 )
i = i + 1
FollowGroup:SetState( FollowGroup, "Formation", Formation )
end
return self
end
--- FormationTrail Handler OnAfter for FORMATION
-- @param #FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @return #FORMATION
function FORMATION:onafterFormationTrail( FollowGroupSet, From , Event , To, XStart, XSpace, YStart )
self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,0,0,0, self.Formation.Trail )
return self
end
--- FormationStack Handler OnAfter for FORMATION
-- @param #FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @return #FORMATION
function FORMATION:onafterFormationStack( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace )
self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,YSpace,0,0, self.Formation.Stack )
return self
end
--- FormationLeftLine Handler OnAfter for FORMATION
-- @param #FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
-- @return #FORMATION
function FORMATION:onafterFormationLeftLine( FollowGroupSet, From , Event , To, XStart, YStart, ZStart, ZSpace )
self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,0,YStart,0,-ZStart,-ZSpace, self.Formation.LeftLine )
return self
end
--- FormationRightLine Handler OnAfter for FORMATION
-- @param #FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
-- @return #FORMATION
function FORMATION:onafterFormationRightLine( FollowGroupSet, From , Event , To, XStart, YStart, ZStart, ZSpace )
self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,0,YStart,0,ZStart,ZSpace,self.Formation.RightLine)
return self
end
--- FormationLeftWing Handler OnAfter for FORMATION
-- @param #FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
function FORMATION:onafterFormationLeftWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, ZStart, ZSpace )
self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,0,-ZStart,-ZSpace,self.Formation.LeftWing)
return self
end
--- FormationRightWing Handler OnAfter for FORMATION
-- @function [parent=#FORMATION] OnAfterFormationRightWing
-- @param #FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
function FORMATION:onafterFormationRightWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, ZStart, ZSpace )
self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,0,ZStart,ZSpace,self.Formation.RightWing)
return self
end
--- FormationCenterWing Handler OnAfter for FORMATION
-- @param #FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
function FORMATION:onafterFormationCenterWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace )
local FollowSet = FollowGroupSet:GetSet()
local i = 0
for FollowID, FollowGroup in pairs( FollowSet ) do
local PointVec3 = COORDINATE:New()
local Side = ( i % 2 == 0 ) and 1 or -1
local Row = i / 2 + 1
PointVec3:SetX( XStart + Row * XSpace )
PointVec3:SetY( YStart )
PointVec3:SetZ( Side * ( ZStart + i * ZSpace ) )
local Vec3 = PointVec3:GetVec3()
FollowGroup:SetState( self, "FormationVec3", Vec3 )
i = i + 1
FollowGroup:SetState( FollowGroup, "Formation", self.Formation.Vic )
end
return self
end
--- FormationVic Handle for FORMATION
-- @param #FORMATION self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
-- @return #FORMATION
function FORMATION:onafterFormationVic( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace )
self:onafterFormationCenterWing(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,YSpace,ZStart,ZSpace)
return self
end
--- FormationBox Handler OnAfter for FORMATION
-- @param #FORMATION self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
-- @param #number ZLevels The amount of levels on the Z-axis.
-- @return #FORMATION
function FORMATION:onafterFormationBox( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace, ZLevels )
local FollowSet = FollowGroupSet:GetSet()
local i = 0
for FollowID, FollowGroup in pairs( FollowSet ) do
local PointVec3 = COORDINATE:New()
local ZIndex = i % ZLevels
local XIndex = math.floor( i / ZLevels )
local YIndex = math.floor( i / ZLevels )
PointVec3:SetX( XStart + XIndex * XSpace )
PointVec3:SetY( YStart + YIndex * YSpace )
PointVec3:SetZ( -ZStart - (ZSpace * ZLevels / 2 ) + ZSpace * ZIndex )
local Vec3 = PointVec3:GetVec3()
FollowGroup:SetState( self, "FormationVec3", Vec3 )
i = i + 1
FollowGroup:SetState( FollowGroup, "Formation", self.Formation.Box )
end
return self
end
--- Use the method @{FORMATION#FORMATION.SetFlightRandomization}() to make the air units in your formation randomize their flight a bit while in formation.
-- @param #FORMATION self
-- @param #number FlightRandomization The formation flying errors that pilots can make while in formation. Is a range set in meters.
-- @return #FORMATION
function FORMATION:SetFlightRandomization( FlightRandomization )
self.FlightRandomization = FlightRandomization
return self
end
--- Stop function. Formation will not be updated any more.
-- @param #FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The following set of groups.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To The to state.
function FORMATION:onafterStop(FollowGroupSet, From, Event, To)
self:E("Stopping formation.")
end
--- Follow event fuction. Check if coming from state "stopped". If so the transition is rejected.
-- @param #FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The following set of groups.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To The to state.
function FORMATION:onbeforeFollow( FollowGroupSet, From, Event, To )
if From=="Stopped" then
return false -- Deny transition.
end
return true
end
--- Enter following state.
-- @param #FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The following set of groups.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To The to state.
function FORMATION:onenterFollowing( FollowGroupSet )
if self.FollowUnit:IsAlive() then
local ClientUnit = self.FollowUnit
local CT1, CT2, CV1, CV2
CT1 = ClientUnit:GetState( self, "CT1" )
local CuVec3=ClientUnit:GetVec3()
if CT1 == nil or CT1 == 0 then
ClientUnit:SetState( self, "CV1", CuVec3)
ClientUnit:SetState( self, "CT1", timer.getTime() )
else
CT1 = ClientUnit:GetState( self, "CT1" )
CT2 = timer.getTime()
CV1 = ClientUnit:GetState( self, "CV1" )
CV2 = CuVec3
ClientUnit:SetState( self, "CT1", CT2 )
ClientUnit:SetState( self, "CV1", CV2 )
end
--FollowGroupSet:ForEachGroupAlive( bla, self, ClientUnit, CT1, CV1, CT2, CV2)
for _,_group in pairs(FollowGroupSet:GetSet()) do
local group=_group --Wrapper.Group#GROUP
if group and group:IsAlive() then
self:FollowMe(group, ClientUnit, CT1, CV1, CT2, CV2)
end
end
self:__Follow( -self.dtFollow )
end
end
--- Follow me.
-- @param #FORMATION self
-- @param Wrapper.Group#GROUP FollowGroup Follow group.
-- @param Wrapper.Unit#UNIT ClientUnit Client Unit.
-- @param DCS#Time CT1 Time
-- @param DCS#Vec3 CV1 Vec3
-- @param DCS#Time CT2 Time
-- @param DCS#Vec3 CV2 Vec3
function FORMATION:FollowMe(FollowGroup, ClientUnit, CT1, CV1, CT2, CV2)
if not self:Is("Stopped") then
self:T({Mode=FollowGroup:GetState( FollowGroup, "Mode" )})
FollowGroup:OptionROTEvadeFire()
FollowGroup:OptionROEReturnFire()
local GroupUnit = FollowGroup:GetUnit( 1 )
local GuVec3=GroupUnit:GetVec3()
local FollowFormation = FollowGroup:GetState( self, "FormationVec3" )
if FollowFormation then
local FollowDistance = FollowFormation.x
local GT1 = GroupUnit:GetState( self, "GT1" )
if CT1 == nil or CT1 == 0 or GT1 == nil or GT1 == 0 then
GroupUnit:SetState( self, "GV1", GuVec3)
GroupUnit:SetState( self, "GT1", timer.getTime() )
else
local CD = ( ( CV2.x - CV1.x )^2 + ( CV2.y - CV1.y )^2 + ( CV2.z - CV1.z )^2 ) ^ 0.5
local CT = CT2 - CT1
local CS = ( 3600 / CT ) * ( CD / 1000 ) / 3.6
local CDv = { x = CV2.x - CV1.x, y = CV2.y - CV1.y, z = CV2.z - CV1.z }
local Ca = math.atan2( CDv.x, CDv.z )
local GT1 = GroupUnit:GetState( self, "GT1" )
local GT2 = timer.getTime()
local GV1 = GroupUnit:GetState( self, "GV1" )
local GV2 = GuVec3
GV2.x=GV2.x+math.random( -self.FlightRandomization / 2, self.FlightRandomization / 2 )
GV2.y=GV2.y+math.random( -self.FlightRandomization / 2, self.FlightRandomization / 2 )
GV2.z=GV2.z+math.random( -self.FlightRandomization / 2, self.FlightRandomization / 2 )
GroupUnit:SetState( self, "GT1", GT2 )
GroupUnit:SetState( self, "GV1", GV2 )
local GD = ( ( GV2.x - GV1.x )^2 + ( GV2.y - GV1.y )^2 + ( GV2.z - GV1.z )^2 ) ^ 0.5
local GT = GT2 - GT1
-- Calculate the distance
local GDv = { x = GV2.x - CV1.x, y = GV2.y - CV1.y, z = GV2.z - CV1.z }
local Alpha_T = math.atan2( GDv.x, GDv.z ) - math.atan2( CDv.x, CDv.z )
local Alpha_R = ( Alpha_T < 0 ) and Alpha_T + 2 * math.pi or Alpha_T
local Position = math.cos( Alpha_R )
local GD = ( ( GDv.x )^2 + ( GDv.z )^2 ) ^ 0.5
local Distance = GD * Position + - CS * 0.5
-- Calculate the group direction vector
local GV = { x = GV2.x - CV2.x, y = GV2.y - CV2.y, z = GV2.z - CV2.z }
-- Calculate GH2, GH2 with the same height as CV2.
local GH2 = { x = GV2.x, y = CV2.y + FollowFormation.y, z = GV2.z }
-- Calculate the angle of GV to the orthonormal plane
local alpha = math.atan2( GV.x, GV.z )
local GVx = FollowFormation.z * math.cos( Ca ) + FollowFormation.x * math.sin( Ca )
local GVz = FollowFormation.x * math.cos( Ca ) - FollowFormation.z * math.sin( Ca )
-- Now we calculate the intersecting vector between the circle around CV2 with radius FollowDistance and GH2.
-- From the GeoGebra model: CVI = (x(CV2) + FollowDistance cos(alpha), y(GH2) + FollowDistance sin(alpha), z(CV2))
local Inclination = ( Distance + FollowFormation.x ) / 10
if Inclination < -30 then
Inclination = - 30
end
local CVI = {
x = CV2.x + CS * 10 * math.sin(Ca),
y = GH2.y + Inclination, -- + FollowFormation.y,
z = CV2.z + CS * 10 * math.cos(Ca),
}
-- Calculate the direction vector DV of the escort group. We use CVI as the base and CV2 as the direction.
local DV = { x = CV2.x - CVI.x, y = CV2.y - CVI.y, z = CV2.z - CVI.z }
-- We now calculate the unary direction vector DVu, so that we can multiply DVu with the speed, which is expressed in meters / s.
-- We need to calculate this vector to predict the point the escort group needs to fly to according its speed.
-- The distance of the destination point should be far enough not to have the aircraft starting to swipe left to right...
local DVu = { x = DV.x / FollowDistance, y = DV.y, z = DV.z / FollowDistance }
-- Now we can calculate the group destination vector GDV.
local GDV = { x = CVI.x, y = CVI.y, z = CVI.z }
local ADDx = FollowFormation.x * math.cos(alpha) - FollowFormation.z * math.sin(alpha)
local ADDz = FollowFormation.z * math.cos(alpha) + FollowFormation.x * math.sin(alpha)
local GDV_Formation = {
x = GDV.x - GVx,
y = GDV.y,
z = GDV.z - GVz
}
-- Debug smoke.
if self.SmokeDirectionVector == true then
trigger.action.smoke( GDV, trigger.smokeColor.Green )
trigger.action.smoke( GDV_Formation, trigger.smokeColor.White )
end
local Time = 120
local Speed = - ( Distance + FollowFormation.x ) / Time
if Distance > -10000 then
Speed = - ( Distance + FollowFormation.x ) / 60
end
if Distance > -2500 then
Speed = - ( Distance + FollowFormation.x ) / 20
end
local GS = Speed + CS
--self:F( { Distance = Distance, Speed = Speed, CS = CS, GS = GS } )
-- Now route the escort to the desired point with the desired speed.
FollowGroup:RouteToVec3( GDV_Formation, GS ) -- DCS models speed in Mps (Miles per second)
end
end
end
end
+47 -27
View File
@@ -22,7 +22,7 @@
-- @module Functional.Mantis
-- @image Functional.Mantis.jpg
--
-- Last Update: May 2025
-- Last Update: August 2025
-------------------------------------------------------------------------
--- **MANTIS** class, extends Core.Base#BASE
@@ -108,10 +108,15 @@
-- * Patriot
-- * Rapier
-- * Roland
-- * IRIS-T SLM
-- * Pantsir S1
-- * TOR M2
-- * C-RAM
-- * Silkworm (though strictly speaking this is a surface to ship missile)
-- * SA-2, SA-3, SA-5, SA-6, SA-7, SA-8, SA-9, SA-10, SA-11, SA-13, SA-15, SA-19
-- * From IDF mod: STUNNER IDFA, TAMIR IDFA (Note all caps!)
-- * From HDS (see note on HDS below): SA-2, SA-3, SA-10B, SA-10C, SA-12, SA-17, SA-20A, SA-20B, SA-23, HQ-2
-- * From HDS (see note on HDS below): SA-2, SA-3, SA-10B, SA-10C, SA-12, SA-17, SA-20A, SA-20B, SA-23, HQ-2, SAMP/T Block 1, SAMP/T Block 1INT, SAMP/T Block2
-- * Other Mods: Nike
--
-- * From SMA: RBS98M, RBS70, RBS90, RBS90M, RBS103A, RBS103B, RBS103AM, RBS103BM, Lvkv9040M
-- **NOTE** If you are using the Swedish Military Assets (SMA), please note that the **group name** for RBS-SAM types also needs to contain the keyword "SMA"
@@ -125,10 +130,11 @@
-- * SA-2 (with V759 missile, e.g. "Red SAM SA-2 HDS")
-- * SA-2 (with HQ-2 launcher, use HQ-2 in the group name, e.g. "Red SAM HQ-2" )
-- * SA-3 (with V601P missile, e.g. "Red SAM SA-3 HDS")
-- * SA-10B (overlap with other SA-10 types, e.g. "Red SAM SA-10B HDS")
-- * SA-10C (overlap with other SA-10 types, e.g. "Red SAM SA-10C HDS")
-- * SA-12 (launcher dependent range, e.g. "Red SAM SA-12 HDS")
-- * SA-23 (launcher dependent range, e.g. "Red SAM SA-23 HDS")
-- * SA-10B (overlap with other SA-10 types, e.g. "Red SAM SA-10B HDS" with 5P85CE launcher)
-- * SA-10C (overlap with other SA-10 types, e.g. "Red SAM SA-10C HDS" with 5P85SE launcher)
-- * SA-12 (launcher dependent range, e.g. "Red SAM SA-12 HDS 2" for the 9A82 variant and "Red SAM SA-12 HDS 1" for the 9A83 variant)
-- * SA-23 (launcher dependent range, e.g. "Red SAM SA-23 HDS 2" for the 9A82ME variant and "Red SAM SA-23 HDS 1" for the 9A83ME variant)
-- * SAMP/T (launcher dependent range, e.g. "Blue SAM SAMPT Block 1 HDS" for Block 1, "Blue SAM SAMPT Block 1INT HDS", "Blue SAM SAMPT Block 2 HDS")
--
-- The other HDS types work like the rest of the known SAM systems.
--
@@ -274,6 +280,7 @@
MANTIS = {
ClassName = "MANTIS",
name = "mymantis",
version = "0.9.34",
SAM_Templates_Prefix = "",
SAM_Group = nil,
EWR_Templates_Prefix = "",
@@ -382,15 +389,21 @@ MANTIS.SamData = {
["Chaparral"] = { Range=8, Blindspot=0, Height=3, Type="Short", Radar="Chaparral" },
["Linebacker"] = { Range=4, Blindspot=0, Height=3, Type="Point", Radar="Linebacker", Point="true" },
["Silkworm"] = { Range=90, Blindspot=1, Height=0.2, Type="Long", Radar="Silkworm" },
["HEMTT_C-RAM_Phalanx"] = { Range=2, Blindspot=0, Height=2, Type="Point", Radar="HEMTT_C-RAM_Phalanx", Point="true" },
["C-RAM"] = { Range=2, Blindspot=0, Height=2, Type="Point", Radar="HEMTT_C-RAM_Phalanx", Point="true" },
-- units from HDS Mod, multi launcher options is tricky
["SA-10B"] = { Range=75, Blindspot=0, Height=18, Type="Medium" , Radar="SA-10B"},
["SA-17"] = { Range=50, Blindspot=3, Height=30, Type="Medium", Radar="SA-17" },
["SA-17"] = { Range=50, Blindspot=3, Height=50, Type="Medium", Radar="SA-17" },
["SA-20A"] = { Range=150, Blindspot=5, Height=27, Type="Long" , Radar="S-300PMU1"},
["SA-20B"] = { Range=200, Blindspot=4, Height=27, Type="Long" , Radar="S-300PMU2"},
["HQ-2"] = { Range=50, Blindspot=6, Height=35, Type="Medium", Radar="HQ_2_Guideline_LN" },
["TAMIR IDFA"] = { Range=20, Blindspot=0.6, Height=12.3, Type="Short", Radar="IRON_DOME_LN" },
["STUNNER IDFA"] = { Range=250, Blindspot=1, Height=45, Type="Long", Radar="DAVID_SLING_LN" },
["STUNNER IDFA"] = { Range=250, Blindspot=1, Height=45, Type="Long", Radar="DAVID_SLING_LN" },
["NIKE"] = { Range=155, Blindspot=6, Height=30, Type="Long", Radar="HIPAR" },
["Dog Ear"] = { Range=11, Blindspot=0, Height=9, Type="Point", Radar="Dog Ear", Point="true" },
-- CH Added to DCS core 2.9.19.x
["Pantsir S1"] = { Range=20, Blindspot=1.2, Height=15, Type="Point", Radar="PantsirS1" , Point="true" },
["Tor M2"] = { Range=12, Blindspot=1, Height=10, Type="Point", Radar="TorM2", Point="true" },
["IRIS-T SLM"] = { Range=40, Blindspot=0.5, Height=20, Type="Medium", Radar="CH_IRIST_SLM" },
}
--- SAM data HDS
@@ -406,13 +419,17 @@ MANTIS.SamDataHDS = {
-- group name MUST contain HDS to ID launcher type correctly!
["SA-2 HDS"] = { Range=56, Blindspot=7, Height=30, Type="Medium", Radar="V759" },
["SA-3 HDS"] = { Range=20, Blindspot=6, Height=30, Type="Short", Radar="V-601P" },
["SA-10C HDS 2"] = { Range=90, Blindspot=5, Height=25, Type="Long" , Radar="5P85DE ln"}, -- V55RUD
["SA-10C HDS 1"] = { Range=90, Blindspot=5, Height=25, Type="Long" , Radar="5P85CE ln"}, -- V55RUD
["SA-12 HDS 2"] = { Range=100, Blindspot=10, Height=25, Type="Long" , Radar="S-300V 9A82 l"},
["SA-12 HDS 1"] = { Range=75, Blindspot=1, Height=25, Type="Long" , Radar="S-300V 9A83 l"},
["SA-10B HDS"] = { Range=90, Blindspot=5, Height=25, Type="Long" , Radar="5P85CE ln"}, -- V55RUD
["SA-10C HDS"] = { Range=75, Blindspot=5, Height=25, Type="Long" , Radar="5P85SE ln"}, -- V55RUD
["SA-17 HDS"] = { Range=50, Blindspot=3, Height=50, Type="Medium", Radar="SA-17 " },
["SA-12 HDS 2"] = { Range=100, Blindspot=13, Height=30, Type="Long" , Radar="S-300V 9A82 l"},
["SA-12 HDS 1"] = { Range=75, Blindspot=6, Height=25, Type="Long" , Radar="S-300V 9A83 l"},
["SA-23 HDS 2"] = { Range=200, Blindspot=5, Height=37, Type="Long", Radar="S-300VM 9A82ME" },
["SA-23 HDS 1"] = { Range=100, Blindspot=1, Height=50, Type="Long", Radar="S-300VM 9A83ME" },
["HQ-2 HDS"] = { Range=50, Blindspot=6, Height=35, Type="Medium", Radar="HQ_2_Guideline_LN" },
["SAMPT Block 1 HDS"] = { Range=120, Blindspot=1, Height=20, Type="long", Radar="SAMPT_MLT_Blk1" }, -- Block 1 Launcher
["SAMPT Block 1INT HDS"] = { Range=150, Blindspot=1, Height=25, Type="long", Radar="SAMPT_MLT_Blk1NT" }, -- Block 1-INT Launcher
["SAMPT Block 2 HDS"] = { Range=200, Blindspot=10, Height=70, Type="long", Radar="SAMPT_MLT_Blk2" }, -- Block 2 Launcher
}
--- SAM data SMA
@@ -452,15 +469,15 @@ MANTIS.SamDataCH = {
-- https://www.currenthill.com/
-- group name MUST contain CHM to ID launcher type correctly!
["2S38 CHM"] = { Range=6, Blindspot=0.1, Height=4.5, Type="Short", Radar="2S38" },
["PantsirS1 CHM"] = { Range=20, Blindspot=1.2, Height=15, Type="Short", Radar="PantsirS1" },
["PantsirS1 CHM"] = { Range=20, Blindspot=1.2, Height=15, Type="Point", Radar="PantsirS1", Point="true" },
["PantsirS2 CHM"] = { Range=30, Blindspot=1.2, Height=18, Type="Medium", Radar="PantsirS2" },
["PGL-625 CHM"] = { Range=10, Blindspot=1, Height=5, Type="Short", Radar="PGL_625" },
["HQ-17A CHM"] = { Range=15, Blindspot=1.5, Height=10, Type="Short", Radar="HQ17A" },
["M903PAC2 CHM"] = { Range=120, Blindspot=3, Height=24.5, Type="Long", Radar="MIM104_M903_PAC2" },
["M903PAC3 CHM"] = { Range=160, Blindspot=1, Height=40, Type="Long", Radar="MIM104_M903_PAC3" },
["TorM2 CHM"] = { Range=12, Blindspot=1, Height=10, Type="Short", Radar="TorM2" },
["TorM2K CHM"] = { Range=12, Blindspot=1, Height=10, Type="Short", Radar="TorM2K" },
["TorM2M CHM"] = { Range=16, Blindspot=1, Height=10, Type="Short", Radar="TorM2M" },
["TorM2 CHM"] = { Range=12, Blindspot=1, Height=10, Type="Point", Radar="TorM2", Point="true" },
["TorM2K CHM"] = { Range=12, Blindspot=1, Height=10, Type="Point", Radar="TorM2K", Point="true" },
["TorM2M CHM"] = { Range=16, Blindspot=1, Height=10, Type="Point", Radar="TorM2M", Point="true" },
["NASAMS3-AMRAAMER CHM"] = { Range=50, Blindspot=2, Height=35.7, Type="Medium", Radar="CH_NASAMS3_LN_AMRAAM_ER" },
["NASAMS3-AIM9X2 CHM"] = { Range=20, Blindspot=0.2, Height=18, Type="Short", Radar="CH_NASAMS3_LN_AIM9X2" },
["C-RAM CHM"] = { Range=2, Blindspot=0, Height=2, Type="Point", Radar="CH_Centurion_C_RAM", Point="true" },
@@ -680,9 +697,6 @@ do
-- counter for SAM table updates
self.checkcounter = 1
-- TODO Version
-- @field #string version
self.version="0.9.30"
self:I(string.format("***** Starting MANTIS Version %s *****", self.version))
--- FSM Functions ---
@@ -879,7 +893,11 @@ do
self.AcceptZones = AcceptZones or {}
self.RejectZones = RejectZones or {}
self.ConflictZones = ConflictZones or {}
if #self.AcceptZones > 0 or #self.RejectZones > 0 or #self.ConflictZones > 0 then
self.AcceptZonesNo = UTILS.TableLength(self.AcceptZones)
self.RejectZonesNo = UTILS.TableLength(self.RejectZones)
self.ConflictZonesNo = UTILS.TableLength(self.ConflictZones)
self:T(string.format("AcceptZonesNo = %d | RejectZonesNo = %d | ConflictZonesNo = %d",self.AcceptZonesNo,self.RejectZonesNo,self.ConflictZonesNo))
if self.AcceptZonesNo > 0 or self.RejectZonesNo > 0 or self.ConflictZonesNo > 0 then
self.usezones = true
end
return self
@@ -1271,7 +1289,8 @@ do
self:T(self.lid.."_CheckCoordinateInZones")
local inzone = false
-- acceptzones
if #self.AcceptZones > 0 then
self:T(string.format("AcceptZonesNo = %d | RejectZonesNo = %d | ConflictZonesNo = %d",self.AcceptZonesNo,self.RejectZonesNo,self.ConflictZonesNo))
if self.AcceptZonesNo > 0 then
for _,_zone in pairs(self.AcceptZones) do
local zone = _zone -- Core.Zone#ZONE
if zone:IsCoordinateInZone(coord) then
@@ -1282,7 +1301,7 @@ do
end
end
-- rejectzones
if #self.RejectZones > 0 and inzone then -- maybe in accept zone, but check the overlaps
if self.RejectZonesNo > 0 then
for _,_zone in pairs(self.RejectZones) do
local zone = _zone -- Core.Zone#ZONE
if zone:IsCoordinateInZone(coord) then
@@ -1293,7 +1312,7 @@ do
end
end
-- conflictzones
if #self.ConflictZones > 0 and not inzone then -- if not already accepted, might be in conflict zones
if self.ConflictZonesNo > 0 then
for _,_zone in pairs(self.ConflictZones) do
local zone = _zone -- Core.Zone#ZONE
if zone:IsCoordinateInZone(coord) then
@@ -1359,6 +1378,7 @@ do
end
-- check accept/reject zones
local zonecheck = true
self:T("self.usezones = "..tostring(self.usezones))
if self.usezones then
-- DONE
zonecheck = self:_CheckCoordinateInZones(coord)
@@ -1798,7 +1818,7 @@ do
if self.Shorad and self.Shorad.ActiveGroups and self.Shorad.ActiveGroups[name] then
activeshorad = true
end
if IsInZone and not suppressed and not activeshorad then --check any target in zone and not currently managed by SEAD
if IsInZone and (not suppressed) and (not activeshorad) then --check any target in zone and not currently managed by SEAD
if samgroup:IsAlive() then
-- switch on SAM
local switch = false
@@ -1830,7 +1850,7 @@ do
-- link in to SHORAD if available
-- DONE: Test integration fully
if self.ShoradLink and (Distance < self.ShoradActDistance or Distance < blind ) then -- don't give SHORAD position away too early
local Shorad = self.Shorad
local Shorad = self.Shorad --Functional.Shorad#SHORAD
local radius = self.checkradius
local ontime = self.ShoradTime
Shorad:WakeUpShorad(name, radius, ontime)
@@ -2110,7 +2130,7 @@ do
if self.debug and self.verbose then
self:I(self.lid .. "Status Report")
for _name,_state in pairs(self.SamStateTracker) do
self:I(string.format("Site %s\tStatus %s",_name,_state))
self:I(string.format("Site %s | Status %s",_name,_state))
end
end
local interval = self.detectinterval * -1
+217 -61
View File
@@ -90,7 +90,8 @@
-- @image Scoring.JPG
--- @type SCORING
-- @field Players A collection of the current players that have joined the game.
-- @field #table Players A collection of the current players that have joined the game.
-- @field Core.Set#SET_SCENERY ScoringScenery
-- @extends Core.Base#BASE
--- SCORING class
@@ -229,7 +230,8 @@ SCORING = {
ClassID = 0,
Players = {},
AutoSave = true,
version = "1.18.4"
version = "1.18.4",
ScoringScenery = nil, -- Core.Set#SET_SCENERY
}
local _SCORINGCoalition = {
@@ -320,8 +322,12 @@ function SCORING:New( GameName, SavePath, AutoSave )
-- Create the CSV file.
self.AutoSavePath = SavePath
self.AutoSave = AutoSave or true
self:OpenCSV( GameName )
self.AutoSave = (AutoSave == nil or AutoSave == true) and true or false
if self.AutoSavePath and self.AutoSave == true then
self:OpenCSV( GameName )
end
self:I("SCORING "..tostring(GameName).." started! v"..self.version)
return self
@@ -375,7 +381,7 @@ function SCORING:AddUnitScore( ScoreUnit, Score )
return self
end
--- Removes a @{Wrapper.Unit} for additional scoring when the @{Wrapper.Unit} is destroyed.
--- Removes a @{Wrapper.Unit} for scoring when the @{Wrapper.Unit} is destroyed.
-- @param #SCORING self
-- @param Wrapper.Unit#UNIT ScoreUnit The @{Wrapper.Unit} for which the Score needs to be given.
-- @return #SCORING
@@ -392,10 +398,26 @@ end
-- Note that if there was already a @{Wrapper.Static} declared within the scoring with the same name,
-- then the old @{Wrapper.Static} will be replaced with the new @{Wrapper.Static}.
-- @param #SCORING self
-- @param Wrapper.Static#UNIT ScoreStatic The @{Wrapper.Static} for which the Score needs to be given.
-- @param Wrapper.Static#STATIC ScoreStatic The @{Wrapper.Static} for which the Score needs to be given.
-- @param #number Score The Score value.
-- @return #SCORING
function SCORING:AddStaticScore( ScoreStatic, Score )
return self:AddScoreStatic( ScoreStatic, Score )
end
--- Add a @{Wrapper.Static} for additional scoring when the @{Wrapper.Static} is destroyed.
-- Note that if there was already a @{Wrapper.Static} declared within the scoring with the same name,
-- then the old @{Wrapper.Static} will be replaced with the new @{Wrapper.Static}.
-- @param #SCORING self
-- @param Wrapper.Static#STATIC ScoreStatic The @{Wrapper.Static} for which the Score needs to be given.
-- @param #number Score The Score value.
-- @return #SCORING
function SCORING:AddScoreStatic( ScoreStatic, Score )
if ScoreStatic == nil then
BASE:E("SCORING.AddStaticScore: Parameter ScoreStatic is nil!")
return self
end
local StaticName = ScoreStatic:GetName()
@@ -404,7 +426,61 @@ function SCORING:AddStaticScore( ScoreStatic, Score )
return self
end
--- Removes a @{Wrapper.Static} for additional scoring when the @{Wrapper.Static} is destroyed.
--- Add a @{Wrapper.Scenery} for additional scoring when the @{Wrapper.Scenery} is destroyed.
-- @param #SCORING self
-- @param Wrapper.Scenery#SCENERY ScoreScenery The @{Wrapper.Scenery} for which the Score needs to be given.
-- @param #number Score The Score value.
-- @return #SCORING
function SCORING:AddScoreScenery( ScoreScenery, Score )
if ScoreScenery == nil then
self:E("SCORING.ScoreScenery: Parameter ScoreScenery is nil!")
return self
end
if not self.ScoringScenery then
self.ScoringScenery = SET_SCENERY:New() -- Core.Set#SET_SCENERY
end
local StaticName = ScoreScenery:GetName()
self:T("Scenery name = ".. StaticName)
self.ScoringScenery:AddScenery(ScoreScenery)
return self
end
--- Removes a @{Wrapper.Scenery} for scoring when the @{Wrapper.Scenery} is destroyed.
-- @param #SCORING self
-- @param Wrapper.Scenery#SCENERY ScoreStatic The @{Wrapper.Scenery} for which the Score needs to be given.
-- @return #SCORING
function SCORING:RemoveSceneryScore( ScoreScenery )
local StaticName = ScoreScenery:GetName()
self.ScoringObjects[StaticName] = nil
return self
end
--- Specify a special additional score for a @{Core.Set#SET_SCENERY}.
-- @param #SCORING self
-- @param Core.Set#SET_SCENERY Set The @{Core.Set#SET_SCENERY} for which each @{Wrapper.Scenery} in the SET a Score is given.
-- @param #number Score The Score value.
-- @return #SCORING
function SCORING:AddScoreSetScenery(Set, Score)
local set = Set.Set
for _,_static in pairs (set) do
if _static ~= nil then
self:AddScoreScenery(_static,Score)
end
end
return self
end
--- Removes a @{Wrapper.Static} for scoring when the @{Wrapper.Static} is destroyed.
-- @param #SCORING self
-- @param Wrapper.Static#UNIT ScoreStatic The @{Wrapper.Static} for which the Score needs to be given.
-- @return #SCORING
@@ -459,6 +535,31 @@ function SCORING:AddScoreSetGroup(Set, Score)
return self
end
--- Specify a special additional score for a @{Core.Set#SET_STATIC}.
-- @param #SCORING self
-- @param Core.Set#SET_STATIC Set The @{Core.Set#SET_STATIC} for which each @{Wrapper.Static} in the SET a Score is given.
-- @param #number Score The Score value.
-- @return #SCORING
function SCORING:AddScoreSetStatic(Set, Score)
local set = Set:GetSetObjects()
for _,_static in pairs (set) do
if _static and _static:IsAlive() then
self:AddStaticScore(_static,Score)
end
end
local function AddScore(static)
self:AddStaticScore(static,Score)
end
function Set:OnAfterAdded(From,Event,To,ObjectName,Object)
AddScore(Object)
end
return self
end
--- Add a @{Core.Zone} to define additional scoring when any object is destroyed in that zone.
-- Note that if a @{Core.Zone} with the same name is already within the scoring added, the @{Core.Zone} (type) and Score will be replaced!
-- This allows for a dynamic destruction zone evolution within your mission.
@@ -478,7 +579,24 @@ function SCORING:AddZoneScore( ScoreZone, Score )
return self
end
--- Remove a @{Core.Zone} for additional scoring.
--- Add a @{Core.Set#SET_ZONE} to define additional scoring when any object is destroyed in that zone.
-- Note that if a @{Core.Zone} with the same name is already within the scoring added, the @{Core.Zone} (type) and Score will be replaced!
-- This allows for a dynamic destruction zone evolution within your mission.
-- @param #SCORING self
-- @param Core.Set#SET_ZONE ScoreZoneSet The @{Core.Set#SET_ZONE} which defines the destruction score perimeters.
-- Note that a zone can be a polygon or a moving zone.
-- @param #number Score The Score value.
-- @return #SCORING
function SCORING:AddZoneScoreSet( ScoreZoneSet, Score )
for _,_zone in pairs(ScoreZoneSet.Set or {}) do
self:AddZoneScore(_zone,Score)
end
return self
end
--- Remove a @{Core.Zone} for scoring.
-- The scoring will search if any @{Core.Zone} is added with the given name, and will remove that zone from the scoring.
-- This allows for a dynamic destruction zone evolution within your mission.
-- @param #SCORING self
@@ -956,7 +1074,7 @@ end
-- @param #SCORING self
-- @param Core.Event#EVENTDATA Event
function SCORING:_EventOnHit( Event )
self:F( { Event } )
--self:F( { Event } )
local InitUnit = nil
local InitUNIT = nil
@@ -985,6 +1103,8 @@ function SCORING:_EventOnHit( Event )
local TargetUnitCoalition = nil
local TargetUnitCategory = nil
local TargetUnitType = nil
local TargetIsScenery = false
local TargetSceneryObject = nil
if Event.IniDCSUnit then
@@ -1006,7 +1126,7 @@ function SCORING:_EventOnHit( Event )
InitUnitCategory = _SCORINGCategory[InitCategory]
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
if Event.TgtDCSUnit then
@@ -1024,12 +1144,23 @@ function SCORING:_EventOnHit( Event )
-- TargetCategory = TargetUnit:getDesc().category
TargetCategory = Event.TgtCategory
TargetType = Event.TgtTypeName
-- Scenery hit
if TargetUNIT ~= nil and TargetUNIT:IsInstanceOf("SCENERY") then
TargetCategory = Unit.Category.STRUCTURE
TargetIsScenery = true
TargetType = "Scenery"
TargetSceneryObject = TargetUNIT
self:T("***** Target is Scenery and TargetUNIT is SCENERY object!")
UTILS.PrintTableToLog(TargetSceneryObject)
end
TargetUnitCoalition = _SCORINGCoalition[TargetCoalition]
TargetUnitCategory = _SCORINGCategory[TargetCategory]
TargetUnitType = TargetType
self:T( { TargetUnitName, TargetGroupName, TargetPlayerName, TargetCoalition, TargetCategory, TargetType, TargetUnitCoalition, TargetUnitCategory, TargetUnitType } )
--self:T( { TargetUnitName=TargetUnitName, TargetGroupName=TargetGroupName, TargetPlayerName=TargetPlayerName, TargetCoalition=TargetCoalition, TargetCategory=TargetCategory, TargetType=TargetType, TargetUnitCoalition=TargetUnitCoalition, TargetUnitCategory=TargetUnitCategory, TargetUnitType=TargetUnitType } )
end
if InitPlayerName ~= nil then -- It is a player that is hitting something
@@ -1042,7 +1173,7 @@ function SCORING:_EventOnHit( Event )
self:T( "Hitting Something" )
-- What is he hitting?
if TargetCategory then
if (TargetCategory ~=nil) and (TargetIsScenery == false) then
-- A target got hit, score it.
-- Player contains the score data from self.Players[InitPlayerName]
@@ -1062,7 +1193,7 @@ function SCORING:_EventOnHit( Event )
PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0
PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT
-- After an instant kill we can't compute the threat level anymore. To fix this we compute at OnEventBirth
if PlayerHit.UNIT.ThreatType == nil then
if PlayerHit.UNIT and PlayerHit.UNIT.ThreatType == nil then
PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel()
-- if this fails for some reason, set a good default value
if PlayerHit.ThreatType == nil or PlayerHit.ThreatType == "" then
@@ -1117,17 +1248,22 @@ function SCORING:_EventOnHit( Event )
MESSAGE.Type.Update )
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
else
elseif TargetIsScenery ~= true then
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit enemy target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " ..
"Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty,
MESSAGE.Type.Update )
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
elseif TargetIsScenery == true then
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit scenery object." .. " Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty,
MESSAGE.Type.Update )
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
end
self:ScoreCSV( InitPlayerName, TargetPlayerName, "HIT_SCORE", 1, 1, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
end
else -- A scenery object was hit.
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit scenery object.",
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit nothing special.",
MESSAGE.Type.Update )
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
@@ -1143,21 +1279,21 @@ function SCORING:_EventOnHit( Event )
-- It is a weapon initiated by a player, that is hitting something
-- This seems to occur only with scenery and static objects.
if Event.WeaponPlayerName ~= nil then
self:_AddPlayerFromUnit( Event.WeaponUNIT )
if self.Players[Event.WeaponPlayerName] then -- This should normally not happen, but i'll test it anyway.
if TargetPlayerName ~= nil then -- It is a player hitting another player ...
self:_AddPlayerFromUnit( TargetUNIT )
end
if Event.WeaponPlayerName ~= nil or TargetIsScenery == true then
local playername = Event.WeaponPlayerName or Event.IniPlayerName or "Ghost"
--self:_AddPlayerFromUnit( Event.WeaponUNIT )
if self.Players[playername] then -- This should normally not happen, but i'll test it anyway.
self:T( "Hitting Scenery" )
self:T( "Hitting Scenery or Static" )
-- What is he hitting?
if TargetCategory then
if Event.TgtObjectCategory then
-- A scenery or static got hit, score it.
-- Player contains the score data from self.Players[WeaponPlayerName]
local Player = self.Players[Event.WeaponPlayerName]
local Player = self.Players[playername]
-- Ensure there is a hit table per TargetCategory and TargetUnitName.
Player.Hit[TargetCategory] = Player.Hit[TargetCategory] or {}
@@ -1165,15 +1301,17 @@ function SCORING:_EventOnHit( Event )
-- PlayerHit contains the score counters and data per unit that was hit.
local PlayerHit = Player.Hit[TargetCategory][TargetUnitName]
-- Init player scores
PlayerHit.Score = PlayerHit.Score or 0
PlayerHit.Penalty = PlayerHit.Penalty or 0
PlayerHit.ScoreHit = PlayerHit.ScoreHit or 0
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit or 0
PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0
PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT
-- After an instant kill we can't compute the threat level anymore. To fix this we compute at OnEventBirth
if PlayerHit.UNIT.ThreatType == nil then
if PlayerHit.UNIT and PlayerHit.UNIT.ThreatType == nil then
PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel()
-- if this fails for some reason, set a good default value
if PlayerHit.ThreatType == nil then
@@ -1181,8 +1319,8 @@ function SCORING:_EventOnHit( Event )
PlayerHit.ThreatType = "Unknown"
end
else
PlayerHit.ThreatLevel = PlayerHit.UNIT.ThreatLevel
PlayerHit.ThreatType = PlayerHit.UNIT.ThreatType
PlayerHit.ThreatLevel = PlayerHit.UNIT and PlayerHit.UNIT.ThreatLevel or 1
PlayerHit.ThreatType = PlayerHit.UNIT and PlayerHit.UNIT.ThreatType or "Unknown"
end
-- Only grant hit scores if there was more than one second between the last hit.
@@ -1190,50 +1328,68 @@ function SCORING:_EventOnHit( Event )
PlayerHit.TimeStamp = timer.getTime()
local Score = 0
if InitCoalition then -- A coalition object was hit, probably a static.
if InitCoalition == TargetCoalition then
-- TODO: Penalty according scale
local Penalty = 10
Player.Penalty = Player.Penalty + Penalty --* self.ScaleDestroyPenalty
PlayerHit.Penalty = PlayerHit.Penalty + Penalty --* self.ScaleDestroyPenalty
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1 * self.ScaleDestroyPenalty
MESSAGE
:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit friendly target " ..
TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
"Penalty: -" .. 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 )
else
local TgtName = Event.TgtDCSUnit and Event.TgtDCSUnit.getName and Event.TgtDCSUnit:getName() or "Unknown"
--if InitCoalition then -- A coalition object was hit, probably a static.
if TargetIsScenery == true and self.ScoringScenery:IsInSet(TargetSceneryObject) then
Player.Score = Player.Score + self.ScoreIncrementOnHit
PlayerHit.Score = PlayerHit.Score + self.ScoreIncrementOnHit
PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit enemy target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. playername .. "' hit scenery target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
"Score: " .. PlayerHit.Score .. ". Score Total:" .. 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_SCORE", 1, 1, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
end
else -- A scenery object was hit.
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit scenery object.",
MESSAGE.Type.Update )
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
self:ScoreCSV( playername, TargetPlayerName, "HIT_SCORE", 1, 1, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
elseif TargetIsScenery == false and Event.TgtObjectCategory == Object.Category.STATIC and self.ScoringObjects[TgtName] then
Player.Score = Player.Score + self.ScoreIncrementOnHit
PlayerHit.Score = PlayerHit.Score + self.ScoreIncrementOnHit
PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. playername .. "' hit static target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
"Score: " .. PlayerHit.Score .. ". Score Total:" .. 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, "", "HIT_SCORE", 1, 0, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, "", "Scenery", TargetUnitType )
end
self:ScoreCSV( playername, TargetPlayerName, "HIT_SCORE", 1, 1, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
else
self:E("Hit unregistered scenery or static object - NO target! ("..TgtName..")")
end
--end
end
end
end
-- Check if there are Zones where the destruction happened.
for ZoneName, ScoreZoneData in pairs( self.ScoringZones ) do
self:F( { ScoringZone = ScoreZoneData } )
local hit=Event.TgtUnit
local ScoreZone = ScoreZoneData.ScoreZone -- Core.Zone#ZONE_BASE
local Score = ScoreZoneData.Score
if TargetUNIT and ScoreZone:IsVec2InZone( TargetUNIT:GetVec2() ) then
-- A scenery or static got hit, score it.
-- Player contains the score data from self.Players[WeaponPlayerName]
local PlayerName = Event.IniPlayerName or "Ghost"
local Player = self.Players[PlayerName]
Player.Score = Player.Score + Score
Player.Score = Player.Score + self.ScoreIncrementOnHit
MESSAGE:NewType( self.DisplayMessagePrefix .. "hit in zone '" .. ScoreZone:GetName() .. "'." ..
"Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! " .. "Total: " .. Player.Score - Player.Penalty,
MESSAGE.Type.Information )
:ToAllIf( self:IfMessagesZone() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesZone() and self:IfMessagesToCoalition() )
self:ScoreCSV( PlayerName, "", "HIT_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, "", "Zone", TargetUnitType )
end
end
end
end
end
--- Track DEAD or CRASH events for the scoring.
-- @param #SCORING self
-- @param Core.Event#EVENTDATA Event
@@ -1291,7 +1447,7 @@ function SCORING:_EventOnDeadOrCrash( Event )
local Destroyed = false
-- What is the player destroying?
if Player and Player.Hit and Player.Hit[TargetCategory] and Player.Hit[TargetCategory][TargetUnitName] and Player.Hit[TargetCategory][TargetUnitName].TimeStamp ~= 0 and (TargetUnit.BirthTime == nil or Player.Hit[TargetCategory][TargetUnitName].TimeStamp > TargetUnit.BirthTime) then -- Was there a hit for this unit for this player before registered???
if Player and Player.Hit and Player.Hit[TargetCategory] and Player.Hit[TargetCategory][TargetUnitName] and Player.Hit[TargetCategory][TargetUnitName].TimeStamp ~= 0 and TargetUnit and (TargetUnit.BirthTime == nil or Player.Hit[TargetCategory][TargetUnitName].TimeStamp > TargetUnit.BirthTime) then -- Was there a hit for this unit for this player before registered???
local TargetThreatLevel = Player.Hit[TargetCategory][TargetUnitName].ThreatLevel
local TargetThreatType = Player.Hit[TargetCategory][TargetUnitName].ThreatType
@@ -1923,7 +2079,7 @@ function SCORING:ScoreCSV( PlayerName, TargetPlayerName, ScoreType, ScoreTimes,
TargetUnitType = TargetUnitType or ""
TargetUnitName = TargetUnitName or ""
if lfs and io and os and self.AutoSave then
if lfs and io and os and self.AutoSave == true and self.CSVFile ~= nil then
self.CSVFile:write(
'"' .. self.GameName .. '"' .. ',' ..
'"' .. self.RunTime .. '"' .. ',' ..
File diff suppressed because it is too large Load Diff
@@ -1132,7 +1132,7 @@
-- specify an "Assignment". This can be later used to identify the request and take the right actions.
--
-- Once the request is processed, the @{#WAREHOUSE.OnAfterSelfRequest} function is called. This is where we hook in and postprocess the spawned assets.
-- In particular, we use the @{AI.AI_Formation#AI_FORMATION} class to make some nice escorts for our carrier.
-- In particular, we use the @{Functional.Formation#FORMATION} class to make some nice escorts for our carrier.
--
-- When the resue helo is spawned, we can check that this is the correct asset and make the helo go into formation with the carrier.
-- Once the helo runs out of fuel, it will automatically return to the ship and land. For the warehouse, this means that the "cargo", i.e. the helicopter
@@ -1175,7 +1175,7 @@
--
-- -- Define AI Formation object.
-- -- Note that this has to be a global variable or the garbage collector will remove it for some reason!
-- CarrierFormationLeft = AI_FORMATION:New(Mother, groupset, "Left Formation with Carrier", "Escort Carrier.")
-- CarrierFormationLeft = FORMATION:New(Mother, groupset, "Left Formation with Carrier")
--
-- -- Formation parameters.
-- CarrierFormationLeft:FormationLeftWing(200 ,50, 0, 0, 500, 50)
@@ -1190,7 +1190,7 @@
--
-- -- Define AI Formation object.
-- -- Note that this has to be a global variable or the garbage collector will remove it for some reason!
-- CarrierFormationRight = AI_FORMATION:New(Mother, groupset, "Right Formation with Carrier", "Escort Carrier.")
-- CarrierFormationRight = FORMATION:New(Mother, groupset, "Right Formation with Carrier")
--
-- -- Formation parameters.
-- CarrierFormationRight:FormationRightWing(200 ,50, 0, 0, 500, 50)
@@ -1208,7 +1208,7 @@
-- group:StartUncontrolled()
--
-- -- Define AI Formation object.
-- CarrierFormationHelo = AI_FORMATION:New(Mother, groupset, "Helo Formation with Carrier", "Fly Formation.")
-- CarrierFormationHelo = FORMATION:New(Mother, groupset, "Helo Formation with Carrier")
--
-- -- Formation parameters.
-- CarrierFormationHelo:FormationCenterWing(-150, 50, 20, 50, 100, 50)
@@ -3153,7 +3153,7 @@ end
-- @param #WAREHOUSE self
-- @return Core.Point#COORDINATE The coordinate of the warehouse.
function WAREHOUSE:GetCoordinate()
return self.warehouse:GetCoordinate()
return self.warehouse:GetCoord()
end
--- Get 3D vector of warehouse static.
@@ -3340,7 +3340,6 @@ function WAREHOUSE:FindAssetInDB(group)
if aid~=nil then
local asset=_WAREHOUSEDB.Assets[aid]
self:T2({asset=asset})
if asset==nil then
self:_ErrorMessage(string.format("ERROR: Asset for group %s not found in the data base!", group:GetName()), 0)
end
@@ -3918,7 +3917,7 @@ end
-- @param #string assignment A free to choose string specifying an assignment for the asset. This can be used with the @{#WAREHOUSE.OnAfterNewAsset} function.
-- @param #table other (Optional) Table of other useful data. Can be collected via WAREHOUSE.OnAfterNewAsset() function for example
function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribute, forcecargobay, forceweight, loadradius, skill, liveries, assignment, other)
self:T({group=group, ngroups=ngroups, forceattribute=forceattribute, forcecargobay=forcecargobay, forceweight=forceweight})
--self:T({group=group:GetName(), ngroups=ngroups, forceattribute=forceattribute, forcecargobay=forcecargobay, forceweight=forceweight})
-- Set default.
local n=ngroups or 1
@@ -4247,6 +4246,16 @@ function WAREHOUSE:_AssetItemInfo(asset)
self:T3({Template=asset.template})
end
--- This function uses Disposition and other fallback logic to find better ground positions for ground units.
--- NOTE: This is not a spawn randomizer.
--- It will try to find clear ground locations avoiding trees, water, roads, runways, map scenery, statics and other units in the area and modifies the provided positions table.
--- Maintains the original layout and unit positions as close as possible by searching for the next closest valid position to each unit.
--- Uses UTILS.ValidateAndRepositionGroundUnits.
-- @param #boolean Enabled Enable/disable the feature.
function WAREHOUSE:SetValidateAndRepositionGroundUnits(Enabled)
self.ValidateAndRepositionGroundUnits = Enabled
end
--- On after "NewAsset" event. A new asset has been added to the warehouse stock.
-- @param #WAREHOUSE self
-- @param #string From From state.
@@ -4436,7 +4445,6 @@ end
-- @param #WAREHOUSE.Queueitem Request Information table of the request.
-- @return #boolean If true, request is granted.
function WAREHOUSE:onbeforeRequest(From, Event, To, Request)
self:T3({warehouse=self.alias, request=Request})
-- Distance from warehouse to requesting warehouse.
local distance=self:GetCoordinate():Get2DDistance(Request.warehouse:GetCoordinate())
@@ -5965,6 +5973,10 @@ function WAREHOUSE:_SpawnAssetGroundNaval(alias, asset, request, spawnzone, late
template.y = coord.z
template.alt = coord.y
if self.ValidateAndRepositionGroundUnits then
UTILS.ValidateAndRepositionGroundUnits(template.units)
end
-- Spawn group.
local group=_DATABASE:Spawn(template) --Wrapper.Group#GROUP
@@ -6139,9 +6151,6 @@ function WAREHOUSE:_SpawnAssetAircraft(alias, asset, request, parking, uncontrol
-- Uncontrolled spawning.
template.uncontrolled=uncontrolled
-- Debug info.
self:T2({airtemplate=template})
-- Spawn group.
local group=_DATABASE:Spawn(template) --Wrapper.Group#GROUP
@@ -6893,7 +6902,7 @@ function WAREHOUSE:_CheckConquered()
for _,_unit in pairs(units) do
local unit=_unit --Wrapper.Unit#UNIT
local distance=coord:Get2DDistance(unit:GetCoordinate())
local distance=coord:Get2DDistance(unit:GetCoord())
-- Filter only alive groud units. Also check distance again, because the scan routine might give some larger distances.
if unit:IsGround() and unit:IsAlive() and distance <= radius then
@@ -8587,6 +8596,8 @@ function WAREHOUSE:_DeleteStockItem(stockitem)
local item=self.stock[i] --#WAREHOUSE.Assetitem
if item.uid==stockitem.uid then
table.remove(self.stock,i)
-- remove also from warehouse DB
_WAREHOUSEDB.Assets[stockitem.uid]=nil
break
end
end
+7
View File
@@ -20,6 +20,13 @@ _DATABASE:_RegisterCargos()
_DATABASE:_RegisterZones()
_DATABASE:_RegisterAirbases()
--- Function that writes to DCS log file
-- @param #string text Formatted text.
-- @param ... Format passed to string.format().
function printf(text, ...)
env.info(string.format(text, ...))
end
--- Check if os etc is available.
BASE:I("Checking de-sanitization of os, io and lfs:")
local __na = false
+9
View File
@@ -32,6 +32,7 @@ __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/MarkerOps_Base.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/TextAndSound.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/Pathline.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/ClientMenu.lua')
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/Vector.lua')
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Wrapper/Object.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Wrapper/Identifiable.lua' )
@@ -84,6 +85,7 @@ __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/ZoneGoalCargo.lua'
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/Tiresias.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/Stratego.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/ClientWatch.lua')
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/Formation.lua')
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/Airboss.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/RecoveryTanker.lua' )
@@ -186,4 +188,11 @@ __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_Cargo_Dispatcher
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_Capture_Zone.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_Capture_Dispatcher.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Navigation/Beacons.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Navigation/Point.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Navigation/Procedure.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Navigation/FlightPlan.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Navigation/Radios.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Navigation/Towns.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Globals.lua' )
+6 -1
View File
@@ -32,6 +32,7 @@ __Moose.Include( 'Core\\MarkerOps_Base.lua' )
__Moose.Include( 'Core\\TextAndSound.lua' )
__Moose.Include( 'Core\\Condition.lua' )
__Moose.Include( 'Core\\ClientMenu.lua' )
__Moose.Include( 'Core\\Vector.lua' )
__Moose.Include( 'Wrapper\\Object.lua' )
__Moose.Include( 'Wrapper\\Identifiable.lua' )
@@ -115,7 +116,6 @@ __Moose.Include( 'Ops\\Operation.lua' )
__Moose.Include( 'Ops\\FlightControl.lua' )
__Moose.Include( 'Ops\\PlayerRecce.lua' )
__Moose.Include( 'Ops\\EasyGCICAP.lua' )
__Moose.Include( 'Ops\\EasyA2G.lua' )
__Moose.Include( 'AI\\AI_Balancer.lua' )
__Moose.Include( 'AI\\AI_Air.lua' )
@@ -178,4 +178,9 @@ __Moose.Include( 'Tasking\\Task_Cargo_Dispatcher.lua' )
__Moose.Include( 'Tasking\\Task_Capture_Zone.lua' )
__Moose.Include( 'Tasking\\Task_Capture_Dispatcher.lua' )
__Moose.Include( 'Navigation\\Point.lua' )
__Moose.Include( 'Navigation\\Beacons.lua' )
__Moose.Include( 'Navigation\\Radios.lua' )
__Moose.Include( 'Navigation\\Towns.lua' )
__Moose.Include( 'Globals.lua' )
@@ -0,0 +1,434 @@
--- **NAVIGATION** - Beacons of the map/theatre.
--
-- **Main Features:**
--
-- * Access beacons of the map
-- * Find closest beacon
-- * Get frequencies and channels
--
-- ===
--
-- ## Example Missions:
--
-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Navigation%20-%20Beacons).
--
-- ===
--
-- ### Author: **funkyfranky**
--
-- ===
-- @module Navigation.Beacons
-- @image MOOSE.JPG
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- BEACONS class.
-- @type BEACONS
--
-- @field #string ClassName Name of the class.
-- @field #number verbose Verbosity of output.
-- @field #table beacons Beacons.
--
-- @extends Core.Base#BASE
--- *Hope is the beacon that guides lost ships back to the shore.*
--
-- ===
--
-- # The BEACONS Concept
--
-- This class is designed to make information about beacons of a map/theatre easier accessible. The information contains location, type and frequencies of all or specific beacons of the map.
--
-- **Note** that try to avoid hard coding stuff in Moose since DCS is updated frequently and things change. Therefore, the main source of information is either a file `beacons.lua` that can be
-- found in the installation directory of DCS for each map or a table that the user needs to provide.
-- **Note** your `MissionScripting` environment needs to be desanitized to read this data. `Package` als needs to be available.
--
-- # Basic Setup
--
-- A new `BEACONS` object can be created with the @{#BEACONS.NewFromFile}(*beacons_lua_file*) function.
--
-- local beacons=BEACONS:NewFromFile("<DCS_Install_Directory>\Mods\terrains\<Map_Name>\beacons.lua")
-- beacons:MarkerShow()
--
-- This will load the beacons from the `<DCS_Install_Directory>` for the specific map and place markers on the F10 map. This is the first step you should do to ensure that the file
-- you provided is correct and all relevant beacons are present.
--
-- # User Functions
--
-- ## F10 Map Markers
--
-- ## Position
--
-- ## Get Closest Beacon
--
--
-- @field #BEACONS
BEACONS = {
ClassName = "BEACONS",
verbose = 1,
beacons = {},
}
--- Mission capability.
-- @type BEACONS.Beacon
-- @field #function display_name Function that returns the localized name.
-- @field #number type Beacon type.
-- @field #string beaconId Beacon ID.
-- @field #string callsign Call sign.
-- @field #number frequency Frequency in Hz.
-- @field #number channel TACAN, RSBN or PRMG channel depending on type.
-- @field #table position Position table.
-- @field #number direction Direction in degrees.
-- @field #table positionGeo Table with latitude and longitude.
-- @field #table sceneObjects Table with scenery objects, e.g. `{t:393396742}`.
-- @field #number chartOffsetX No idea what this offset is?!
-- @field DCS#Vec3 vec3 Position vector 3D.
-- @field #number markerID ID for the F10 marker.
-- @field #string typeName Name of becon type.
-- @field Wrapper.Scenery#SCENERY scenery The scenery object.
--- BEACONS class version.
-- @field #string version
BEACONS.version="0.1.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: A lot...
-- DONE: TACAN channel from frequency (was already in beacon.lua as channel)
-- DONE: Scenery object
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor(s)
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create a new BEACONS class instance from a given table.
-- @param #BEACONS self
-- @param #table BeaconTable Table with beacon info.
-- @return #BEACONS self
function BEACONS:NewFromTable(BeaconTable)
-- Inherit everything from BASE class.
self=BASE:Inherit(self, BASE:New()) -- #BEACONS
for _,_beacon in pairs(BeaconTable) do
local beacon=_beacon --#BEACONS.Beacon
-- Get 3D vector
beacon.vec3={x=beacon.position[1], y=beacon.position[2], z=beacon.position[3]}
-- Get coordinate
beacon.coordinate=COORDINATE:NewFromVec3(beacon.vec3)
-- Get type name
beacon.typeName=self:_GetTypeName(beacon.type)
-- Find closest scenery object from scan
beacon.scenery=beacon.coordinate:FindClosestScenery(20)
-- Debug stuff for scenery object
if false then
if beacon.scenery then
env.info(string.format("FF Beacon %s %s %s got scenery object %s, %s", beacon.callsign, beacon.beaconId, beacon.typeName, beacon.scenery:GetName(), beacon.scenery:GetTypeName() ))
UTILS.PrintTableToLog(beacon.scenery.SceneryObject)
UTILS.PrintTableToLog(beacon.sceneObjects)
else
env.info(string.format("FF NO scenery object %s %s %s ", beacon.callsign, beacon.beaconId, beacon.typeName))
end
end
-- Add to table
table.insert(self.beacons, beacon)
end
-- Debug output
self:I(string.format("Added %d beacons", #self.beacons))
if self.verbose > 0 then
local text="Beacon types:"
for typeName,typeID in pairs(BEACON.Type) do
local n=self:CountBeacons(typeID)
text=text..string.format("\n%s = %d", typeName, n)
end
self:I(text)
end
return self
end
--- Create a new BEACONS class instance from a given file.
-- @param #BEACONS self
-- @param #string FileName Full path to the file containing the map beacons.
-- @return #BEACONS self
function BEACONS:NewFromFile(FileName)
-- Inherit everything from BASE class.
self=BASE:Inherit(self, BASE:New()) -- #BEACONS
local exists=UTILS.FileExists(FileName)
if exists==false then
self:E(string.format("ERROR: file with beacon info does not exist!"))
return nil
end
-- This will create a global table `beacons`
dofile(FileName)
-- Get beacons from table.
self=self:NewFromTable(beacons)
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- User Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Get 3D position vector of a specific beacon.
-- @param #BEACONS self
-- @param #BEACONS.Beacon beacon The beacon data structure.
-- @return DCS#Vec3 Position vector.
function BEACONS:GetVec3(beacon)
return beacon.vec3
end
--- Get COORDINATE of a specific beacon.
-- @param #BEACONS self
-- @param #BEACONS.Beacon beacon The beacon data structure.
-- @return Core.Point#COORDINATE The coordinate.
function BEACONS:GetCoordinate(beacon)
local coordinate=COORDINATE:NewFromVec3(beacon.vec3)
return coordinate
end
--- Find closest beacon to a given coordinate.
-- @param #BEACONS self
-- @param Core.Point#COORDINATE Coordinate The reference coordinate.
-- @param #number TypeID (Optional) Only search for specific beacon types, *e.g.* `BEACON.Type.TACAN`.
-- @param #number DistMax (Optional) Max search distance in meters.
-- @param #table ExcludeList (Optional) List of beacons to exclude.
-- @return #BEACONS.Beacon The closest beacon.
function BEACONS:GetClosestBeacon(Coordinate, TypeID, DistMax, ExcludeList)
local beacon=nil --#BEACONS.Beacon
local distmin=math.huge
ExcludeList=ExcludeList or {}
for _,_beacon in pairs(self.beacons) do
local bc=_beacon --#BEACONS.Beacon
if (TypeID==nil or TypeID==bc.type) and (not UTILS.IsInTable(ExcludeList, bc, "beaconId")) then
local dist=Coordinate:Get2DDistance(bc.vec3)
if dist<distmin and (DistMax==nil or dist<=DistMax) then
distmin=dist
beacon=bc
end
end
end
return beacon
end
--- Find closest beacons to a given coordinate.
-- @param #BEACONS self
-- @param Core.Point#COORDINATE Coordinate The reference coordinate.
-- @param #number Nmax Max number of beacons. Default 5.
-- @param #number TypeID (Optional) Only search for specific beacon types, *e.g.* `BEACON.Type.TACAN`.
-- @param #number DistMax (Optional) Max search distance in meters.
-- @return #table Table of #BEACONS.Beacon closest beacons.
function BEACONS:GetClosestBeacons(Coordinate, Nmax, TypeID, DistMax)
Nmax=Nmax or 5
local closest={}
for i=1,Nmax do
local beacon=self:GetClosestBeacon(Coordinate, TypeID, DistMax, closest)
if beacon then
table.insert(closest, beacon)
else
break
end
end
return closest
end
--- Get table of all beacons, optionally of a given type.
-- @param #BEACONS self
-- @param #number TypeID (Optional) Only return specific beacon types, *e.g.* `BEACON.Type.TACAN`. Can be handed in as tanle of beacon types.
-- @return #table Table of beacons. Each element is of type #BEACON.Beacon.
function BEACONS:GetBeacons(TypeID)
local beacons={}
local keys = {}
if TypeID~=nil and type(TypeID) ~= "table" then
TypeID = {TypeID}
end
for _,_typeid in pairs(TypeID or {}) do
if _typeid ~= nil then
keys[_typeid] = _typeid
end
end
for _,_beacon in pairs(self.beacons) do
local bc=_beacon --#BEACONS.Beacon
if TypeID==nil or keys[bc.type] ~= nil then
table.insert(beacons, bc)
end
end
return beacons
end
--- Count beacons, optionally of a given type.
-- @param #BEACONS self
-- @param #number TypeID (Optional) Only count specific beacon types, *e.g.* `BEACON.Type.TACAN`.
-- @return #number Number of beacons.
function BEACONS:CountBeacons(TypeID)
local n=0
if TypeID then
for _,_beacon in pairs(self.beacons) do
local bc=_beacon --#BEACONS.Beacon
if TypeID==bc.type then
n=n+1
end
end
else
n=#self.beacons
end
return n
end
--- Add markers for all beacons on the F10 map. Optionally, only a specific beacon or a certain beacon type can be marked.
-- @param #BEACONS self
-- @param #BEACONS.Beacon Beacon (Optional) Only this specifc beacon.
-- @param #number TypeID (Optional) Only show specific beacon types, *e.g.* `BEACON.Type.TACAN`.
-- @return #BEACONS self
function BEACONS:MarkerShow(Beacon, TypeID)
for _,_beacon in pairs(self.beacons) do
local beacon=_beacon --#BEACONS.Beacon
if Beacon==nil or Beacon.beaconId==beacon.beaconId then
if TypeID==nil or beacon.type==TypeID then
local text=self:_GetMarkerText(beacon)
local coord=COORDINATE:NewFromVec3(beacon.vec3)
if beacon.markerID then
UTILS.RemoveMark(beacon.markerID)
end
beacon.markerID=coord:MarkToAll(text)
end
end
end
return self
end
--- Remove markers of all beacons from the F10 map. Optionally, remove only marker of a specific beacon or a certain beacon type.
-- @param #BEACONS self
-- @param #BEACONS.Beacon Beacon (Optional) Only this specifc beacon.
-- @param #number TypeID (Optional) Only show specific beacon types, *e.g.* `BEACON.Type.TACAN`.
-- @return #BEACONS self
function BEACONS:MarkerRemove(Beacon, TypeID)
for _,_beacon in pairs(self.beacons) do
local beacon=_beacon --#BEACONS.Beacon
if Beacon==nil or Beacon.beaconId==beacon.beaconId then
if TypeID==nil or beacon.type==TypeID then
if beacon.markerID then
UTILS.RemoveMark(beacon.markerID)
beacon.markerID=nil
end
end
end
end
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Private Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Get text displayed in the F10 marker.
-- @param #BEACONS self
-- @param #BEACONS.Beacon beacon The beacon data structure.
-- @return #string Marker text.
function BEACONS:_GetMarkerText(beacon)
local frequency, funit=self:_GetFrequency(beacon.frequency)
local direction=beacon.direction~=nil and beacon.direction or -1
local text=string.format("Beacon %s [ID=%s]", tostring(beacon.typeName), tostring(beacon.beaconId))
text=text..string.format("\nCallsign: %s", tostring(beacon.callsign))
if UTILS.IsInTable({BEACON.Type.TACAN, BEACON.Type.RSBN, BEACON.Type.PRMG_GLIDESLOPE, BEACON.Type.PRMG_LOCALIZER}, beacon.type) then
text=text..string.format("\nChannel: %s", tostring(beacon.channel))
end
text=text..string.format("\nFrequency: %.3f %s", frequency, funit)
text=text..string.format("\nDirection: %.1f°", direction)
return text
end
--- Get converted frequency.
-- @param #BEACONS self
-- @param #number freq Frequency in Hz.
-- @return #number Frequency in better unit.
-- @return #string Unit ("Hz", "kHz", "MHz").
function BEACONS:_GetFrequency(freq)
freq=freq or 0
local unit="Hz"
if freq>=1e6 then
freq=freq/1e6
unit="MHz"
elseif freq>=1e3 then
freq=freq/1e3
unit="kHz"
end
return freq, unit
end
--- Get name of beacon type.
-- @param #BEACONS self
-- @param #number typeID Beacon type number.
-- @return #string Type name.
function BEACONS:_GetTypeName(typeID)
if typeID~=nil then
for typeName,_typeID in pairs(BEACON.Type) do
if _typeID==typeID then
return typeName
end
end
end
return "Unknown"
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -0,0 +1,481 @@
--- **NAVIGATION** - Flight Plan.
--
-- **Main Features:**
--
-- * Manage navigation aids
-- * VOR, NDB
--
-- ===
--
-- ## Example Missions:
--
-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Ops%20-%20FlightPlan).
--
-- ===
--
-- ### Author: **funkyfranky**
--
-- ===
-- @module Navigation.FlightPlan
-- @image NAVIGATION_FlightPlan.png
--- FLIGHTPLAN class.
-- @type FLIGHTPLAN
-- @field #string ClassName Name of the class.
-- @field #number verbose Verbosity of output.
-- @field #table fixes Navigation fixes.
-- @field Core.Pathline#PATHLINE pathline Pathline of the plan.
-- @field Wrapper.Airbase#AIRBASE departureAirbase Departure airbase.
-- @field Wrapper.Airbase#AIRBASE destinationAirbase Destination airbase.
-- @field #number altitudeCruiseMin Minimum cruise altitude in feet MSL.
-- @field #number altitudeCruiseMax Maximum cruise altitude in feet MSL.
-- @extends Core.Pathline#PATHLINE
--- *Life is what happens to us while we are making other plans.* -- Allen Saunders
--
-- ===
--
-- # The FLIGHTPLAN Concept
--
-- This class has a great concept!
--
-- # Basic Setup
--
-- A new `FLIGHTPLAN` object can be created with the @{#FLIGHTPLAN.New}() function.
--
-- myFlightplan=FLIGHTPLAN:New("Plan A")
-- myFleet:SetPortZone(ZonePort1stFleet)
-- myFleet:Start()
--
-- A fleet needs a *port zone*, which is set via the @{#FLIGHTPLAN.SetPortZone}(`PortZone`) function. This is the zone where the naval assets are spawned and return to.
--
-- Finally, the fleet needs to be started using the @{#FLIGHTPLAN.Start}() function. If the fleet is not started, it will not process any requests.
--
-- @field #FLIGHTPLAN
FLIGHTPLAN = {
ClassName = "FLIGHTPLAN",
verbose = 0,
fixes = {}
}
--- Type of flightplan.
-- @type FLIGHTPLAN.Type
-- @field #string IFRH Instrument Flying Rules High Altitude.
-- @field #string IFRL Instrument Flying Rules Low Altitude.
-- @field #string VFR Visual Flight Rules.
FLIGHTPLAN.Type={
IFRH = "IFR High",
IFRL = "IFR Low",
VFR = "VFR",
}
--- FLIGHTPLAN class version.
-- @field #string version
FLIGHTPLAN.version="0.0.1"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: How to connect SID, STAR, ENROUTE, TRANSITION, APPROACH. Typical flightplan SID --> ENROUTE --> STAR --> APPROACH
-- TODO: Add approach.
-- DONE: How to handle the FLIGHTGROUP:_LandAtAirBase
-- TODO: Do we always need a holding pattern? https://www.faa.gov/air_traffic/publications/atpubs/aip_html/part2_enr_section_1.5.html#:~:text=If%20no%20holding%20pattern%20is,than%20that%20desired%20by%20ATC.
-- DOEN: Read from MSFS file.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create a new FLIGHTPLAN instance.
-- @param #FLIGHTPLAN self
-- @param #string Name Name of this flight plan.
-- @return #FLIGHTPLAN self
function FLIGHTPLAN:New(Name)
-- Inherit everything from BASE class.
self=BASE:Inherit(self, PATHLINE:New(Name)) -- #FLIGHTPLAN
-- Set alias.
self.alias=tostring(Name)
-- Set some string id for output to DCS.log file.
self.lid=string.format("FLIGHTPLAN %s | ", self.alias)
--self.pathline=PATHLINE:New(Name)
-- Debug info.
self:I(self.lid..string.format("Created FLIGHTPLAN!"))
return self
end
--- Create a new FLIGHTPLAN instance from another FLIGHTPLAN acting as blue print.
-- The newly created flight plan is deep copied from the given one.
-- @param #FLIGHTPLAN self
-- @param #FLIGHTPLAN FlightPlan Blue print of the flight plan to copy.
-- @return #FLIGHTPLAN self
function FLIGHTPLAN:NewFromFlightPlan(FlightPlan)
self=UTILS.DeepCopy(FlightPlan)
return self
end
--- Create a new FLIGHTPLAN instance from a given file.
-- Currently, the file has to be an MSFS 2020 .pln file as, *e.g.*, exported from [Navigraph](https://navigraph.com/).
--
-- **Note** that the flight plan does only cover the departure, enroute and arrival portions but **not the approach** part!
-- @param #FLIGHTPLAN self
-- @param #string FileName Full path to file.
-- @return #FLIGHTPLAN self
function FLIGHTPLAN:NewFromFile(FileName)
if UTILS.FileExists(FileName) then
self=FLIGHTPLAN._ReadFileMSFS(FileName)
else
error(string.format("ERROR: File not found! File name=%s", tostring(FileName)))
end
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- User Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Add navigation fix to the flight plan.
-- @param #FLIGHTPLAN self
-- @param Navigation.Point#NAVPOINT NavFix The nav fix.
-- @return #FLIGHTPLAN self
function FLIGHTPLAN:AddNavFix(NavFix)
table.insert(self.fixes, NavFix)
local point=self:AddPointFromVec3(NavFix.vector:GetVec3(true))
point.navpoint=NavFix
return self
end
--- Set departure airbase.
-- @param #FLIGHTPLAN self
-- @param #string AirbaseName Name of the airbase or AIRBASE object.
-- @return #FLIGHTPLAN self
function FLIGHTPLAN:SetDepartureAirbase(AirbaseName)
self.departureAirbase=AIRBASE:FindByName(AirbaseName)
return self
end
--- Set destination airbase.
-- @param #FLIGHTPLAN self
-- @param #string AirbaseName Name of the airbase or AIRBASE object.
-- @return #FLIGHTPLAN self
function FLIGHTPLAN:SetDestinationAirbase(AirbaseName)
self.destinationAirbase=AIRBASE:FindByName(AirbaseName)
return self
end
--- Set cruise altitude.
-- @param #FLIGHTPLAN self
-- @param #number AltMin Minimum altitude in feet MSL.
-- @param #number AltMax Maximum altitude in feet MSL. Default is `AltMin`.
-- @return #FLIGHTPLAN self
function FLIGHTPLAN:SetCruiseAltitude(AltMin, AltMax)
self.altitudeCruiseMin=AltMin
self.altitudeCruiseMax=AltMax or self.altitudeCruiseMin
return self
end
--- Set cruise speed.
-- @param #FLIGHTPLAN self
-- @param #number SpeedMin Minimum speed in knots.
-- @param #number SpeedMax Maximum speed in knots. Default is `SpeedMin`.
-- @return #FLIGHTPLAN self
function FLIGHTPLAN:SetCruiseSpeed(SpeedMin, SpeedMax)
self.speedCruiseMin=SpeedMin
self.speedCruiseMax=SpeedMax or self.speedCruiseMin
return self
end
--- Get the name of this flight plan.
-- @param #FLIGHTPLAN self
-- @return #string The name.
function FLIGHTPLAN:GetName()
return self.alias
end
--- Get cruise altitude. This returns a random altitude between the set min/max cruise altitudes.
-- @param #FLIGHTPLAN self
-- @return #number Cruise altitude in feet MSL.
function FLIGHTPLAN:GetCruiseAltitude()
local alt=10000
if self.altitudeCruiseMin and self.altitudeCruiseMax then
alt=math.random(self.altitudeCruiseMin, self.altitudeCruiseMax)
elseif self.altitudeCruiseMin then
alt=self.altitudeCruiseMin
elseif self.altitudeCruiseMax then
alt=self.altitudeCruiseMax
end
return alt
end
--- Get cruise speed. This returns a random speed between the set min/max cruise speeds.
-- @param #FLIGHTPLAN self
-- @return #number Cruise speed in knots.
function FLIGHTPLAN:GetCruiseSpeed()
local speed=250
if self.speedCruiseMin and self.speedCruiseMax then
speed=math.random(self.speedCruiseMin, self.speedCruiseMax)
elseif self.speedCruiseMin then
speed=self.speedCruiseMin
elseif self.altitudeCruiseMax then
speed=self.speedCruiseMax
end
return speed
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Private Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Read flight plan from a given MSFS 2020 .plt file.
-- @param #string FileName Name of the file.
-- @return #FLIGHTPLAN The flight plan.
function FLIGHTPLAN._ReadFileMSFS(FileName)
local function readfile(filename)
local lines = {}
-- Open file in read binary mode.
local file=assert(io.open(filename, "rb"), string.format("File not found! File name = %s", tostring(filename)))
for line in file:lines() do
lines[#lines+1] = line
end
-- Close file.
file:close()
-- Return data
return lines
end
--- This function returns an XML element, i.e. the string between <...> and </...>.
local function getXMLelement(line)
local element=string.match(line, ">(.+)<")
return element
end
--- This function returns Latitude and Longitude
local function getLatLong(line)
local latlong=getXMLelement(line)
-- The format is "N41° 38' 20.00",E41° 33' 19.00",+000000.00" so we still need to process that.
local lat,long=string.match(latlong, "(.+),(.+),")
return lat,long
end
-- Read data from file.
local data=readfile(FileName)
local flightplan={}
local waypoints={}
local wp=nil
local gotwaypoint=false
for i,line in pairs(data) do
--print(line)
-- Title
if string.find(line, "<Title>") then
flightplan.title=getXMLelement(line)
end
-- Departure ICAO
if string.find(line, "<DepartureID>") then
flightplan.departureICAO=getXMLelement(line)
end
-- Destination ICAO
if string.find(line, "<DestinationID>") then
flightplan.destinationICAO=getXMLelement(line)
end
-- FPType
if string.find(line, "<FPType>") then
flightplan.plantype=getXMLelement(line)
end
-- Route type
if string.find(line, "<RouteType>") then
flightplan.routetype=getXMLelement(line)
end
-- Cruise alt in feet
if string.find(line, "<CruisingAlt>") then
flightplan.altCruise=getXMLelement(line)
end
-- Departure LLA
if string.find(line, "<DepartureLLA>") then
local lat,long=getLatLong(line)
end
-- Destination LLA
if string.find(line, "<DestinationLLA>") then
local lat,long=getLatLong(line)
end
-- Departure Name
if string.find(line, "<DepartureName>") then
local DepartureName=getXMLelement(line)
end
-- DestinationName
if string.find(line, "<DestinationName>") then
local DestinationName=getXMLelement(line)
end
---
-- Waypoint stuff
---
-- New waypoint starts.
if string.find(line, "ATCWaypoint id") then
--Get string inside quotes " and ".
local wpid=string.match(line, [["(.+)"]])
-- Create a new wp table.
wp={}
-- Set waypoint name.
wp.name=wpid
end
-- Waypoint info ends.
if string.find(line, "</ATCWaypoint>") then
-- This is the end of the waypoint.
-- Add info to waypoints table.
table.insert(waypoints, wp)
-- Set waypoint to nil. We create an empty table if the next wp starts.
wp=nil
end
-- Waypoint type (Airport, Intersection, NDB, VORTAC)
if string.find(line, "<ATCWaypointType>") then
local wptype=getXMLelement(line)
wp.type=wptype
end
-- Waypoint position.
if string.find(line, "<WorldPosition>") then
wp.lat, wp.long=getLatLong(line)
end
-- Runway should exist for initial and final WP if it is an airport.
if string.find(line, "RunwayNumberFP") then
wp.runway=getXMLelement(line)
end
-- Runway designator: LEFT, RIGHT, CENTER
if string.find(line, "RunwayDesignatorFP") then
wp.runwayDesignator=getXMLelement(line)
end
-- Segment is Departure
if string.find(line, "<DepartureFP>") then
wp.segment="Departure"
end
-- Segment is Arrival
if string.find(line, "<ArrivalFP>") then
wp.segment="Arrival"
end
-- Segment is Enroute
if string.find(line, "<ATCAirway>") then
wp.segment="Enroute"
end
-- Approach type: VORDME, LOCALIZER
if string.find(line, "ApproachTypeFP") then
flightplan.approachtype=getXMLelement(line)
end
-- Approach type suffic: Z
if string.find(line, "SuffixFP") then
local SuffixFP=getXMLelement(line)
end
end
for key, value in pairs(flightplan) do
env.info(string.format("Flightplan %s=%s", key, tostring(value)))
end
env.info(string.format("Number of waypoints=%d", #waypoints))
for i,wp in pairs(waypoints) do
env.info(string.format("Waypoint name=%s type=%s segment=%s runway=%s lat=%s long=%s", wp.name, wp.type, tostring(wp.segment), tostring(wp.runway)..tostring(wp.runwayDesignator or ""), wp.lat, wp.long))
end
-- Create a new flightplan.
local fp=FLIGHTPLAN:New(flightplan.title)
-- Set cruise altitude.
fp:SetCruiseAltitude(flightplan.altCruise)
-- Set departure and destination airports.
fp:SetDepartureAirbase(flightplan.departureICAO)
fp:SetDestinationAirbase(flightplan.destinationICAO)
--TODO: Remove first and last waypoint if they are identical to the departure/destination airport!
for i,wp in pairs(waypoints) do
-- Create a navpoint.
local navpoint=NAVPOINT:NewFromLLDMS(wp.name, wp.type, wp.lat, wp.long)
navpoint:SetAltMin(flightplan.altCruise)
-- Add point to flightplan.
-- TODO: section departure, enroute, arrival.
fp:AddNavFix(navpoint)
end
return fp
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -0,0 +1,602 @@
--- **NAVIGATION** - Navigation Airspace Points, Fixes and Aids.
--
-- **Main Features:**
--
-- * Navigation Fixes
-- * Navigation Aids
--
-- ===
--
-- ## Example Missions:
--
-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Navigation%20-%20NavFix).
--
-- ===
--
-- ### Author: **funkyfranky**
--
-- ===
-- @module Navigation.Point
-- @image MOOSE.JPG
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- NAVFIX class.
-- @type NAVFIX
--
-- @field #string ClassName Name of the class.
-- @field #number verbose Verbosity of output.
-- @field #string name Name of the point.
-- @field #string typePoint Type of the point, *e.g. "Intersection", "VOR", "Airport".
-- @field Core.Vector#VECTOR vector Position vector of the fix.
-- @field Wrapper.Marker#MARKER marker Marker on F10 map.
-- @field #number altMin Minimum altitude in meters.
-- @field #number altMax Maximum altitude in meters.
-- @field #number speedMin Minimum speed in knots.
-- @field #number speedMax Maximum speed in knots.
--
-- @field #boolean isCompulsory Is this a compulsory fix.
-- @field #boolean isFlyover Is this a flyover fix (`true`) or turning point otherwise.
-- @field #boolean isFAF Is this a final approach fix.
-- @field #boolean isIAF Is this an initial approach fix.
-- @field #boolean isIF Is this an initial fix.
-- @field #boolean isMAF Is this an initial fix.
--
-- @extends Core.Base#BASE
--- *A fleet of British ships at war are the best negotiators.* -- Horatio Nelson
--
-- ===
--
-- # The NAVFIX Concept
--
-- The NAVFIX class has a great concept!
--
-- A NAVFIX describes a geo position and can, *e.g.*, be part of a FLIGHTPLAN. It has a unique name and is of a certain type, *e.g.* "Intersection", "VOR", "Airbase" etc.
-- It can also have further properties as min/max altitudes and speeds that aircraft need to obey when they pass the point.
--
-- # Basic Setup
--
-- A new `NAVFIX` object can be created with the @{#NAVFIX.New}() function.
--
-- myNavPoint=NAVFIX:New()
-- myTemplate:SetXYZ(X, Y, Z)
--
-- This is how it works.
--
-- @field #NAVFIX
NAVFIX = {
ClassName = "NAVFIX",
verbose = 0,
}
--- Type of point.
-- @type NAVFIX.Type
-- @field #string POINT Waypoint.
-- @field #string INTERSECTION Intersection of airway.
-- @field #string AIRPORT Airport.
-- @field #string VOR Very High Frequency Omnidirectional Range Station.
-- @field #string DME Distance Measuring Equipment.
-- @field #string NDB Non-Directional Beacon.
-- @field #string VORDME Combined VHF omnidirectional range (VOR) with a distance-measuring equipment (DME).
-- @field #string LOC Localizer.
-- @field #string ILS Instrument Landing System.
-- @field #string TACAN TACtical Air Navigation System (TACAN).
NAVFIX.Type={
POINT="Point",
INTERSECTION="Intersection",
AIRPORT="Airport",
NDB="NDB",
VOR="VOR",
DME="DME",
VORDME="VOR/DME",
LOC="Localizer",
ILS="ILS",
TACAN="TACAN"
}
--- NAVFIX class version.
-- @field #string version
NAVFIX.version="0.1.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: A lot...
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor(s)
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create a new NAVFIX class instance from a given VECTOR.
-- @param #NAVFIX self
-- @param #string Name Name/ident of the point. Should be unique!
-- @param #string Type Type of the point. Default `NAVFIX.Type.POINT`.
-- @param Core.Vector#VECTOR Vector Position vector of the navpoint.
-- @return #NAVFIX self
function NAVFIX:NewFromVector(Name, Type, Vector)
-- Inherit everything from BASE class.
self=BASE:Inherit(self, BASE:New()) -- #NAVFIX
-- Vector of point.
self.vector=Vector
-- Name of point.
self.name=Name
-- Type of the point.
self.typePoint=Type or NAVFIX.Type.POINT
local coord=COORDINATE:NewFromVec3(self.vector)
-- Marker on F10.
self.marker=MARKER:New(coord, self:_GetMarkerText())
-- Log ID string.
self.lid=string.format("NAVFIX %s [%s] | ", tostring(self.name), tostring(self.typePoint))
-- Debug info.
self:I(self.lid..string.format("Created NAVFIX"))
return self
end
--- Create a new NAVFIX class instance from a given COORDINATE.
-- @param #NAVFIX self
-- @param #string Name Name of the fix. Should be unique!
-- @param #string Type Type of the point. Default `NAVFIX.Type.POINT`.
-- @param Core.Point#COORDINATE Coordinate Coordinate of the point.
-- @return #NAVFIX self
function NAVFIX:NewFromCoordinate(Name, Type, Coordinate)
-- Create a VECTOR from the coordinate.
local Vector=VECTOR:NewFromVec(Coordinate)
-- Create NAVFIX.
self=NAVFIX:NewFromVector(Name, Type, Vector)
return self
end
--- Create a new NAVFIX instance from given latitude and longitude in degrees, minutes and seconds (DMS).
-- @param #NAVFIX self
-- @param #string Name Name of the fix. Should be unique!
-- @param #string Type Type of the point. Default `NAVFIX.Type.POINT`.
-- @param #string Latitude Latitude in DMS as string.
-- @param #string Longitude Longitude in DMS as string.
-- @return #NAVFIX self
function NAVFIX:NewFromLLDMS(Name, Type, Latitude, Longitude)
-- Create a VECTOR from the coordinate.
local Vector=VECTOR:NewFromLLDMS(Latitude, Longitude)
-- Create NAVFIX.
self=NAVFIX:NewFromVector(Name, Type, Vector)
return self
end
--- Create a new NAVFIX instance from given latitude and longitude in decimal degrees (DD).
-- @param #NAVFIX self
-- @param #string Name Name of the fix. Should be unique!
-- @param #string Type Type of the point. Default `NAVFIX.Type.POINT`.
-- @param #number Latitude Latitude in DD.
-- @param #number Longitude Longitude in DD.
-- @return #NAVFIX self
function NAVFIX:NewFromLLDD(Name, Type, Latitude, Longitude)
-- Create a VECTOR from the coordinate.
local Vector=VECTOR:NewFromLLDD(Latitude, Longitude)
-- Create NAVFIX.
self=NAVFIX:NewFromVector(Name, Type, Vector)
return self
end
--- Create a new NAVFIX class instance relative to a given other NAVFIX.
-- You have to specify the distance and bearing from the new point to the given point. *E.g.*, for a distance of 5 NM and a bearing of 090° (West), the
-- new nav point is created 5 NM East of the given nav point. The reason is that this corresponts to convention used in most maps.
-- You can, however, use the `Reciprocal` switch to create the new point in the direction you specify.
-- @param #NAVFIX self
-- @param #string Name Name of the fix. Should be unique!
-- @param #string Type Type of navfix.
-- @param #NAVFIX NavFix The given/existing navigation fix relative to which the new fix is created.
-- @param #number Distance Distance from the given to the new point in nautical miles.
-- @param #number Bearing Bearing [Deg] from the new point to the given one.
-- @param #boolean Reciprocal If `true` the reciprocal `Bearing` is taken so it specifies the direction from the given point to the new one.
-- @return #NAVFIX self
function NAVFIX:NewFromNavFix(Name, Type, NavFix, Distance, Bearing, Reciprocal)
-- Convert magnetic to true bearing by adding magnetic declination, e.g. mag. bearing 10°M ==> true bearing 16°M (for 6° variation on Caucasus map)
Bearing=Bearing+UTILS.GetMagneticDeclination()
if Reciprocal then
Bearing=Bearing-180
end
-- Translate.
local Vector=NavFix.vector:Translate(UTILS.NMToMeters(Distance), Bearing, true)
self=NAVFIX:NewFromVector(Name, Type, Vector)
return self
end
--- Create a new NAVFIX class instance from BEACONS.Beacon data.
-- @param #NAVFIX self
-- @param Navigation.Beacons#BEACONS.Beacon Beacon The beacon data.
-- @return #NAVFIX self
function NAVFIX:NewFromBeacon(Beacon)
local frequency, unit = BEACONS:_GetFrequency(Beacon.frequency)
frequency = string.format("%.3f",frequency)
if Beacon.typeName == "TACAN" then
frequency = Beacon.channel
unit = "X"
end
self = NAVFIX:NewFromVector(string.format("%s %s %s",Beacon.typeName,frequency,unit),Beacon.typeName,Beacon.vec3)
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- User Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Set whether this is the intermediate fix (IF).
-- @param #NAVFIX self
-- @return #NAVFIX self
function NAVFIX:SetIntermediateFix(IntermediateFix)
self.isIF=IntermediateFix
return self
end
--- Set whether this is an initial approach fix (IAF).
-- The IAF is the point where the initial approach segment of an instrument approach begins.
-- It is usually a designated intersection, VHF omidirectional range (VOR) non-directional beacon (NDB)
-- or distance measuring equipment (DME) fix.
-- The IAF may be collocated with the intermediate fix (IF) of the instrument apprach an in such case they designate the
-- beginning of the intermediate segment of the approach. When the IAF and the IF are combined, there is no inital approach segment.
-- @param #NAVFIX self
-- @param #boolean IntermediateFix If `true`, this is an intermediate fix.
-- @return #NAVFIX self
function NAVFIX:SetInitialApproachFix(IntermediateFix)
self.isIAF=IntermediateFix
return self
end
--- Set whether this is the final approach fix (FAF).
-- @param #NAVFIX self
-- @param #boolean FinalApproachFix If `true`, this is a final approach fix.
-- @return #NAVFIX self
function NAVFIX:SetFinalApproachFix(FinalApproachFix)
self.isFAF=FinalApproachFix
return self
end
--- Set whether this is the final approach fix (FAF).
-- @param #NAVFIX self
-- @param #boolean FinalApproachFix If `true`, this is a final approach fix.
-- @return #NAVFIX self
function NAVFIX:SetMissedApproachFix(MissedApproachFix)
self.isMAF=MissedApproachFix
return self
end
--- Set minimum altitude.
-- @param #NAVFIX self
-- @param #number Altitude Min altitude in feet.
-- @return #NAVFIX self
function NAVFIX:SetAltMin(Altitude)
self.altMin=Altitude
return self
end
--- Set maximum altitude.
-- @param #NAVFIX self
-- @param #number Altitude Max altitude in feet.
-- @return #NAVFIX self
function NAVFIX:SetAltMax(Altitude)
self.altMax=Altitude
return self
end
--- Set mandatory altitude (min alt = max alt).
-- @param #NAVFIX self
-- @param #number Altitude Altitude in feet.
-- @return #NAVFIX self
function NAVFIX:SetAltMandatory(Altitude)
self.altMin=Altitude
self.altMax=Altitude
return self
end
--- Set minimum allowed speed at this fix.
-- @param #NAVFIX self
-- @param #number Speed Min speed in knots.
-- @return #NAVFIX self
function NAVFIX:SetSpeedMin(Speed)
self.speedMin=Speed
return self
end
--- Set maximum allowed speed at this fix.
-- @param #NAVFIX self
-- @param #number Speed Max speed in knots.
-- @return #NAVFIX self
function NAVFIX:SetSpeedMax(Speed)
self.speedMax=Speed
return self
end
--- Set mandatory speed (min speed = max speed) at this fix.
-- @param #NAVFIX self
-- @param #number Speed Mandatory speed in knots.
-- @return #NAVFIX self
function NAVFIX:SetSpeedMandatory(Speed)
self.speedMin=Speed
self.speedMax=Speed
return self
end
--- Set whether this fix is compulsory.
-- @param #NAVFIX self
-- @param #boolean Compulsory If `true`, this is a compulsory fix. If `false` or nil, it is non-compulsory.
-- @return #NAVFIX self
function NAVFIX:SetCompulsory(Compulsory)
self.isCompulsory=Compulsory
return self
end
--- Set whether this is a fly-over fix fix.
-- @param #NAVFIX self
-- @param #boolean FlyOver If `true`, this is a fly over fix. If `false` or nil, it is not.
-- @return #NAVFIX self
function NAVFIX:SetFlyOver(FlyOver)
self.isFlyover=FlyOver
return self
end
--- Get the altitude in feet MSL. If min and max altitudes are set, it will return a random altitude between min and max.
-- @param #NAVFIX self
-- @return #number Altitude in feet MSL. Can be `nil`, if neither min nor max altitudes have beeen set.
function NAVFIX:GetAltitude()
local alt=nil
if self.altMin and self.altMax and self.altMin~=self.altMax then
alt=math.random(self.altMin, self.altMax)
elseif self.altMin then
alt=self.altMin
elseif self.altMax then
alt=self.altMax
end
return alt
end
--- Get the speed. If min and max speeds are set, it will return a random speed between min and max.
-- @param #NAVFIX self
-- @return #number Speed in knots. Can be `nil`, if neither min nor max speeds have beeen set.
function NAVFIX:GetSpeed()
local speed=nil
if self.speedMin and self.speedMax and self.speedMin~=self.speedMax then
speed=math.random(self.speedMin, self.speedMax)
elseif self.speedMin then
speed=self.speedMin
elseif self.speedMax then
speed=self.speedMax
end
return speed
end
--- Add marker the NAVFIX on the F10 map.
-- @param #NAVFIX self
-- @return #NAVFIX self
function NAVFIX:MarkerShow()
self.marker:ToAll()
return self
end
--- Remove marker of the NAVFIX from the F10 map.
-- @param #NAVFIX self
-- @return #NAVFIX self
function NAVFIX:MarkerRemove()
self.marker:Remove()
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Private Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Get text displayed in the F10 marker.
-- @param #NAVFIX self
-- @return #string Marker text.
function NAVFIX:_GetMarkerText()
local altmin=self.altMin and tostring(self.altMin) or ""
local altmax=self.altMax and tostring(self.altMax) or ""
local speedmin=self.speedMin and tostring(self.speedMin) or ""
local speedmax=self.speedMax and tostring(self.speedMax) or ""
local text=string.format("NAVFIX %s", self.name)
if self.isIAF then
text=text..string.format(" (IAF)")
end
if self.isIF then
text=text..string.format(" (IF)")
end
text=text..string.format("\nAltitude [ft]: %s - %s", altmin, altmax)
text=text..string.format("\nSpeed [knots]: %s - %s", speedmin, speedmax)
text=text..string.format("\nCompulsory: %s", tostring(self.isCompulsory))
text=text..string.format("\nFly Over: %s", tostring(self.isFlyover))
return text
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- NAVAID class.
-- @type NAVAID
-- @field #string ClassName Name of the class.
-- @field #number verbose Verbosity of output.
-- @extends Navigation.Point#NAVFIX
--- *A fleet of British ships at war are the best negotiators.* -- Horatio Nelson
--
-- ===
--
-- # The NAVAID Concept
--
-- A NAVAID consists of one or multiple FLOTILLAs. These flotillas "live" in a WAREHOUSE that has a phyiscal struction (STATIC or UNIT) and can be captured or destroyed.
--
-- # Basic Setup
--
-- A new `NAVAID` object can be created with the @{#NAVAID.New}(`WarehouseName`, `FleetName`) function, where `WarehouseName` is the name of the static or unit object hosting the fleet
-- and `FleetName` is the name you want to give the fleet. This must be *unique*!
--
-- myFleet=NAVAID:New("myWarehouseName", "1st Fleet")
-- myFleet:SetPortZone(ZonePort1stFleet)
-- myFleet:Start()
--
-- A fleet needs a *port zone*, which is set via the @{#NAVAID.SetPortZone}(`PortZone`) function. This is the zone where the naval assets are spawned and return to.
--
-- Finally, the fleet needs to be started using the @{#NAVAID.Start}() function. If the fleet is not started, it will not process any requests.
--
-- @field #NAVAID
NAVAID = {
ClassName = "NAVAID",
verbose = 0,
}
--- NAVAID class version.
-- @field #string version
NAVAID.version="0.1.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: Add frequencies. Which unit MHz, kHz, Hz?
-- TODO: Add radial function
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create a new NAVAID class instance.
-- @param #NAVAID self
-- @param #string Name Name/ident of this navaid.
-- @param #string Type Type of the point. Default `NAVFIX.Type.POINT`.
-- @param #string ZoneName Name of the zone to scan the scenery.
-- @param #string SceneryName Name of the scenery object.
-- @return #NAVAID self
function NAVAID:NewFromScenery(Name, Type, ZoneName, SceneryName)
-- Get the zone.
local zone=ZONE:FindByName(ZoneName)
-- Get coordinate.
local Coordinate=zone:GetCoordinate()
-- Inherit everything from NAVFIX class.
self=BASE:Inherit(self, NAVFIX:NewFromCoordinate(Name, Type, Coordinate)) -- #NAVAID
-- Set zone.
self.zone=ZONE:FindByName(ZoneName)
-- Try to get the scenery object. Note not all can be found unfortunately.
if SceneryName then
self.scenery=SCENERY:FindByNameInZone(SceneryName, ZoneName)
if not self.scenery then
self:E(string.format("ERROR: Could not find scenery object %s in zone %s", SceneryName, ZoneName))
end
end
-- Alias.
self.alias=string.format("%s %s %s", tostring(ZoneName), tostring(SceneryName), tostring(Type))
-- Set some string id for output to DCS.log file.
self.lid=string.format("NAVAID %s | ", self.alias)
-- Debug info.
self:I(self.lid..string.format("Created NAVAID!"))
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- User Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Set frequency the beacon transmits on.
-- @param #NAVAID self
-- @param #number Frequency Frequency in Hz.
-- @return #NAVAID self
function NAVAID:SetFrequency(Frequency)
self.frequency=Frequency
return self
end
--- Set channel of, *e.g.*, TACAN beacons.
-- @param #NAVAID self
-- @param #number Channel The channel.
-- @param #string Band The band either `"X"` (default) or `"Y"`.
-- @return #NAVAID self
function NAVAID:SetChannel(Channel, Band)
self.channel=Channel
self.band=Band or "X"
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Private Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Add private CLASS functions here.
-- No private NAVAID functions yet.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -0,0 +1,333 @@
--- **NAVIGATION** - Prodedures for Departure (*e.g.* SID), Enroute, Arrival (*e.g.* STAR) and Approach.
--
-- **Main Features:**
--
-- * Stuff
-- * More Stuff
--
-- ===
--
-- ## Example Missions:
--
-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Navigation%20-%20Template).
--
-- ===
--
-- ### Author: **funkyfranky**
--
-- ===
-- @module Navigation.Procedure
-- @image NAVIGATION_Procedure.png
--- APPROACH class.
-- @type APPROACH
-- @field #string ClassName Name of the class.
-- @field #number verbose Verbosity of output.
-- @field #string apptype Approach type (ILS, VOR, LOC).
-- @field Wrapper.Airbase#AIRBASE airbase Airbase of this approach.
-- @field Wrapper.Airbase#AIRBASE.Runway runway Runway of this approach.
-- @field Navigation.Point#NAVAID navaid Primary navigation aid.
-- @field #number wpcounter Running number counting the waypoints to generate its UID.
-- @list <#APPROACH.Waypoint> path Path of approach consisting of waypoints.
-- @extends Core.Base#BASE
--- *A fleet of British ships at war are the best negotiators.* -- Horatio Nelson
--
-- ===
--
-- # The APPROACH Concept
--
-- A typical approach has (up to) four segments. It starts with the initial approach segment, followed by the intermediate approach segment, followed
-- by the final approach segment. In case something goes wrong during the final approach, the missed approach segment kicks in.
--
-- The initial approach segment starts at the initial approach fix (IAF). The segment can contain multiple other fixes, that need to be passed.
-- Note, that an approach procedure can have more than one intitial approach segment and IAF.
--
-- The intermediate approach segment starts at the intermediate fix (IF). The intermediate approach segment blends the initial approach segment into the final approach segment.
-- It is the segment in which aircraft configuration, speed, and positioning adjustments are made for entry into the final approach segment.
--
--
-- https://en.wikipedia.org/wiki/Visual_approach
-- https://en.wikipedia.org/wiki/Instrument_approach
--
-- # Basic Setup
--
-- A new `APPROACH` object can be created with the @{#APPROACH.New}() function.
--
-- myTemplate=APPROACH:New()
-- myTemplate:SetXYZ(X, Y, Z)
--
-- This is how it works.
--
-- @field #APPROACH
APPROACH = {
ClassName = "APPROACH",
verbose = 0,
wpcounter = 0,
}
--- Type of approach.
-- @type APPROACH.Type
-- @field #string VFR Visual Flight Rules.
-- @field #string VOR VOR
-- @field #string NDB NDB
APPROACH.Type={
VFR="VFR",
VOR="VOR",
ILS="ILS",
}
--- Setments of approach.
-- @type APPROACH.Segment
-- @field #string INITIAL Initial approach segment.
-- @field #string INTERMEDIATE Intermediate approach segment.
-- @field #string FINAL Final approach segment.
-- @field #string MISSED Missed approach segment.
APPROACH.Segment={
INITIAL="Initial",
INTERMEDIATE="Intermediate",
FINAL="Final",
MISSED="Missed",
}
--- Waypoint of the approach.
-- @type APPROACH.Waypoint
-- @field #number uid Unique ID of the point.
-- @field #string segment The segment this point belongs to.
-- @field Navigation.Point#NAVFIX navfix The navigation fix that determines the coordinates of this point.
--- APPROACH class version.
-- @field #string version
APPROACH.version="0.0.1"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: A lot...
-- Initial approach segment --> Intermediate approach segment: starts at IF --> Final approach segment
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create a new APPROACH class instance.
-- @param #APPROACH self
-- @param #string Type Type of approach (ILS, VOR, LOC).
-- @param Wrapper.Airbase#AIRBASE Airbase The airbase or name of the airbase.
-- @param Wrapper.Airbase#AIRBASE.Runway Runway The runway or name of the runway.
-- @return #APPROACH self
function APPROACH:New(Type, Airbase, Runway)
-- Inherit everything from BASE class.
self=BASE:Inherit(self, BASE:New()) -- #APPROACH
-- Set approach type.
-- TODO: Check if this is a valid/known approach type.
self.apptype=Type
if type(Airbase)=="string" then
self.airbase=AIRBASE:FindByName(Airbase)
else
self.airbase=Airbase
end
if type(Runway)=="string" then
self.runway=self.airbase:GetRunwayByName(Runway)
else
self.runway=Runway
end
-- Debug info.
self:I("Created new approach for airbase %s: type=%s, runway=%s", self.airbase:GetName(), self.apptype, self.runway.name)
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- User Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Set the primary navigation aid used in the approach.
-- @param #APPROACH self
-- @param Navigation.Point#NAVAID NavAid The NAVAID.
-- @return #APPROACH self
function APPROACH:SetNavAid(NavAid)
self.navaid=NavAid
return self
end
--- Add a waypoint to the path of the approach.
-- @param #APPROACH self
-- @param Navigation.Point#NAVFIX NavFix The navigation fix.
-- @param #string Segment The approach segment this fix belongs to.
-- @return #APPROACH.Waypoint The waypoint data table.
function APPROACH:AddNavFix(NavFix, Segment)
self.wpcounter=self.wpcounter+1
local point={} --#APPROACH.Waypoint
point.uid=self.wpcounter
point.segment=Segment
point.navfix=NavFix
table.insert(self.path, point)
return point
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Private Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Add private functions here.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- DEPARTURE class.
-- @type DEPARTURE
-- @field #string ClassName Name of the class.
-- @field #number verbose Verbosity of output.
-- @field #string apptype DEPARTURE type (ILS, VOR, LOC).
-- @field Wrapper.Airbase#AIRBASE airbase Airbase of this DEPARTURE.
-- @field Wrapper.Airbase#AIRBASE.Runway runway Runway of this DEPARTURE.
-- @field Navigation.Point#NAVAID navaid Primary navigation aid.
-- @field #number wpcounter Running number counting the waypoints to generate its UID.
-- @list <#DEPARTURE.Waypoint> path Path of DEPARTURE consisting of waypoints.
-- @extends Core.Base#BASE
--- *A fleet of British ships at war are the best negotiators.* -- Horatio Nelson
--
-- ===
--
-- # The DEPARTURE Concept
--
-- Bla.
--
-- @field #DEPARTURE
DEPARTURE = {
ClassName = "DEPARTURE",
verbose = 0,
wpcounter = 0,
}
--- Type of DEPARTURE.
-- @type DEPARTURE.Type
-- @field #string VOR VOR
-- @field #string NDB NDB
DEPARTURE.Type={
VOR="VOR",
ILS="ILS",
}
--- Setments of DEPARTURE.
-- @type DEPARTURE.Segment
-- @field #string INITIAL Initial DEPARTURE segment.
-- @field #string INTERMEDIATE Intermediate DEPARTURE segment.
-- @field #string FINAL Final DEPARTURE segment.
-- @field #string MISSED Missed DEPARTURE segment.
DEPARTURE.Segment={
INITIAL="Initial",
INTERMEDIATE="Intermediate",
FINAL="Final",
MISSED="Missed",
}
--- Waypoint of the DEPARTURE.
-- @type DEPARTURE.Waypoint
-- @field #number uid Unique ID of the point.
-- @field #string segment The segment this point belongs to.
-- @field Navigation.Point#NAVFIX navfix The navigation fix that determines the coordinates of this point.
--- DEPARTURE class version.
-- @field #string version
DEPARTURE.version="0.0.1"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: A lot...
-- Initial DEPARTURE segment --> Intermediate DEPARTURE segment: starts at IF --> Final DEPARTURE segment
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create a new DEPARTURE class instance.
-- @param #DEPARTURE self
-- @param Wrapper.Airbase#AIRBASE Airbase The airbase or name of the airbase.
-- @param Wrapper.Airbase#AIRBASE.Runway Runway The runway or name of the runway.
-- @param #string Type Type of DEPARTURE (ILS, VOR, LOC).
-- @return #DEPARTURE self
function DEPARTURE:New(Airbase, Runway)
-- Inherit everything from BASE class.
self=BASE:Inherit(self, BASE:New()) -- #DEPARTURE
if type(Airbase)=="string" then
self.airbase=AIRBASE:FindByName(Airbase)
else
self.airbase=Airbase
end
if type(Runway)=="string" then
self.runway=self.airbase:GetRunwayByName(Runway)
else
self.runway=Runway
end
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- User Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Set the primary navigation aid used in the DEPARTURE.
-- @param #DEPARTURE self
-- @param Navigation.Point#NAVAID NavAid The NAVAID.
-- @return #DEPARTURE self
function DEPARTURE:SetNavAid(NavAid)
self.navaid=NavAid
return self
end
--- Add a waypoint to the path of the DEPARTURE.
-- @param #DEPARTURE self
-- @param Navigation.Point#NAVFIX NavFix The navigation fix.
-- @param #string Segment The DEPARTURE segment this fix belongs to.
-- @return #DEPARTURE.Waypoint The waypoint data.
function DEPARTURE:AddWaypoint(NavFix, Segment)
self.wpcounter=self.wpcounter+1
local point={} --#DEPARTURE.Waypoint
point.uid=self.wpcounter
point.segment=Segment
point.navfix=NavFix
table.insert(self.path, point)
return point
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Private Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Add DEPARTURE private functions here.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -0,0 +1,423 @@
--- **NAVIGATION** - Airbase radios.
--
-- **Main Features:**
--
-- * Get radio frequencies of airbases
-- * Find closest airbase radios
-- * Mark radio frequencies on F10 map
--
-- ===
--
-- ## Example Missions:
--
-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Navigation%20-%20Radios).
--
-- ===
--
-- ### Author: **funkyfranky**
--
-- ===
-- @module Navigation.Radios
-- @image MOOSE.JPG
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- RADIOS class.
-- @type RADIOS
--
-- @field #string ClassName Name of the class.
-- @field #number verbose Verbosity of output.
-- @field #table radios Radios.
--
-- @extends Core.Base#BASE
--- *It's not true I had nothing on, I had the radio on.* -- *Marilyn Monroe*
--
-- ===
--
-- # The RADIOS Concept
--
-- This class is designed to make information about radios of a map/theatre easier accessible. The information contains mostly the frequencies of airbases of the map.
--
-- **Note** that try to avoid hard coding stuff in Moose since DCS is updated frequently and things change. Therefore, the main source of information is either a file `radio.lua` that can be
-- found in the installation directory of DCS for each map or a table that the user needs to provide.
-- **Note** your `MissionScripting` environment needs to be desanitized to read this data.
--
-- # Basic Setup
--
-- A new `RADIOS` object can be created with the @{#RADIOS.NewFromFile}(*radio_lua_file*) function.
--
-- local radios=RADIOS:NewFromFile("<DCS_Install_Directory>\Mods\terrains\<Map_Name>\radio.lua")
-- radios:MarkerShow()
--
-- This will load the radios from the `<DCS_Install_Directory>` for the specific map and place markers on the F10 map. This is the first step you should do to ensure that the file
-- you provided is correct and all relevant radios are present.
--
-- # User Functions
--
-- ## F10 Map Markers
--
-- ## Position
--
-- ## Closest Radio
--
--
-- @field #RADIOS
RADIOS = {
ClassName = "RADIOS",
verbose = 0,
radios = {},
}
--- Radio item data structure.
-- @type RADIOS.Radio
-- @field #string radioId Radio ID.
-- @field #table role Roles of the radio (usually {"ground", "tower", "approach"}).
-- @field #table callsign Callsigns of the radio (usually the airbase name).
-- @field #table frequency Frequencies of the radios.
-- @field #table position Position table.
-- @field #table sceneObjects Scenery objects.
-- @field #string name Name of the airbase.
-- @field Wrapper.Airbase#AIRBASE airbase Airbase.
-- @field Core.Point#COORDINATE coordinate The COORDINATE of the radio.
-- @field DCS#Vec3 vec3 3D vector.
-- @field #number markerID Marker ID.
--- Radio item data structure.
-- @type RADIOS.Frequency
-- @field #number modu Modulation type.
-- @field #number freq Frequency in Hz.
--- RADIOS class version.
-- @field #string version
RADIOS.version="0.1.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: A lot...
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor(s)
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create a new RADIOS class instance from a given table.
-- @param #RADIOS self
-- @param #table RadioTable Table with radios info.
-- @return #RADIOS self
function RADIOS:NewFromTable(RadioTable)
-- Inherit everything from BASE class.
self=BASE:Inherit(self, BASE:New()) -- #RADIOS
--local airbasenames=AIRBASE.GetAllAirbaseNames()
-- Get all airdromes
local airdromes=AIRBASE.GetAllAirbases(nil, Airbase.Category.AIRDROME)
for _,_radio in pairs(RadioTable) do
local radio=_radio --#RADIOS.Radio
-- The table structure of callsign is a bit awkward. We need to get the airbase name.
-- Note that unfortunately, the callsign does not always correspond to the airbase name.
if false then
local cs=radio.callsign[1]
if cs and cs.common then
radio.name=cs.common[1]
elseif cs and cs.nato then
radio.name=cs.nato[1]
else
radio.name="Unknown"
end
radio.name=self:_GetAirbaseName(airbasenames, radio.name)
radio.airbase=AIRBASE:FindByName(radio.name)
end
-- Each radio item has a key radioId = 'airfield106_0', where 106 is the UID of the airbase.
-- So we can use that to get the airbase.
local aid = tonumber(string.match(radio.radioId, "airfield(%d+)_"))
-- Get airbase
radio.airbase=self:_GetAirbaseByID(airdromes, aid)
-- Set other stuff
if radio.airbase then
radio.coordinate=radio.airbase:GetCoordinate()
radio.vec3=radio.airbase:GetVec3()
radio.name=radio.airbase:GetName()
end
-- Add to table
table.insert(self.radios, radio)
end
-- Debug output
self:I(string.format("Added %d radios", #self.radios))
return self
end
--- Create a new RADIOS class instance from a given file.
-- @param #RADIOS self
-- @param #string FileName Full path to the file containing the map radios.
-- @return #RADIOS self
function RADIOS:NewFromFile(FileName)
-- Inherit everything from BASE class.
self=BASE:Inherit(self, BASE:New()) -- #RADIOS
local exists=UTILS.FileExists(FileName)
if exists==false then
self:E(string.format("ERROR: file with radios info does not exist! File=%s", tostring(FileName)))
return nil
end
-- Backup DCS radio table
local radiobak=UTILS.DeepCopy(radio)
-- This will create a global table `radio`
dofile(FileName)
-- Get radios from table.
self=self:NewFromTable(radio)
-- Restore DCS radio table
radio=UTILS.DeepCopy(radiobak)
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- User Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Get 3D position vector of a specific radio.
-- @param #RADIOS self
-- @param #RADIOS.Radio radio The radio data structure.
-- @return DCS#Vec3 Position vector.
function RADIOS:GetVec3(radio)
return radio.vec3
end
--- Get COORDINATE of a specific radio.
-- @param #RADIOS self
-- @param #RADIOS.Radio radio The radio data structure.
-- @return Core.Point#COORDINATE The coordinate.
function RADIOS:GetCoordinate(radio)
return radio.coordinate
end
--- Find closest radio to a given coordinate.
-- @param #RADIOS self
-- @param Core.Point#COORDINATE Coordinate The reference coordinate.
-- @param #number DistMax (Optional) Max search distance in meters.
-- @param #table ExcludeList (Optional) List of radios to exclude.
-- @return #RADIOS.Radio The closest radio.
function RADIOS:GetClosestRadio(Coordinate, DistMax, ExcludeList)
local radio=nil --#RADIOS.Radio
local distmin=math.huge
ExcludeList=ExcludeList or {}
for _,_radio in pairs(self.radios) do
local ra=_radio --#RADIOS.Radio
if (not UTILS.IsInTable(ExcludeList, ra, "radioId")) then
local dist=Coordinate:Get2DDistance(ra.coordinate)
if dist<distmin and (DistMax==nil or dist<=DistMax) then
distmin=dist
radio=ra
end
end
end
return radio
end
--- Find closest radios to a given coordinate.
-- @param #RADIOS self
-- @param Core.Point#COORDINATE Coordinate The reference coordinate.
-- @param #number Nmax Max number of radios. Default 5.
-- @param #number DistMax (Optional) Max search distance in meters.
-- @return #table Table of #RADIOS.Radio closest radios.
function RADIOS:GetClosestRadios(Coordinate, Nmax, DistMax)
Nmax=Nmax or 5
local closest={}
for i=1,Nmax do
local radio=self:GetClosestRadio(Coordinate, DistMax, closest)
if radio then
table.insert(closest, radio)
else
break
end
end
return closest
end
--- Add markers for all radios on the F10 map.
-- @param #RADIOS self
-- @param #RADIOS.Radio Radio (Optional) Only this specifc radio.
-- @return #RADIOS self
function RADIOS:MarkerShow(Radio)
for _,_radio in pairs(self.radios) do
local radio=_radio --#RADIOS.Radio
if Radio==nil or Radio.radioId==radio.radioId then
local coord=self:GetCoordinate(radio)
if coord then
local text=self:_GetMarkerText(radio)
if radio.markerID then
UTILS.RemoveMark(radio.markerID)
end
radio.markerID=coord:MarkToAll(text)
end
end
end
return self
end
--- Remove markers of all radios from the F10 map.
-- @param #RADIOS self
-- @param #RADIOS.Radio Radio (Optional) Only this specifc radio.
-- @return #RADIOS self
function RADIOS:MarkerRemove(Radio)
for _,_radio in pairs(self.radios) do
local radio=_radio --#RADIOS.Radio
if Radio==nil or Radio.radioId==radio.radioId then
if radio.markerID then
UTILS.RemoveMark(radio.markerID)
radio.markerID=nil
end
end
end
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Private Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Get text displayed in the F10 marker.
-- @param #RADIOS self
-- @param #RADIOS.Radio radio The radio data structure.
-- @return #string Marker text.
function RADIOS:_GetMarkerText(radio)
local text=string.format("Radio %s", tostring(radio.name))
for b,f in pairs(radio.frequency) do
local frequency=f --#RADIOS.Frequency
local mod=frequency[1]
local fre=frequency[2]
local freq, funit=self:_GetFrequency(fre)
--UTILS.PrintTableToLog(frequency)
local band=self:_GetBandName(b)
text=text..string.format("\n%s: %.3f %s", band, freq, funit)
end
return text
end
--- Get converted frequency.
-- @param #RADIOS self
-- @param #number freq Frequency in Hz.
-- @return #number Frequency in better unit.
-- @return #string Unit ("Hz", "kHz", "MHz").
function RADIOS:_GetFrequency(freq)
freq=freq or 0
local unit="Hz"
if freq>=1e6 then
freq=freq/1e6
unit="MHz"
elseif freq>=1e3 then
freq=freq/1e3
unit="kHz"
end
return freq, unit
end
--- Get name of frequency band.
-- @param #RADIOS self
-- @param #number BandNumber Band as number.
-- @return #string Band name.
function RADIOS:_GetBandName(BandNumber)
if BandNumber~=nil then
for bandName,bandNumber in pairs(ENUMS.FrequencyBand) do
if bandNumber==BandNumber then
return bandName
end
end
end
return "Unknown"
end
--- Get name of frequency band.
-- @param #RADIOS self
-- @param #table airbasenames Names of all airbases.
-- @param #string name Name of airbase.
-- @return #string Name of airbase
function RADIOS:_GetAirbaseName(airbasenames, name)
local airbase=AIRBASE:FindByName(name)
if airbase then
return name
else
for _,airbasename in pairs(airbasenames) do
if string.find(airbasename, name) then
return airbasename
end
end
end
return "Unknown"
end
--- Get name of frequency band.
-- @param #RADIOS self
-- @param #table airbases Table of airbases.
-- @param #number aid Airbase ID.
-- @return Wrapper.Airbase#AIRBASE Airbase matching the ID or nil.
function RADIOS:_GetAirbaseByID(airbases, aid)
for _,_airbase in pairs(airbases) do
local airbase=_airbase --Wrapper.Airbase#AIRBASE
local id=airbase:GetID(true)
if id==aid then
return airbase
end
end
return nil
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -0,0 +1,108 @@
--- **NAVIGATION** - Template.
--
-- **Main Features:**
--
-- * Stuff
-- * More Stuff
--
-- ===
--
-- ## Example Missions:
--
-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Navigation%20-%20Template).
--
-- ===
--
-- ### Author: **funkyfranky**
--
-- ===
-- @module Navigation.Template
-- @image NAVIGATION_Template.png
--- TEMPLATE class.
-- @type TEMPLATE
-- @field #string ClassName Name of the class.
-- @field #number verbose Verbosity of output.
-- @extends Core.Base#BASE
--- *A fleet of British ships at war are the best negotiators.* -- Horatio Nelson
--
-- ===
--
-- # The TEMPLATE Concept
--
-- The TEMPLATE class has a great concept!
--
-- # Basic Setup
--
-- A new `TEMPLATE` object can be created with the @{#TEMPLATE.New}() function.
--
-- myTemplate=TEMPLATE:New()
-- myTemplate:SetXYZ(X, Y, Z)
--
-- This is how it works.
--
-- @field #TEMPLATE
TEMPLATE = {
ClassName = "TEMPLATE",
verbose = 0,
}
--- Type of navaid
-- @type TEMPLATE.Type
-- @field #string VOR VOR
-- @field #string NDB NDB
TEMPLATE.TYPE={
VOR="VOR",
NDB="NDB",
}
--- TEMPLATE class version.
-- @field #string version
TEMPLATE.version="0.0.1"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: A lot...
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create a new TEMPLATE class instance.
-- @param #TEMPLATE self
-- @return #TEMPLATE self
function TEMPLATE:New()
-- Inherit everything from SCENERY class.
self=BASE:Inherit(self, BASE:New()) -- #TEMPLATE
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- User Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Set frequency.
-- @param #TEMPLATE self
-- @param #number Frequency Frequency in Hz.
-- @return #TEMPLATE self
function TEMPLATE:SetFrequency(Frequency)
self.frequency=Frequency
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Private Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -0,0 +1,332 @@
--- **NAVIGATION** - Towns of the map/theatre.
--
-- **Main Features:**
--
-- * Find towns of map
-- * Road and rail connections
-- * Find closest town to a given coordinate
--
-- ===
--
-- ## Example Missions:
--
-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Navigation%20-%20Towns).
--
-- ===
--
-- ### Author: **funkyfranky**
--
-- ===
-- @module Navigation.Towns
-- @image MOOSE.JPG
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- TOWNS class.
-- @type TOWNS
--
-- @field #string ClassName Name of the class.
-- @field #number verbose Verbosity of output.
-- @field #table towns Towns.
--
-- @extends Core.Base#BASE
--- *Hope is the beacon that guides lost ships back to the shore.*
--
-- ===
--
-- # The TOWNS Concept
--
-- This class is designed to make information about towns of a map/theatre easier accessible. The information contains location and road/rail connections of the towns.
--
-- **Note** that try to avoid hard coding stuff in Moose since DCS is updated frequently and things change. Therefore, the main source of information is either a file `towns.lua` that can be
-- found in the installation directory of DCS for each map or a table that the user needs to provide.
-- **Note** your `MissionScripting` environment needs to be desanitized to read this data.
--
-- # Basic Setup
--
-- A new `TOWNS` object can be created with the @{#TOWNS.NewFromFile}(*towns_lua_file*) function.
--
-- local towns=TOWNS:NewFromFile("<DCS_Install_Directory>\Mods\terrains\<Map_Name>\map\towns.lua")
-- towns:MarkerShow()
--
-- This will load the towns from the `<DCS_Install_Directory>` for the specific map and place markers on the F10 map. This is the first step you should do to ensure that the file
-- you provided is correct and all relevant towns are present.
--
-- # User Functions
--
-- ## F10 Map Markers
--
-- ## Position
--
-- ## Get Closest Town
--
--
-- @field #TOWNS
TOWNS = {
ClassName = "TOWNS",
verbose = 0,
towns = {},
}
--- Town data.
-- @type TOWNS.Town
-- @field #string display_name Displayed name.
-- @field #string name Name of the town.
-- @field #number latitude Latitude.
-- @field #number longitude Longitude
-- @field DCS#Vec3 vec3 Position vector 3D.
-- @field Core.Point#COORDINATE coordinate The coordinate.
-- @field Core.Point#COORDINATE coordRoad The coordinate of the closest road.
-- @field Core.Point#COORDINATE coordRail The coordinate of the closest railway.
-- @field #number markerID ID for the F10 marker.
--- TOWNS class version.
-- @field #string version
TOWNS.version="0.1.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: A lot...
-- DONE: Road connection
-- DONE: Rail connection
-- DONE: Connection between towns
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor(s)
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create a new TOWNS class instance from a given table.
-- @param #TOWNS self
-- @param #table TownTable Table with all towns data.
-- @return #TOWNS self
function TOWNS:NewFromTable(TownTable)
-- Inherit everything from BASE class.
self=BASE:Inherit(self, BASE:New()) -- #TOWNS
for TownName,_town in pairs(TownTable) do
local town=_town --#TOWNS.Town
town.name=TownName
-- Get coordinate
town.coordinate=COORDINATE:NewFromLLDD(town.latitude, town.longitude)
-- Get coordinate of closest road
town.coordRoad=town.coordinate:GetClosestPointToRoad()
-- Get coordinate of closest rail
town.coordRail=town.coordinate:GetClosestPointToRoad(true)
-- Add to table
table.insert(self.towns, town)
end
-- Debug output
self:I(string.format("Added %d towns", #self.towns))
return self
end
--- Create a new TOWNS class instance from a given file.
-- @param #TOWNS self
-- @param #string FileName Full path to the file containing the towns data.
-- @return #TOWNS self
function TOWNS:NewFromFile(FileName)
-- Inherit everything from BASE class.
self=BASE:Inherit(self, BASE:New()) -- #TOWNS
local exists=UTILS.FileExists(FileName)
if exists==false then
self:E(string.format("ERROR: file with towns info does not exist!"))
return nil
end
-- This will create a global table `towns`
dofile(FileName)
-- Get towns from table.
self=self:NewFromTable(towns)
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- User Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Get 3D position vector of a specific town.
-- @param #TOWNS self
-- @param #TOWNS.Town town The town data structure.
-- @return DCS#Vec3 Position vector.
function TOWNS:GetVec3(town)
return town.vec3
end
--- Get COORDINATE of a specific town.
-- @param #TOWNS self
-- @param #TOWNS.Town town The town data structure.
-- @return Core.Point#COORDINATE The coordinate.
function TOWNS:GetCoordinate(town)
return town.coordinate
end
--- Get closest road coordinate of a town.
-- @param #TOWNS self
-- @param #TOWNS.Town town The town data structure.
-- @return Core.Point#COORDINATE The closest road coordinate.
function TOWNS:GetCoordRoad(town)
return town.coordRoad
end
--- Get closest rail coordinate of a town.
-- @param #TOWNS self
-- @param #TOWNS.Town town The town data structure.
-- @return Core.Point#COORDINATE The closest rail coordinate.
function TOWNS:GetCoordRail(town)
return town.coordRail
end
--- Get road or rail connection between two towns.
-- @param #TOWNS self
-- @param #TOWNS.Town townA The town data structure.
-- @param #TOWNS.Town townB The town data structure.
-- @param #boolean Railroad If `true`, find rail road connection
-- @return Core.Pathline#PATHLINE Pathline connecting the two towns on road.
function TOWNS:GetConnectionRoad(townA, townB, Railroad)
local path=townA.coordRoad:GetPathlineOnRoad(townB.coordRoad, false, Railroad)
return path
end
--- Find closest town to a given coordinate.
-- @param #TOWNS self
-- @param Core.Point#COORDINATE Coordinate The reference coordinate.
-- @param #number DistMax (Optional) Max search distance in meters.
-- @param #table ExcludeList (Optional) List of towns excluded from the search.
-- @return #TOWNS.Town The closest town.
function TOWNS:GetClosestTown(Coordinate, DistMax, ExcludeList)
local Town=nil --#TOWNS.Town
local distmin=math.huge
ExcludeList=ExcludeList or {}
for _,_town in pairs(self.towns) do
local town=_town --#TOWNS.Town
if (not UTILS.IsInTable(ExcludeList, town, "name")) then
local dist=Coordinate:Get2DDistance(town.coordinate)
if dist<distmin then
distmin=dist
Town=town
end
end
end
return Town
end
--- Find closest towns to a given coordinate.
-- @param #TOWNS self
-- @param Core.Point#COORDINATE Coordinate The reference coordinate.
-- @param #number Nmax Max number of towns. Default 5.
-- @param #number DistMax (Optional) Max search distance in meters.
-- @return #table Table of #TOWNS.Town closest towns.
function TOWNS:GetClosestTowns(Coordinate, Nmax, DistMax)
Nmax=Nmax or 5
local closest={}
for i=1,Nmax do
local town=self:GetClosestTown(Coordinate, DistMax, closest)
if town then
table.insert(closest, town)
else
break
end
end
return closest
end
--- Get table of all towns, optionally of a given type.
-- @param #TOWNS self
-- @return #table Table of towns. Each element is of type #TOWN.Town.
function TOWNS:GetTowns()
return self.towns
end
--- Add markers for all towns on the F10 map.
-- @param #TOWNS self
-- @param #TOWNS.Town Town (Optional) Only this specifc town.
-- @return #TOWNS self
function TOWNS:MarkerShow(Town)
for _,_town in pairs(self.towns) do
local town=_town --#TOWNS.Town
if Town==nil or Town.name==town.name then
local text=self:_GetMarkerText(town)
local coord=town.coordinate
if town.markerID then
UTILS.RemoveMark(town.markerID)
end
town.markerID=coord:MarkToAll(text)
end
end
return self
end
--- Remove markers of all towns from the F10 map.
-- @param #TOWNS self
-- @param #TOWNS.Town Town (Optional) Only this specifc town.
-- @return #TOWNS self
function TOWNS:MarkerRemove(Town)
for _,_town in pairs(self.towns) do
local town=_town --#TOWNS.Town
if Town==nil or Town.name==town.name then
if town.markerID then
UTILS.RemoveMark(town.markerID)
town.markerID=nil
end
end
end
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Private Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Get text displayed in the F10 marker.
-- @param #TOWNS self
-- @param #TOWNS.Town town The town data structure.
-- @return #string Marker text.
function TOWNS:_GetMarkerText(town)
local text=string.format("Town %s", town.name)
--text=text..string.format("\nCallsign: %s", tostring(beacon.callsign))
return text
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+93 -21
View File
@@ -159,6 +159,8 @@ AIRWING = {
-- @field #number refuelsystem Refueling system type: `0=Unit.RefuelingSystem.BOOM_AND_RECEPTACLE`, `1=Unit.RefuelingSystem.PROBE_AND_DROGUE`.
-- @field #number noccupied Number of flights on this patrol point.
-- @field Wrapper.Marker#MARKER marker F10 marker.
-- @field #boolean IsZonePoint flag for using a (moving) zone as point for patrol etc.
-- @field Core.Zone#ZONE_BASE patrolzone in case Patrol coordinate was handed as zone, store here.
--- Patrol zone.
-- @type AIRWING.PatrolZone
@@ -187,13 +189,14 @@ AIRWING = {
--- AIRWING class version.
-- @field #string version
AIRWING.version="0.9.6"
AIRWING.version="0.9.7"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: Check that airbase has enough parking spots if a request is BIG.
-- DONE: Allow (moving) zones as base for patrol points.
-- DONE: Spawn in air ==> Needs WAREHOUSE update.
-- DONE: Spawn hot.
-- DONE: Make special request to transfer squadrons to anther airwing (or warehouse).
@@ -807,13 +810,22 @@ function AIRWING:_PatrolPointMarkerText(point)
end
--- Update marker of the patrol point.
-- @param #AIRWING self
-- @param #AIRWING.PatrolData point Patrol point table.
function AIRWING:UpdatePatrolPointMarker(point)
if self.markpoints then -- sometimes there's a direct call from #OPSGROUP
if self and self.markpoints then -- sometimes there's a direct call from #OPSGROUP
local text=string.format("%s Occupied=%d\nheading=%03d, leg=%d NM, alt=%d ft, speed=%d kts",
point.type, point.noccupied, point.heading, point.leg, point.altitude, point.speed)
point.marker:UpdateText(text, 1)
if point.IsZonePoint and point.IsZonePoint == true and point.patrolzone then
-- update position
local Coordinate = point.patrolzone:GetCoordinate()
point.marker:UpdateCoordinate(Coordinate)
point.marker:UpdateText(text, 1.5)
else
point.marker:UpdateText(text, 1)
end
end
end
@@ -821,7 +833,7 @@ end
--- Create a new generic patrol point.
-- @param #AIRWING self
-- @param #string Type Patrol point type, e.g. "CAP" or "AWACS". Default "Unknown".
-- @param Core.Point#COORDINATE Coordinate Coordinate of the patrol point. Default 10-15 NM away from the location of the airwing.
-- @param Core.Point#COORDINATE Coordinate Coordinate of the patrol point. Default 10-15 NM away from the location of the airwing. Can be handed as a Core.Zone#ZONE object (e.g. in case you want the point to align with a moving zone).
-- @param #number Altitude Orbit altitude in feet. Default random between Angels 10 and 20.
-- @param #number Heading Heading in degrees. Default random (0, 360] degrees.
-- @param #number LegLength Length of race-track orbit in NM. Default 15 NM.
@@ -830,14 +842,16 @@ end
-- @return #AIRWING.PatrolData Patrol point table.
function AIRWING:NewPatrolPoint(Type, Coordinate, Altitude, Speed, Heading, LegLength, RefuelSystem)
-- Check if a zone was passed instead of a coordinate.
if Coordinate and Coordinate:IsInstanceOf("ZONE_BASE") then
Coordinate=Coordinate:GetCoordinate()
end
local patrolpoint={} --#AIRWING.PatrolData
patrolpoint.type=Type or "Unknown"
patrolpoint.coord=Coordinate or self:GetCoordinate():Translate(UTILS.NMToMeters(math.random(10, 15)), math.random(360))
if Coordinate and Coordinate:IsInstanceOf("ZONE_BASE") then
patrolpoint.IsZonePoint = true
patrolpoint.patrolzone = Coordinate
patrolpoint.coord = patrolpoint.patrolzone:GetCoordinate()
else
patrolpoint.IsZonePoint = false
end
patrolpoint.heading=Heading or math.random(360)
patrolpoint.leg=LegLength or 15
patrolpoint.altitude=Altitude or math.random(10,20)*1000
@@ -847,7 +861,7 @@ function AIRWING:NewPatrolPoint(Type, Coordinate, Altitude, Speed, Heading, LegL
if self.markpoints then
patrolpoint.marker=MARKER:New(Coordinate, "New Patrol Point"):ToAll()
AIRWING.UpdatePatrolPointMarker(patrolpoint)
self:UpdatePatrolPointMarker(patrolpoint)
end
return patrolpoint
@@ -855,7 +869,7 @@ end
--- Add a patrol Point for CAP missions.
-- @param #AIRWING self
-- @param Core.Point#COORDINATE Coordinate Coordinate of the patrol point.
-- @param Core.Point#COORDINATE Coordinate Coordinate of the patrol point. Can be handed as a Core.Zone#ZONE object (e.g. in case you want the point to align with a moving zone).
-- @param #number Altitude Orbit altitude in feet.
-- @param #number Speed Orbit speed in knots.
-- @param #number Heading Heading in degrees.
@@ -872,7 +886,7 @@ end
--- Add a patrol Point for RECON missions.
-- @param #AIRWING self
-- @param Core.Point#COORDINATE Coordinate Coordinate of the patrol point.
-- @param Core.Point#COORDINATE Coordinate Coordinate of the patrol point. Can be handed as a Core.Zone#ZONE object (e.g. in case you want the point to align with a moving zone).
-- @param #number Altitude Orbit altitude in feet.
-- @param #number Speed Orbit speed in knots.
-- @param #number Heading Heading in degrees.
@@ -889,7 +903,7 @@ end
--- Add a patrol Point for TANKER missions.
-- @param #AIRWING self
-- @param Core.Point#COORDINATE Coordinate Coordinate of the patrol point.
-- @param Core.Point#COORDINATE Coordinate Coordinate of the patrol point. Can be handed as a Core.Zone#ZONE object (e.g. in case you want the point to align with a moving zone).
-- @param #number Altitude Orbit altitude in feet.
-- @param #number Speed Orbit speed in knots.
-- @param #number Heading Heading in degrees.
@@ -907,7 +921,7 @@ end
--- Add a patrol Point for AWACS missions.
-- @param #AIRWING self
-- @param Core.Point#COORDINATE Coordinate Coordinate of the patrol point.
-- @param Core.Point#COORDINATE Coordinate Coordinate of the patrol point. Can be handed as a Core.Zone#ZONE object (e.g. in case you want the point to align with a moving zone).
-- @param #number Altitude Orbit altitude in feet.
-- @param #number Speed Orbit speed in knots.
-- @param #number Heading Heading in degrees.
@@ -974,6 +988,46 @@ function AIRWING:SetTakeoffAir()
return self
end
--- Set the aircraft of the AirWing to land straight in.
-- @param #AIRWING self
-- @return #FLIGHTGROUP self
function AIRWING:SetLandingStraightIn()
self.OptionLandingStraightIn = true
return self
end
--- Set the aircraft of the AirWing to land in pairs for groups > 1 aircraft.
-- @param #AIRWING self
-- @return #AIRWING self
function AIRWING:SetLandingForcePair()
self.OptionLandingForcePair = true
return self
end
--- Set the aircraft of the AirWing to NOT land in pairs.
-- @param #AIRWING self
-- @return #AIRWING self
function AIRWING:SetLandingRestrictPair()
self.OptionLandingRestrictPair = true
return self
end
--- Set the aircraft of the AirWing to land after overhead break.
-- @param #AIRWING self
-- @return #AIRWING self
function AIRWING:SetLandingOverheadBreak()
self.OptionLandingOverheadBreak = true
return self
end
--- [Helicopter] Set the aircraft of the AirWing to prefer vertical takeoff and landing.
-- @param #AIRWING self
-- @return #AIRWING self
function AIRWING:SetOptionPreferVerticalLanding()
self.OptionPreferVerticalLanding = true
return self
end
--- Set despawn after landing. Aircraft will be despawned after the landing event.
-- Can help to avoid DCS AI taxiing issues.
-- @param #AIRWING self
@@ -1136,6 +1190,10 @@ function AIRWING:_GetPatrolData(PatrolPoints, RefuelSystem)
for _,_patrolpoint in pairs(PatrolPoints) do
local patrolpoint=_patrolpoint --#AIRWING.PatrolData
if patrolpoint.IsZonePoint and patrolpoint.IsZonePoint == true and patrolpoint.patrolzone then
-- update
patrolpoint.coord = patrolpoint.patrolzone:GetCoordinate()
end
if (RefuelSystem and patrolpoint.refuelsystem and RefuelSystem==patrolpoint.refuelsystem) or RefuelSystem==nil or patrolpoint.refuelsystem==nil then
return patrolpoint
end
@@ -1195,7 +1253,7 @@ function AIRWING:CheckCAP()
patrol.noccupied=patrol.noccupied+1
if self.markpoints then AIRWING.UpdatePatrolPointMarker(patrol) end
if self.markpoints then self:UpdatePatrolPointMarker(patrol) end
self:AddMission(missionCAP)
@@ -1247,7 +1305,7 @@ function AIRWING:CheckRECON()
patrol.noccupied=patrol.noccupied+1
if self.markpoints then AIRWING.UpdatePatrolPointMarker(patrol) end
if self.markpoints then self:UpdatePatrolPointMarker(patrol) end
self:AddMission(missionRECON)
@@ -1292,7 +1350,7 @@ function AIRWING:CheckTANKER()
patrol.noccupied=patrol.noccupied+1
if self.markpoints then AIRWING.UpdatePatrolPointMarker(patrol) end
if self.markpoints then self:UpdatePatrolPointMarker(patrol) end
self:AddMission(mission)
@@ -1311,7 +1369,7 @@ function AIRWING:CheckTANKER()
patrol.noccupied=patrol.noccupied+1
if self.markpoints then AIRWING.UpdatePatrolPointMarker(patrol) end
if self.markpoints then self:UpdatePatrolPointMarker(patrol) end
self:AddMission(mission)
@@ -1349,7 +1407,7 @@ function AIRWING:CheckAWACS()
patrol.noccupied=patrol.noccupied+1
if self.markpoints then AIRWING.UpdatePatrolPointMarker(patrol) end
if self.markpoints then self:UpdatePatrolPointMarker(patrol) end
self:AddMission(mission)
@@ -1464,7 +1522,21 @@ function AIRWING:onafterFlightOnMission(From, Event, To, FlightGroup, Mission)
self:T(self.lid..string.format("Group %s on %s mission %s", FlightGroup:GetName(), Mission:GetType(), Mission:GetName()))
if self.UseConnectedOpsAwacs and self.ConnectedOpsAwacs then
self.ConnectedOpsAwacs:__FlightOnMission(2,FlightGroup,Mission)
end
end
-- Landing Options
if self.OptionLandingForcePair then
FlightGroup:SetOptionLandingForcePair()
elseif self.OptionLandingOverheadBreak then
FlightGroup:SetOptionLandingOverheadBreak()
elseif self.OptionLandingRestrictPair then
FlightGroup:SetOptionLandingRestrictPair()
elseif self.OptionLandingStraightIn then
FlightGroup:SetOptionLandingStraightIn()
end
-- Landing Options Helo
if self.OptionPreferVerticalLanding then
FlightGroup:SetOptionPreferVertical()
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
File diff suppressed because it is too large Load Diff
+6 -7
View File
@@ -839,8 +839,8 @@ function ARMYGROUP:Status()
local ammo=self:GetAmmoElement(element)
-- Output text for element.
text=text..string.format("\n[%d] %s: status=%s, life=%.1f/%.1f, guns=%d, rockets=%d, bombs=%d, missiles=%d, cargo=%d/%d kg",
i, name, status, life, life0, ammo.Guns, ammo.Rockets, ammo.Bombs, ammo.Missiles, element.weightCargo, element.weightMaxCargo)
text=text..string.format("\n[%d] %s: status=%s, life=%.1f/%.1f, guns=%d, cannons=%d, rockets=%d, missiles=%d, cargo=%d/%d kg",
i, name, status, life, life0, ammo.Guns, ammo.Cannons, ammo.Rockets, ammo.Missiles, element.weightCargo, element.weightMaxCargo)
end
if #self.elements==0 then
text=text.." none!"
@@ -1571,7 +1571,7 @@ end
-- @param Core.Zone#ZONE Zone The zone to return to.
-- @param #number Formation Formation of the group.
function ARMYGROUP:onafterRTZ(From, Event, To, Zone, Formation)
self:T2(self.lid.."onafterRTZ")
self:T(self.lid.."onafterRTZ")
-- Zone.
local zone=Zone or self.homezone
@@ -1841,8 +1841,6 @@ function ARMYGROUP:_UpdateEngageTarget()
-- Check if target moved more than 100 meters or we do not have line of sight.
if dist>100 or los==false then
--env.info("FF Update Engage Target Moved "..self.engage.Target:GetName())
-- Update new position.
self.engage.Coordinate:UpdateFromVec3(vec3)
@@ -1852,13 +1850,14 @@ function ARMYGROUP:_UpdateEngageTarget()
-- Remove current waypoint
self:RemoveWaypointByID(self.engage.Waypoint.uid)
local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.9)
-- Get new coordinate where to go.
local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.95)
-- Add waypoint after current.
self.engage.Waypoint=self:AddWaypoint(intercoord, self.engage.Speed, uid, self.engage.Formation, true)
-- Set if we want to resume route after reaching the detour waypoint.
self.engage.Waypoint.detour=0
self.engage.Waypoint.detour=1
end
+227 -38
View File
@@ -324,6 +324,10 @@
--
-- A ground attack mission can be created with the @{#AUFTRAG.NewGROUNDATTACK}() function.
--
-- ## NAVALENGAGEMENT
--
-- A naval engagement mission can be created with the @{#AUFTRAG.NewNAVALENGAGEMENT}() function.
--
-- # Assigning Missions
--
-- An AUFTRAG can be assigned to groups (FLIGHTGROUP, ARMYGROUP, NAVYGROUP), legions (AIRWING, BRIGADE, FLEET) or to a COMMANDER.
@@ -397,6 +401,7 @@ AUFTRAG = {
conditionPush = {},
conditionSuccessSet = false,
conditionFailureSet = false,
repeatDelay = 1,
}
--- Global mission counter.
@@ -442,6 +447,7 @@ _AUFTRAGSNR=0
-- @field #string HOVER Hover.
-- @field #string LANDATCOORDINATE Land at coordinate.
-- @field #string GROUNDATTACK Ground attack.
-- @field #string NAVALENGAGEMENT Naval engagement (similar to GROUNDATTACK).
-- @field #string CARGOTRANSPORT Cargo transport.
-- @field #string RELOCATECOHORT Relocate a cohort from one legion to another.
-- @field #string AIRDEFENSE Air defense.
@@ -490,6 +496,7 @@ AUFTRAG.Type={
HOVER="Hover",
LANDATCOORDINATE="Land at Coordinate",
GROUNDATTACK="Ground Attack",
NAVALENGAGEMENT="Naval Engagement",
CARGOTRANSPORT="Cargo Transport",
RELOCATECOHORT="Relocate Cohort",
AIRDEFENSE="Air Defence",
@@ -514,6 +521,7 @@ AUFTRAG.Type={
-- @field #string BARRAGE Barrage.
-- @field #string HOVER Hover.
-- @field #string GROUNDATTACK Ground attack.
-- @field #string NAVALENGAGEMENT Naval engagement.
-- @field #string FERRY Ferry mission.
-- @field #string RELOCATECOHORT Relocate cohort.
-- @field #string AIRDEFENSE Air defense.
@@ -536,6 +544,7 @@ AUFTRAG.SpecialTask={
ARMORATTACK="AmorAttack",
HOVER="Hover",
GROUNDATTACK="Ground Attack",
NAVALENGAGEMENT="Naval Engagement",
FERRY="Ferry",
RELOCATECOHORT="Relocate Cohort",
AIRDEFENSE="Air Defense",
@@ -665,7 +674,7 @@ AUFTRAG.Category={
--- AUFTRAG class version.
-- @field #string version
AUFTRAG.version="1.2.1"
AUFTRAG.version="1.3.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@@ -1320,13 +1329,19 @@ end
-- @param #number Altitude Orbit altitude in feet. Default is y component of `Coordinate`.
-- @param #number Speed Orbit indicated airspeed in knots at the set altitude ASL. Default 350 KIAS.
-- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West).
-- @param #number Leg Length of race-track in NM. Default 10 NM.
-- @param #number Leg Length of race-track in NM. Default 10 NM. Set to 0 for a simple circular orbit.
-- @param #number RefuelSystem Refueling system (0=boom, 1=probe). This info is *only* for AIRWINGs so they launch the right tanker type.
-- @return #AUFTRAG self
function AUFTRAG:NewTANKER(Coordinate, Altitude, Speed, Heading, Leg, RefuelSystem)
local mission
if Leg == 0 then
mission=AUFTRAG:NewORBIT_CIRCLE(Coordinate,Altitude,Speed)
else
mission=AUFTRAG:NewORBIT_RACETRACK(Coordinate,Altitude,Speed,Heading,Leg)
end
-- Create ORBIT first.
local mission=AUFTRAG:NewORBIT_RACETRACK(Coordinate, Altitude, Speed, Heading, Leg)
--local mission=AUFTRAG:NewORBIT_RACETRACK(Coordinate, Altitude, Speed, Heading, Leg)
-- Mission type TANKER.
mission.type=AUFTRAG.Type.TANKER
@@ -1428,7 +1443,7 @@ function AUFTRAG:NewCAP(ZoneCAP, Altitude, Speed, Coordinate, Heading, Leg, Targ
mission:_SetLogID()
-- DCS task parameters:
mission.engageZone=ZoneCAP
mission.engageZone=ZoneCAP or Coordinate
mission.engageTargetTypes=TargetTypes or {"Air"}
-- Mission options:
@@ -1715,6 +1730,42 @@ function AUFTRAG:NewSEAD(Target, Altitude)
return mission
end
--- **[AIR]** Create a SEAD in Zone mission.
-- @param #AUFTRAG self
-- @param Core.Zone#ZONE TargetZone The target zone to attack.
-- @param #number Altitude Engage altitude in feet. Default 25000 ft.
-- @param #table TargetTypes Table of string of DCS known target types, defaults to {"Air Defence"}. See [DCS Target Attributes](https://wiki.hoggitworld.com/view/DCS_enum_attributes)
-- @param #number Duration Engage this much time when the AUFTRAG starts executing.
-- @return #AUFTRAG self
function AUFTRAG:NewSEADInZone(TargetZone, Altitude, TargetTypes, Duration)
local mission=AUFTRAG:New(AUFTRAG.Type.SEAD)
--mission:_TargetFromObject(TargetZone)
-- DCS Task options:
mission.engageWeaponType=ENUMS.WeaponFlag.Auto
mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL
mission.engageAltitude=UTILS.FeetToMeters(Altitude or 25000)
mission.engageZone = TargetZone
mission.engageTargetTypes = TargetTypes or {"Air Defence"}
-- Mission options:
mission.missionTask=ENUMS.MissionTask.SEAD
mission.missionAltitude=mission.engageAltitude
mission.missionFraction=0.2
mission.optionROE=ENUMS.ROE.OpenFire
mission.optionROT=ENUMS.ROT.EvadeFire
mission.categories={AUFTRAG.Category.AIRCRAFT}
mission.DCStask=mission:GetDCSMissionTask()
mission:SetDuration(Duration or 1800)
return mission
end
--- **[AIR]** Create a STRIKE mission. Flight will attack the closest map object to the specified coordinate.
-- @param #AUFTRAG self
-- @param Core.Point#COORDINATE Target The target coordinate. Can also be given as a GROUP, UNIT, STATIC, SET_GROUP, SET_UNIT, SET_STATIC or TARGET object.
@@ -1752,8 +1803,9 @@ end
-- @param Core.Point#COORDINATE Target Target coordinate. Can also be specified as a GROUP, UNIT, STATIC, SET_GROUP, SET_UNIT, SET_STATIC or TARGET object.
-- @param #number Altitude Engage altitude in feet. Default 25000 ft.
-- @param #number EngageWeaponType Which weapon to use. Defaults to auto, ie ENUMS.WeaponFlag.Auto. See ENUMS.WeaponFlag for options.
-- @param #boolean Divebomb If true, use a dive bombing attack approach.
-- @return #AUFTRAG self
function AUFTRAG:NewBOMBING(Target, Altitude, EngageWeaponType)
function AUFTRAG:NewBOMBING(Target, Altitude, EngageWeaponType, Divebomb)
local mission=AUFTRAG:New(AUFTRAG.Type.BOMBING)
@@ -1770,6 +1822,7 @@ function AUFTRAG:NewBOMBING(Target, Altitude, EngageWeaponType)
mission.missionFraction=0.5
mission.optionROE=ENUMS.ROE.OpenFire
mission.optionROT=ENUMS.ROT.NoReaction -- No reaction is better.
mission.optionDivebomb = Divebomb or nil
-- Evaluate result after 5 min. We might need time until the bombs have dropped and targets have been detroyed.
mission.dTevaluate=5*60
@@ -1984,7 +2037,7 @@ function AUFTRAG:NewRESCUEHELO(Carrier)
-- Mission options:
mission.missionTask=ENUMS.MissionTask.NOTHING
mission.missionFraction=0.5
mission.missionFraction=0.9
mission.optionROE=ENUMS.ROE.WeaponHold
mission.optionROT=ENUMS.ROT.NoReaction
@@ -2165,7 +2218,7 @@ end
-- **Note** that it is recommended to set the weapon range via the `OPSGROUP:AddWeaponRange()` function as this cannot be retrieved from the DCS API.
-- @param #AUFTRAG self
-- @param Core.Point#COORDINATE Target Center of the firing solution.
-- @param #number Nshots Number of shots to be fired. Default `#nil`.
-- @param #number Nshots Number of shots to be fired. Default `#nil`. If value is in (0,1), it is interpreted as per cent of available ammo.
-- @param #number Radius Radius of the shells in meters. Default 100 meters.
-- @param #number Altitude Altitude in meters. Can be used to setup a Barrage. Default `#nil`.
-- @return #AUFTRAG self
@@ -2279,8 +2332,9 @@ end
-- @param #number Speed Speed in knots.
-- @param #number Altitude Altitude in feet. Only for airborne units. Default 2000 feet ASL.
-- @param #string Formation Formation used by ground units during patrol. Default "Off Road".
-- @param #number StayInZoneTime Stay this many seconds in the zone when done, only then drive back.
-- @return #AUFTRAG self
function AUFTRAG:NewCAPTUREZONE(OpsZone, Coalition, Speed, Altitude, Formation)
function AUFTRAG:NewCAPTUREZONE(OpsZone, Coalition, Speed, Altitude, Formation, StayInZoneTime)
local mission=AUFTRAG:New(AUFTRAG.Type.CAPTUREZONE)
@@ -2294,6 +2348,7 @@ function AUFTRAG:NewCAPTUREZONE(OpsZone, Coalition, Speed, Altitude, Formation)
mission.optionROE=ENUMS.ROE.ReturnFire
mission.optionROT=ENUMS.ROT.PassiveDefense
mission.optionAlarm=ENUMS.AlarmState.Auto
mission.StayInZoneTime = StayInZoneTime
mission.missionFraction=0.1
mission.missionSpeed=Speed and UTILS.KnotsToKmph(Speed) or nil
@@ -2335,11 +2390,13 @@ function AUFTRAG:NewARMORATTACK(Target, Speed, Formation)
return mission
end
--- **[GROUND]** Create a GROUNDATTACK mission. Ground group(s) will go to a target object and attack.
--- **[GROUND]** Create a GROUNDATTACK mission. Ground group(s) will go to a target object and attack at their own discretion.
-- Unfortunately, the "Attack Group" and "Attack Unit" tasks do not work for ground and naval groups (only for aircraft).
-- Therefore, we resort to this workaround, which guides the attacking group to the vicinity of the target. Then they start shooting on their own, once they detect the target.
-- @param #AUFTRAG self
-- @param Wrapper.Positionable#POSITIONABLE Target The target to attack. Can be a GROUP, UNIT or STATIC object.
-- @param #number Speed Speed in knots. Default max.
-- @param #string Formation The attack formation, e.g. "Wedge", "Vee" etc. Default `ENUMS.Formation.Vehicle.Vee`.
-- @param #string Formation The attack formation, e.g. "Wedge", "Vee" etc. Default `ENUMS.Formation.Vehicle.Vee`. Only working for ground, not naval!
-- @return #AUFTRAG self
function AUFTRAG:NewGROUNDATTACK(Target, Speed, Formation)
@@ -2366,6 +2423,38 @@ function AUFTRAG:NewGROUNDATTACK(Target, Speed, Formation)
return mission
end
--- **[NAVAL]** Create a NAVALENGAGEMENT mission. Naval group(s) will go to a target object and attack at their own discretion.
-- Unfortunately, the "Attack Group" and "Attack Unit" tasks do not work for ground and naval groups (only for aircraft).
-- Therefore, we resort to this workaround, which guides the attacking group to the vicinity of the target. Then they start shooting on their own, once they detect the target.
-- @param #AUFTRAG self
-- @param Wrapper.Positionable#POSITIONABLE Target The target to attack. Can be a GROUP, UNIT or STATIC object.
-- @param #number Speed Speed in knots. Default max.
-- @param #number Depth The attack depth in meters. Only for submarines!
-- @return #AUFTRAG self
function AUFTRAG:NewNAVALENGAGEMENT(Target, Speed, Depth)
local mission=AUFTRAG:New(AUFTRAG.Type.NAVALENGAGEMENT)
mission:_TargetFromObject(Target)
mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.NAVALENGAGEMENT)
-- Defaults.
mission.optionROE=ENUMS.ROE.OpenFire
mission.optionAlarm=ENUMS.AlarmState.Auto
mission.missionFraction=0.70
mission.missionSpeed=Speed and UTILS.KnotsToKmph(Speed) or nil
mission.missionAltitude=Depth or 0
mission.categories={AUFTRAG.Category.NAVAL}
mission.DCStask=mission:GetDCSMissionTask()
mission.DCStask.params.speed=mission.missionSpeed and UTILS.KmphToMps(mission.missionSpeed) or nil
return mission
end
--- **[AIR, GROUND, NAVAL]** Create a RECON mission.
-- @param #AUFTRAG self
-- @param Core.Set#SET_ZONE ZoneSet The recon zones.
@@ -2658,33 +2747,35 @@ function AUFTRAG:NewFromTarget(Target, MissionType)
local mission=nil --#AUFTRAG
if MissionType==AUFTRAG.Type.ANTISHIP then
mission=self:NewANTISHIP(Target, Altitude)
mission=self:NewANTISHIP(Target)
elseif MissionType==AUFTRAG.Type.ARTY then
mission=self:NewARTY(Target, Nshots, Radius)
mission=self:NewARTY(Target, 0.3) -- use 30% of the available ammo
elseif MissionType==AUFTRAG.Type.BAI then
mission=self:NewBAI(Target, Altitude)
mission=self:NewBAI(Target)
elseif MissionType==AUFTRAG.Type.BOMBCARPET then
mission=self:NewBOMBCARPET(Target, Altitude, CarpetLength)
mission=self:NewBOMBCARPET(Target)
elseif MissionType==AUFTRAG.Type.BOMBING then
mission=self:NewBOMBING(Target, Altitude)
mission=self:NewBOMBING(Target)
elseif MissionType==AUFTRAG.Type.BOMBRUNWAY then
mission=self:NewBOMBRUNWAY(Target, Altitude)
mission=self:NewBOMBRUNWAY(Target)
elseif MissionType==AUFTRAG.Type.STRAFING then
mission=self:NewSTRAFING(Target, Altitude)
mission=self:NewSTRAFING(Target)
elseif MissionType==AUFTRAG.Type.CAS then
mission=self:NewCAS(ZONE_RADIUS:New(Target:GetName(),Target:GetVec2(),1000), Altitude, Speed, Target:GetAverageCoordinate(), Heading, Leg, TargetTypes)
mission=self:NewCAS(ZONE_RADIUS:New(Target:GetName(),Target:GetVec2(),1000), nil, nil, Target:GetAverageCoordinate())
elseif MissionType==AUFTRAG.Type.CASENHANCED then
mission=self:NewCASENHANCED(ZONE_RADIUS:New(Target:GetName(),Target:GetVec2(),1000), Altitude, Speed, RangeMax, NoEngageZoneSet, TargetTypes)
mission=self:NewCASENHANCED(ZONE_RADIUS:New(Target:GetName(),Target:GetVec2(),1000))
elseif MissionType==AUFTRAG.Type.INTERCEPT then
mission=self:NewINTERCEPT(Target)
elseif MissionType==AUFTRAG.Type.SEAD then
mission=self:NewSEAD(Target, Altitude)
mission=self:NewSEAD(Target)
elseif MissionType==AUFTRAG.Type.STRIKE then
mission=self:NewSTRIKE(Target, Altitude)
mission=self:NewSTRIKE(Target)
elseif MissionType==AUFTRAG.Type.ARMORATTACK then
mission=self:NewARMORATTACK(Target, Speed)
mission=self:NewARMORATTACK(Target)
elseif MissionType==AUFTRAG.Type.GROUNDATTACK then
mission=self:NewGROUNDATTACK(Target, Speed, Formation)
mission=self:NewGROUNDATTACK(Target)
elseif MissionType==AUFTRAG.Type.NAVALENGAGEMENT then
mission=self:NewNAVALENGAGEMENT(Target)
else
return nil
end
@@ -2801,7 +2892,7 @@ function AUFTRAG:NewAUTO(EngageGroup)
if auftrag==AUFTRAG.Type.ANTISHIP then
mission=AUFTRAG:NewANTISHIP(Target)
elseif auftrag==AUFTRAG.Type.ARTY then
mission=AUFTRAG:NewARTY(Target)
mission=AUFTRAG:NewARTY(Target, 0.2)
elseif auftrag==AUFTRAG.Type.AWACS then
mission=AUFTRAG:NewAWACS(Coordinate, Altitude,Speed,Heading,Leg)
elseif auftrag==AUFTRAG.Type.BAI then
@@ -2966,6 +3057,16 @@ function AUFTRAG:SetRepeat(Nrepeat)
return self
end
--- **[LEGION, COMMANDER, CHIEF]** Set the repeat delay in seconds after a mission is successful/failed. Only valid if the mission is handled by a LEGION (AIRWING, BRIGADE, FLEET) or higher level.
-- @param #AUFTRAG self
-- @param #number Nrepeat Repeat delay in seconds. Default 1.
-- @return #AUFTRAG self
function AUFTRAG:SetRepeatDelay(RepeatDelay)
self.repeatDelay = RepeatDelay
return self
end
--- **[LEGION, COMMANDER, CHIEF]** Set how many times the mission is repeated if it fails. Only valid if the mission is handled by a LEGION (AIRWING, BRIGADE, FLEET) or higher level.
-- @param #AUFTRAG self
-- @param #number Nrepeat Number of repeats. Default 0.
@@ -3961,6 +4062,23 @@ function AUFTRAG:IsOver()
return over
end
--- Check if mission is repeatable.
-- @param #AUFTRAG self
-- @return #boolean If true, mission is repeatable.
function AUFTRAG:IsRepeatable()
local repeatmeS=self.repeatedSuccess<self.NrepeatSuccess or self.repeated<self.Nrepeat
local repeatmeF=self.repeatedFailure<self.NrepeatFailure or self.repeated<self.Nrepeat
if repeatmeS==true or repeatmeF==true then return true else return false end
return false
end
--- Check if mission is NOT repeatable.
-- @param #AUFTRAG self
-- @return #boolean If true, mission is NOT repeatable.
function AUFTRAG:IsNotRepeatable()
return not self:IsRepeatable()
end
--- Check if mission is NOT over.
-- @param #AUFTRAG self
-- @return #boolean If true, mission is NOT over yet.
@@ -4325,7 +4443,7 @@ function AUFTRAG:onafterStatus(From, Event, To)
-- Group info.
if self.verbose>=3 then
-- Data on assigned groups.
local text=string.format("Assets [N=%d,Nassigned=%s, Ndead=%s]:", self.Nassets or 0, self.Nassigned or 0, self.Ndead or 0)
local text=string.format("Assets [N=%d, Nassigned=%s, Ndead=%s]:", self.Nassets or 0, self.Nassigned or 0, self.Ndead or 0)
for i,_asset in pairs(self.assets or {}) do
local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem
text=text..string.format("\n[%d] %s: spawned=%s, requested=%s, reserved=%s", i, asset.spawngroupname, tostring(asset.spawned), tostring(asset.requested), tostring(asset.reserved))
@@ -4366,7 +4484,7 @@ function AUFTRAG:Evaluate()
local owndamage=self.Ncasualties/self.Nelements*100
-- Current number of mission targets.
local Ntargets=self:CountMissionTargets()
local Ntargets=self:CountMissionTargets(true)
local Ntargets0=self:GetTargetInitialNumber()
local Life=self:GetTargetLife()
@@ -4765,6 +4883,8 @@ end
-- @return #boolean If `true`, all groups are done with the mission.
function AUFTRAG:CheckGroupsDone()
local fsmState = self:GetState()
-- Check status of all OPS groups.
for groupname,data in pairs(self.groupdata) do
local groupdata=data --#AUFTRAG.GroupData
@@ -4815,13 +4935,27 @@ function AUFTRAG:CheckGroupsDone()
self:T2(self.lid..string.format("CheckGroupsDone: Mission is still in state %s [FSM=%s] and reinfoce=%d. Mission NOT DONE!", self.status, self:GetState(), self.reinforce))
return false
end
local NopsgroupsAlive=self:CountOpsGroups()
local NopsgroupsDone=self:CountOpsGroupsInStatus(AUFTRAG.GroupStatus.DONE)+self:CountOpsGroupsInStatus(AUFTRAG.GroupStatus.CANCELLED)
-- It could be that all groups were destroyed on the way to the mission execution waypoint.
-- TODO: would be better to check if everybody is dead by now.
if self:IsStarted() and self:CountOpsGroups()==0 then
if self:IsStarted() and NopsgroupsAlive==0 then
self:T(self.lid..string.format("CheckGroupsDone: Mission is STARTED state %s [FSM=%s] but count of alive OPSGROUP is zero. Mission DONE!", self.status, self:GetState()))
return true
end
-- Every group alive is done or cancelled
if NopsgroupsAlive==NopsgroupsDone then
self:T(self.lid..string.format("CheckGroupsDone: Mission is in state %s [FSM=%s] but all groups [=%d] are done or cancelled. Mission DONE!", self.status, self:GetState(), NopsgroupsAlive))
return true
end
if (self:IsStarted() or self:IsExecuting()) and (fsmState == AUFTRAG.Status.STARTED or fsmState == AUFTRAG.Status.EXECUTING) and NopsgroupsAlive>0 then
self:T(self.lid..string.format("CheckGroupsDone: Mission is in state %s [FSM=%s] and count of alive OPSGROUP > zero. Mission NOT DONE!", self.status, self:GetState()))
return false
end
return true
end
@@ -5160,7 +5294,7 @@ function AUFTRAG:onafterSuccess(From, Event, To)
-- Repeat mission.
self:T(self.lid..string.format("Mission SUCCESS! Repeating mission for the %d time (max %d times) ==> Repeat mission!", self.repeated+1, N))
self:Repeat()
self:__Repeat(self.repeatDelay)
else
@@ -5202,7 +5336,7 @@ function AUFTRAG:onafterFailed(From, Event, To)
-- Repeat mission.
self:T(self.lid..string.format("Mission FAILED! Repeating mission for the %d time (max %d times) ==> Repeat mission!", self.repeated+1, N))
self:Repeat()
self:__Repeat(self.repeatDelay)
else
@@ -5413,8 +5547,9 @@ end
--- Count alive mission targets.
-- @param #AUFTRAG self
-- @param #boolean OnlyReallyAlive (Optional) If `true`, count only really alive targets (units, groups) but not coordinates or zones.
-- @return #number Number of alive target units.
function AUFTRAG:CountMissionTargets()
function AUFTRAG:CountMissionTargets(OnlyReallyAlive)
local N=0
@@ -5422,7 +5557,7 @@ function AUFTRAG:CountMissionTargets()
local Coalitions=self.coalition and UTILS.GetCoalitionEnemy(self.coalition, true) or nil
if self.engageTarget then
N=self.engageTarget:CountTargets(Coalitions)
N=self.engageTarget:CountTargets(Coalitions, OnlyReallyAlive)
end
return N
@@ -6110,7 +6245,7 @@ function AUFTRAG:GetDCSMissionTask()
local coords = self.engageTarget:GetCoordinates()
for _, coord in pairs(coords) do
local DCStask = CONTROLLABLE.TaskBombing(nil, coord:GetVec2(), self.engageAsGroup, self.engageWeaponExpend, self.engageQuantity, self.engageDirection, self.engageAltitude, self.engageWeaponType)
local DCStask = CONTROLLABLE.TaskBombing(nil, coord:GetVec2(), self.engageAsGroup, self.engageWeaponExpend, self.engageQuantity, self.engageDirection, self.engageAltitude, self.engageWeaponType, self.optionDivebomb)
table.insert(DCStasks, DCStask)
end
@@ -6150,8 +6285,16 @@ function AUFTRAG:GetDCSMissionTask()
-----------------
-- CAP Mission --
-----------------
local DCStask=CONTROLLABLE.EnRouteTaskEngageTargetsInZone(nil, self.engageZone:GetVec2(), self.engageZone:GetRadius(), self.engageTargetTypes, Priority)
local Vec2 = self.engageZone:GetVec2()
local Radius
if self.engageZone:IsInstanceOf("COORDINATE") then
Radius = UTILS.NMToMeters(20)
else
Radius = self.engageZone:GetRadius()
end
local DCStask=CONTROLLABLE.EnRouteTaskEngageTargetsInZone(nil, Vec2, Radius, self.engageTargetTypes, Priority)
table.insert(self.enrouteTasks, DCStask)
@@ -6305,9 +6448,35 @@ function AUFTRAG:GetDCSMissionTask()
-- Add enroute task SEAD. Disabled that here because the group enganges everything on its route.
--local DCStask=CONTROLLABLE.EnRouteTaskSEAD(nil, self.TargetType)
--table.insert(self.enrouteTasks, DCStask)
self:_GetDCSAttackTask(self.engageTarget, DCStasks)
if self.engageZone then
--local DCStask=CONTROLLABLE.EnRouteTaskSEAD(nil, self.engageTargetTypes)
--table.insert(self.enrouteTasks, DCStask)
self.engageZone:Scan({Object.Category.UNIT},{Unit.Category.GROUND_UNIT})
local ScanUnitSet = self.engageZone:GetScannedSetUnit()
local SeadUnitSet = SET_UNIT:New()
for _,_unit in pairs (ScanUnitSet.Set) do
local unit = _unit -- Wrapper.Unit#UNTI
if unit and unit:IsAlive() and unit:HasSEAD() then
self:T("Adding UNIT for SEAD: "..unit:GetName())
local task = CONTROLLABLE.TaskAttackUnit(nil,unit,GroupAttack,AI.Task.WeaponExpend.ALL,1,Direction,self.engageAltitude,2956984318)
table.insert(DCStasks, task)
SeadUnitSet:AddUnit(unit)
end
end
self.engageTarget = TARGET:New(SeadUnitSet)
--local OrbitTask = CONTROLLABLE.TaskOrbitCircle(nil,self.engageAltitude,self.missionSpeed,self.engageZone:GetCoordinate())
--local Point = self.engageZone:GetVec2()
--local OrbitTask = CONTROLLABLE.TaskOrbitCircleAtVec2(nil,Point,self.engageAltitude,self.missionSpeed)
--table.insert(DCStasks, OrbitTask)
else
self:_GetDCSAttackTask(self.engageTarget, DCStasks)
end
elseif self.type==AUFTRAG.Type.STRIKE then
--------------------
@@ -6527,6 +6696,26 @@ function AUFTRAG:GetDCSMissionTask()
table.insert(DCStasks, DCStask)
elseif self.type==AUFTRAG.Type.NAVALENGAGEMENT then
---------------------------
-- NAVAL ENGAGEMENT Mission --
---------------------------
local DCStask={}
DCStask.id=AUFTRAG.SpecialTask.NAVALENGAGEMENT
-- We create a "fake" DCS task and pass the parameters to the NAVYGROUP.
local param={}
param.target=self:GetTargetData()
param.speed=self.missionSpeed and UTILS.KmphToMps(self.missionSpeed) or nil
param.altitude=self.missionAltitude or 0
DCStask.params=param
table.insert(DCStasks, DCStask)
elseif self.type==AUFTRAG.Type.AMMOSUPPLY then
-------------------------
+91 -25
View File
@@ -17,7 +17,7 @@
-- ===
--
-- ### Author: **applevangelist**
-- @date Last Update Jan 2025
-- @date Last Update July 2025
-- @module Ops.AWACS
-- @image OPS_AWACS.jpg
@@ -237,7 +237,7 @@ do
-- -- Callsign will be "Focus". We'll be a Angels 30, doing 300 knots, orbit leg to 88deg with a length of 25nm.
-- testawacs:SetAwacsDetails(CALLSIGN.AWACS.Focus,1,30,300,88,25)
-- -- Set up SRS on port 5010 - change the below to your path and port
-- testawacs:SetSRS("C:\\Program Files\\DCS-SimpleRadio-Standalone","female","en-GB",5010)
-- testawacs:SetSRS("C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio","female","en-GB",5010)
-- -- Add a "red" border we don't want to cross, set up in the mission editor with a late activated helo named "Red Border#ZONE_POLYGON"
-- testawacs:SetRejectionZone(ZONE:FindByName("Red Border"))
-- -- Our CAP flight will have the callsign "Ford", we want 4 AI planes, Time-On-Station is four hours, doing 300 kn IAS.
@@ -255,7 +255,7 @@ do
-- -- The CAP station zone is called "Fremont". We will be on 255 AM. Note the Orbit Zone is given as *nil* in the `New()`-Statement
-- local testawacs = AWACS:New("GCI Senaki",AwacsAW,"blue",AIRBASE.Caucasus.Senaki_Kolkhi,nil,ZONE:FindByName("Rock"),"Fremont",255,radio.modulation.AM )
-- -- Set up SRS on port 5010 - change the below to your path and port
-- testawacs:SetSRS("C:\\Program Files\\DCS-SimpleRadio-Standalone","female","en-GB",5010)
-- testawacs:SetSRS("C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio","female","en-GB",5010)
-- -- Add a "red" border we don't want to cross, set up in the mission editor with a late activated helo named "Red Border#ZONE_POLYGON"
-- testawacs:SetRejectionZone(ZONE:FindByName("Red Border"))
-- -- Our CAP flight will have the callsign "Ford", we want 4 AI planes, Time-On-Station is four hours, doing 300 kn IAS.
@@ -509,7 +509,7 @@ do
-- @field #AWACS
AWACS = {
ClassName = "AWACS", -- #string
version = "0.2.71", -- #string
version = "0.2.73", -- #string
lid = "", -- #string
coalition = coalition.side.BLUE, -- #number
coalitiontxt = "blue", -- #string
@@ -1123,7 +1123,7 @@ function AWACS:New(Name,AirWing,Coalition,AirbaseName,AwacsOrbit,OpsZone,Station
self.EscortMissionReplacement = {}
-- SRS
self.PathToSRS = "C:\\Program Files\\DCS-SimpleRadio-Standalone"
self.PathToSRS = "C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio"
self.Gender = "female"
self.Culture = "en-GB"
self.Voice = nil
@@ -1242,6 +1242,8 @@ function AWACS:New(Name,AirWing,Coalition,AirbaseName,AwacsOrbit,OpsZone,Station
self:AddTransition("*", "Intercept", "*")
self:AddTransition("*", "InterceptSuccess", "*")
self:AddTransition("*", "InterceptFailure", "*")
self:AddTransition("*", "VIDSuccess", "*")
self:AddTransition("*", "VIDFailure", "*")
self:AddTransition("*", "Stop", "Stopped") -- Stop FSM.
@@ -1365,18 +1367,38 @@ function AWACS:New(Name,AirWing,Coalition,AirbaseName,AwacsOrbit,OpsZone,Station
-- @param #string To To state.
--- On After "InterceptSuccess" event. Intercept successful.
-- @function [parent=#AWACS] OnAfterIntercept
-- @function [parent=#AWACS] OnAfterInterceptSuccess
-- @param #AWACS self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
--- On After "InterceptFailure" event. Intercept failure.
-- @function [parent=#AWACS] OnAfterIntercept
-- @function [parent=#AWACS] OnAfterInterceptFailure
-- @param #AWACS self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
--- On After "VIDSuccess" event. Intercept successful.
-- @function [parent=#AWACS] OnAfterVIDSuccess
-- @param #AWACS self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #number GID Managed group ID (Player)
-- @param Wrapper.Group#GROUP Group (Player) Group done the VID
-- @param #AWACS.ManagedContact Contact The contact that was VID'd
--- On After "VIDFailure" event. Intercept failure.
-- @function [parent=#AWACS] OnAfterVIDFailure
-- @param #AWACS self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #number GID Managed group ID (Player)
-- @param Wrapper.Group#GROUP Group (Player) Group done the VID
-- @param #AWACS.ManagedContact Contact The contact that was VID'd
return self
end
@@ -1574,6 +1596,16 @@ function AWACS:SetLocale(Locale)
return self
end
--- [User] Set own coordinate for BullsEye.
-- @param #AWACS self
-- @param Core.Point#COORDINATE
-- @return #AWACS self
function AWACS:SetBullsCoordinate(Coordinate)
self:T(self.lid.."SetBullsCoordinate")
self.AOCoordinate = Coordinate
return self
end
--- [User] Set the max mission range flights can be away from their home base.
-- @param #AWACS self
-- @param #number NM Distance in nautical miles
@@ -1999,7 +2031,9 @@ function AWACS:SetAdditionalZone(Zone, Draw)
self.BorderZone = Zone
if self.debug then
Zone:DrawZone(self.coalition,{1,0.64,0},1,{1,0.64,0},0.2,1,true)
MARKER:New(Zone:GetCoordinate(),"Defensive Zone"):ToCoalition(self.coalition)
if self.AllowMarkers then
MARKER:New(Zone:GetCoordinate(),"Defensive Zone"):ToCoalition(self.coalition)
end
elseif Draw then
Zone:DrawZone(self.coalition,{1,0.64,0},1,{1,0.64,0},0.2,1,true)
end
@@ -2019,7 +2053,9 @@ function AWACS:SetRejectionZone(Zone,Draw)
--MARKER:New(Zone:GetCoordinate(),"Rejection Zone"):ToAll()
elseif self.debug then
Zone:DrawZone(self.coalition,{1,0.64,0},1,{1,0.64,0},0.2,1,true)
MARKER:New(Zone:GetCoordinate(),"Rejection Zone"):ToCoalition(self.coalition)
if self.AllowMarkers then
MARKER:New(Zone:GetCoordinate(),"Rejection Zone"):ToCoalition(self.coalition)
end
end
return self
end
@@ -2091,7 +2127,7 @@ end
--- [User] Set AWACS SRS TTS details - see @{Sound.SRS} for details. `SetSRS()` will try to use as many attributes configured with @{Sound.SRS#MSRS.LoadConfigFile}() as possible.
-- @param #AWACS self
-- @param #string PathToSRS Defaults to "C:\\Program Files\\DCS-SimpleRadio-Standalone"
-- @param #string PathToSRS Defaults to "C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio"
-- @param #string Gender Defaults to "male"
-- @param #string Culture Defaults to "en-US"
-- @param #number Port Defaults to 5002
@@ -2104,7 +2140,7 @@ end
-- @return #AWACS self
function AWACS:SetSRS(PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey,AccessKey,Backend)
self:T(self.lid.."SetSRS")
self.PathToSRS = PathToSRS or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone"
self.PathToSRS = PathToSRS or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio"
self.Gender = Gender or MSRS.gender or "male"
self.Culture = Culture or MSRS.culture or "en-US"
self.Port = Port or MSRS.port or 5002
@@ -3263,12 +3299,14 @@ function AWACS:_VID(Group,Declaration)
local vidpos = self.gettext:GetEntry("VIDPOS",self.locale)
text = string.format(vidpos,Callsign,self.callsigntxt, Declaration)
self:T(text)
self:__VIDSuccess(3,GID,group,cluster)
else
-- too far away
self:T("Contact VID not close enough")
local vidneg = self.gettext:GetEntry("VIDNEG",self.locale)
text = string.format(vidneg,Callsign,self.callsigntxt)
self:T(text)
self:__VIDFailure(3,GID,group,cluster)
end
self:_NewRadioEntry(text,text,GID,Outcome,true,true,false,true)
end
@@ -4070,10 +4108,14 @@ function AWACS:_CreateAnchorStackFromMarker(Name,Coord)
if self.debug then
AnchorStackOne.StationZone:DrawZone(self.coalition,{0,0,1},1,{0,0,1},0.2,5,true)
local stationtag = string.format("Station: %s\nCoordinate: %s",newname,self.StationZone:GetCoordinate():ToStringLLDDM())
AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
if self.AllowMarkers then
AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
end
else
local stationtag = string.format("Station: %s\nCoordinate: %s",newname,self.StationZone:GetCoordinate():ToStringLLDDM())
AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
if self.AllowMarkers then
AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
end
end
self.AnchorStacks:Push(AnchorStackOne,newname)
@@ -4116,10 +4158,14 @@ function AWACS:_CreateAnchorStack()
--self.AnchorStacks:Flush()
AnchorStackOne.StationZone:DrawZone(self.coalition,{0,0,1},1,{0,0,1},0.2,5,true)
local stationtag = string.format("Station: %s\nCoordinate: %s",newname,self.StationZone:GetCoordinate():ToStringLLDDM())
AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
if self.AllowMarkers then
AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
end
else
local stationtag = string.format("Station: %s\nCoordinate: %s",newname,self.StationZone:GetCoordinate():ToStringLLDDM())
AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
if self.AllowMarkers then
AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
end
end
self.AnchorStacks:Push(AnchorStackOne,newname)
else
@@ -4143,10 +4189,14 @@ function AWACS:_CreateAnchorStack()
if self.debug then
AnchorStackOne.StationZone:DrawZone(self.coalition,{0,0,1},1,{0,0,1},0.2,5,true)
local stationtag = string.format("Station: %s\nCoordinate: %s",newname,self.StationZone:GetCoordinate():ToStringLLDDM())
AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
if self.AllowMarkers then
AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
end
else
local stationtag = string.format("Station: %s\nCoordinate: %s",newname,self.StationZone:GetCoordinate():ToStringLLDDM())
AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
if self.AllowMarkers then
AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
end
end
self.AnchorStacks:Push(AnchorStackOne,newname)
end
@@ -5078,10 +5128,14 @@ function AWACS:AddCAPAirWing(AirWing,Zone)
if self.debug then
AnchorStackOne.StationZone:DrawZone(self.coalition,{0,0,1},1,{0,0,1},0.2,5,true)
local stationtag = string.format("Station: %s\nCoordinate: %s",newname,self.StationZone:GetCoordinate():ToStringLLDDM())
AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
if self.AllowMarkers then
AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
end
else
local stationtag = string.format("Station: %s\nCoordinate: %s",newname,self.StationZone:GetCoordinate():ToStringLLDDM())
AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
if self.AllowMarkers then
AnchorStackOne.AnchorMarker=MARKER:New(AnchorStackOne.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
end
end
self.AnchorStacks:Push(AnchorStackOne,newname)
AirWing.HasOwnStation = true
@@ -5924,23 +5978,35 @@ function AWACS:onafterStart(From, Event, To)
self.OpsZone:DrawZone(self.coalition,{1,0,0},1,{1,0,0},0.2,5,true)
local AOCoordString = self.AOCoordinate:ToStringLLDDM()
local Rocktag = string.format("FEZ: %s\nBulls Coordinate: %s",self.AOName,AOCoordString)
MARKER:New(self.AOCoordinate,Rocktag):ToCoalition(self.coalition)
if self.AllowMarkers then
MARKER:New(self.AOCoordinate,Rocktag):ToCoalition(self.coalition)
end
self.StationZone:DrawZone(self.coalition,{0,0,1},1,{0,0,1},0.2,5,true)
local stationtag = string.format("Station: %s\nCoordinate: %s",self.StationZoneName,self.StationZone:GetCoordinate():ToStringLLDDM())
if not self.GCI then
MARKER:New(self.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
if self.AllowMarkers then
MARKER:New(self.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
end
self.OrbitZone:DrawZone(self.coalition,{0,1,0},1,{0,1,0},0.2,5,true)
MARKER:New(self.OrbitZone:GetCoordinate(),"AIC Orbit Zone"):ToCoalition(self.coalition)
if self.AllowMarkers then
MARKER:New(self.OrbitZone:GetCoordinate(),"AIC Orbit Zone"):ToCoalition(self.coalition)
end
end
else
local AOCoordString = self.AOCoordinate:ToStringLLDDM()
local Rocktag = string.format("FEZ: %s\nBulls Coordinate: %s",self.AOName,AOCoordString)
MARKER:New(self.AOCoordinate,Rocktag):ToCoalition(self.coalition)
if self.AllowMarkers then
MARKER:New(self.AOCoordinate,Rocktag):ToCoalition(self.coalition)
end
if not self.GCI then
MARKER:New(self.OrbitZone:GetCoordinate(),"AIC Orbit Zone"):ToCoalition(self.coalition)
if self.AllowMarkers then
MARKER:New(self.OrbitZone:GetCoordinate(),"AIC Orbit Zone"):ToCoalition(self.coalition)
end
end
local stationtag = string.format("Station: %s\nCoordinate: %s",self.StationZoneName,self.StationZone:GetCoordinate():ToStringLLDDM())
MARKER:New(self.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
if self.AllowMarkers then
MARKER:New(self.StationZone:GetCoordinate(),stationtag):ToCoalition(self.coalition)
end
end
if not self.GCI then
+74 -1
View File
@@ -599,7 +599,80 @@ function BRIGADE:onafterStatus(From, Event, To)
text=text..string.format("\n* %s: spawned=%s", asset.spawngroupname, tostring(asset.spawned))
end
self:I(self.lid..text)
end
end
if self.verbose>=3 then
-- Count numbers
local Ntotal=0
local Nspawned=0
local Nrequested=0
local Nreserved=0
local Nstock=0
local text="\n===========================================\n"
text=text.."Assets:"
local legion=self --Ops.Legion#LEGION
for _,_cohort in pairs(legion.cohorts) do
local cohort=_cohort --Ops.Cohort#COHORT
for _,_asset in pairs(cohort.assets) do
local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem
local state="In Stock"
if asset.flightgroup then
state=asset.flightgroup:GetState()
local mission=legion:GetAssetCurrentMission(asset)
if mission then
state=state..string.format(", Mission \"%s\" [%s]", mission:GetName(), mission:GetType())
end
else
if asset.spawned then
env.info("FF ERROR: asset has opsgroup but is NOT spawned!")
end
if asset.requested and asset.isReserved then
env.info("FF ERROR: asset is requested and reserved. Should not be both!")
state="Reserved+Requested!"
elseif asset.isReserved then
state="Reserved"
elseif asset.requested then
state="Requested"
end
end
-- Text.
text=text..string.format("\n[UID=%03d] %s Legion=%s [%s]: State=%s [RID=%s]",
asset.uid, asset.spawngroupname, legion.alias, cohort.name, state, tostring(asset.rid))
if asset.spawned then
Nspawned=Nspawned+1
end
if asset.requested then
Nrequested=Nrequested+1
end
if asset.isReserved then
Nreserved=Nreserved+1
end
if not (asset.spawned or asset.requested or asset.isReserved) then
Nstock=Nstock+1
end
Ntotal=Ntotal+1
end
end
text=text.."\n-------------------------------------------"
text=text..string.format("\nNstock = %d", Nstock)
text=text..string.format("\nNreserved = %d", Nreserved)
text=text..string.format("\nNrequested = %d", Nrequested)
text=text..string.format("\nNspawned = %d", Nspawned)
text=text..string.format("\nNtotal = %d (=%d)", Ntotal, Nstock+Nspawned+Nrequested+Nreserved)
text=text.."\n==========================================="
self:I(self.lid..text)
end
end
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+7 -3
View File
@@ -35,7 +35,7 @@
-- @field #number Nsuccess Number of successful missions.
-- @field #number Nfailure Number of failed mission.
-- @field #table assetNumbers Asset numbers. Each entry is a table of data type `#CHIEF.AssetNumber`.
-- @extends Ops.Intel#INTEL
-- @extends Ops.Intelligence#INTEL
--- *In preparing for battle I have always found that plans are useless, but planning is indispensable* -- Dwight D Eisenhower
--
@@ -48,7 +48,7 @@
--
-- # Territory
--
-- The chief class allows you to define boarder zones, conflict zones and attack zones.
-- The chief class allows you to define border zones, conflict zones and attack zones.
--
-- ## Border Zones
--
@@ -332,7 +332,7 @@ CHIEF.Strategy = {
--- CHIEF class version.
-- @field #string version
CHIEF.version="0.6.1"
CHIEF.version="0.7.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@@ -2897,6 +2897,8 @@ function CHIEF:_GetMissionPerformanceFromTarget(Target)
table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.ARTY, 30))
else
-- Everything else
table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.BAI, 100))
table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.GROUNDATTACK, 50))
@@ -2912,6 +2914,8 @@ function CHIEF:_GetMissionPerformanceFromTarget(Target)
---
table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.ANTISHIP, 100))
table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.NAVALENGAGEMENT, 50))
table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.ARTY, 30))
else
self:E(self.lid.."ERROR: Unknown Group category!")
+48 -22
View File
@@ -88,7 +88,7 @@ COHORT = {
--- COHORT class version.
-- @field #string version
COHORT.version="0.3.6"
COHORT.version="0.3.7"
--- Global variable to store the unique(!) cohort names
_COHORTNAMES={}
@@ -100,6 +100,7 @@ _COHORTNAMES={}
-- DONE: Create FLOTILLA class.
-- DONE: Added check for properties.
-- DONE: Make general so that PLATOON and SQUADRON can inherit this class.
-- DONE: Better setting of call signs.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
@@ -515,10 +516,12 @@ end
-- @param #COHORT self
-- @param #number Callsign Callsign from CALLSIGN.Aircraft, e.g. "Chevy" for CALLSIGN.Aircraft.CHEVY.
-- @param #number Index Callsign index, Chevy-**1**.
-- @param #string CallsignString (optional) Set this for tasks like TANKER, AWACS or KIOWA and the like, which have special names. E.g. "Darkstar" or "Roughneck".
-- @return #COHORT self
function COHORT:SetCallsign(Callsign, Index)
function COHORT:SetCallsign(Callsign, Index, CallsignString)
self.callsignName=Callsign
self.callsignIndex=Index
self.callsignClearName=CallsignString
self.callsign={}
self.callsign.NumberSquad=Callsign
self.callsign.NumberGroup=Index
@@ -626,32 +629,42 @@ end
--- Remove assets from pool. Not that assets must not be spawned or already reserved or requested.
-- @param #COHORT self
-- @param #number N Number of assets to be removed. Default 1.
-- @param #number Delay Delay in seconds before assets are removed.
-- @return #COHORT self
function COHORT:RemoveAssets(N)
function COHORT:RemoveAssets(N, Delay)
self:T2(self.lid..string.format("Remove %d assets of Cohort", N))
N=N or 1
local n=0
for i=#self.assets,1,-1 do
local asset=self.assets[i] --Functional.Warehouse#WAREHOUSE.Assetitem
if Delay and Delay>0 then
-- Delayed call
self:ScheduleOnce(Delay, COHORT.RemoveAssets, self, N, 0)
else
self:T2(self.lid..string.format("Checking removing asset %s", asset.spawngroupname))
if not (asset.requested or asset.spawned or asset.isReserved) then
self:T2(self.lid..string.format("Removing asset %s", asset.spawngroupname))
table.remove(self.assets, i)
n=n+1
else
self:T2(self.lid..string.format("Could NOT Remove asset %s", asset.spawngroupname))
N=N or 1
local n=0
for i=#self.assets,1,-1 do
local asset=self.assets[i] --Functional.Warehouse#WAREHOUSE.Assetitem
self:T2(self.lid..string.format("Checking removing asset %s", asset.spawngroupname))
if not (asset.requested or asset.spawned or asset.isReserved) then
self:T2(self.lid..string.format("Removing asset %s", asset.spawngroupname))
-- Remove from warehouse and warehouse DB
asset.legion:_DeleteStockItem(asset)
table.remove(self.assets, i)
n=n+1
else
self:T2(self.lid..string.format("Could NOT Remove asset %s", asset.spawngroupname))
end
if n>=N then
break
end
end
if n>=N then
break
end
self:T(self.lid..string.format("Removed %d/%d assets. New asset count=%d", n, N, #self.assets))
end
self:T(self.lid..string.format("Removed %d/%d assets. New asset count=%d", n, N, #self.assets))
return self
end
@@ -679,7 +692,16 @@ end
function COHORT:GetCallsign(Asset)
if self.callsignName then
--[[
["callsign"] =
{
[2] = 1,
["name"] = "Darkstar11",
[3] = 1,
[1] = 5,
[4] = "Darkstar11",
}, -- end of ["callsign"]
]]
Asset.callsign={}
for i=1,Asset.nunits do
@@ -695,12 +717,16 @@ function COHORT:GetCallsign(Asset)
else
self.callsigncounter=self.callsigncounter+1
end
callsign["name"] = self.callsignClearName or UTILS.GetCallsignName(self.callsignName) or "None"
callsign["name"] = string.format("%s%d%d",callsign["name"],callsign[2],callsign[3])
callsign[4] = callsign["name"]
Asset.callsign[i]=callsign
self:T3({callsign=callsign})
--TODO: there is also a table entry .name, which is a string.
--DONE: there is also a table entry .name, which is a string.
--UTILS.PrintTableToLog(callsign)
end
+31 -9
View File
@@ -136,6 +136,7 @@ COMMANDER = {
awacsZones = {},
tankerZones = {},
limitMission = {},
maxMissionsAssignPerCycle = 1,
}
--- COMMANDER class version.
@@ -1535,6 +1536,8 @@ function COMMANDER:CheckMissionQueue()
end
end
local missionsAssigned = 0
-- Loop over missions in queue.
for _,_mission in pairs(self.missionqueue) do
local mission=_mission --Ops.Auftrag#AUFTRAG
@@ -1594,9 +1597,12 @@ function COMMANDER:CheckMissionQueue()
-- Recruited assets but no requested escort available. Unrecruit assets!
LEGION.UnRecruitAssets(assets, mission)
end
-- Only ONE mission is assigned.
return
missionsAssigned = missionsAssigned + 1
if missionsAssigned >= (self.maxMissionsAssignPerCycle or 1) then
return
end
end
else
@@ -1611,6 +1617,16 @@ function COMMANDER:CheckMissionQueue()
end
--- Set how many missions can be assigned in a single status iteration. (eg. This is useful for persistent missions where you need to load all AUFTRAGs on mission start and then change it back to default)
--- Warning: Increasing this value will increase the number of missions started per iteration and thus may lead to performance issues if too many missions are started at once.
-- @param #COMMANDER self
-- @param #number Number of missions assigned per status iteration. Default is 1.
-- @return #COMMANDER self.
function COMMANDER:SetMaxMissionsAssignPerCycle(MaxMissionsAssignPerCycle)
self.maxMissionsAssignPerCycle = MaxMissionsAssignPerCycle or 1
return self
end
--- Get cohorts.
-- @param #COMMANDER self
-- @param #table Legions Special legions.
@@ -1670,9 +1686,12 @@ function COMMANDER:_GetCohorts(Legions, Cohorts, Operation)
for _,_legion in pairs(Legions or {}) do
local legion=_legion --Ops.Legion#LEGION
-- Check that runway is operational.
local Runway=legion:IsAirwing() and legion:IsRunwayOperational() or true
-- Check that runway is operational.
local Runway=true
if legion:IsAirwing() then
Runway=legion:IsRunwayOperational() and legion.airbase and legion.airbase:GetCoalition() == legion:GetCoalition()
end
-- Legion has to be running.
if legion:IsRunning() and Runway then
@@ -1703,9 +1722,12 @@ function COMMANDER:_GetCohorts(Legions, Cohorts, Operation)
for _,_legion in pairs(self.legions) do
local legion=_legion --Ops.Legion#LEGION
-- Check that runway is operational.
local Runway=legion:IsAirwing() and legion:IsRunwayOperational() or true
-- Check that runway is operational.
local Runway=true
if legion:IsAirwing() then
Runway=legion:IsRunwayOperational() and legion.airbase and legion.airbase:GetCoalition() == legion:GetCoalition()
end
-- Legion has to be running.
if legion:IsRunning() and Runway then
+168 -23
View File
@@ -1,13 +1,18 @@
-------------------------------------------------------------------------
-- Easy CAP/GCI Class, based on OPS classes
-------------------------------------------------------------------------
-- Documentation
--
-- ## Documentation:
--
-- https://flightcontrol-master.github.io/MOOSE_DOCS_DEVELOP/Documentation/Ops.EasyGCICAP.html
--
-- ## Example Missions:
--
-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Ops/EasyGCICAP).
--
-------------------------------------------------------------------------
-- Date: September 2023
-- Last Update: July 2024
-- Last Update: Aug 2025
-------------------------------------------------------------------------
--
--- **Ops** - Easy GCI & CAP Manager
@@ -71,6 +76,10 @@
-- @field #boolean DespawnAfterHolding
-- @field #list<Ops.Auftrag#AUFTRAG> ListOfAuftrag
-- @field #string defaulttakeofftype Take off type
-- @field #number FuelLowThreshold
-- @field #number FuelCriticalThreshold
-- @field #boolean showpatrolpointmarks
-- @field #table EngageTargetTypes
-- @extends Core.Fsm#FSM
--- *“Airspeed, altitude, and brains. Two are always needed to successfully complete the flight.”* -- Unknown.
@@ -226,6 +235,10 @@ EASYGCICAP = {
DespawnAfterHolding = true,
ListOfAuftrag = {},
defaulttakeofftype = "hot",
FuelLowThreshold = 25,
FuelCriticalThreshold = 10,
showpatrolpointmarks = false,
EngageTargetTypes = {"Air"},
}
--- Internal Squadron data type
@@ -258,10 +271,11 @@ EASYGCICAP = {
-- @field #number Speed
-- @field #number Heading
-- @field #number LegLength
-- @field Core.Zone#ZONE_BASE Zone
--- EASYGCICAP class version.
-- @field #string version
EASYGCICAP.version="0.1.21"
EASYGCICAP.version="0.1.30"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@@ -315,6 +329,10 @@ function EASYGCICAP:New(Alias, AirbaseName, Coalition, EWRName)
self.DespawnAfterHolding = true
self.ListOfAuftrag = {}
self.defaulttakeofftype = "hot"
self.FuelLowThreshold = 25
self.FuelCriticalThreshold = 10
self.showpatrolpointmarks = false
self.EngageTargetTypes = {"Air"}
-- Set some string id for output to DCS.log file.
self.lid=string.format("EASYGCICAP %s | ", self.alias)
@@ -339,6 +357,63 @@ end
-- Functions
-------------------------------------------------------------------------
--- Get a specific managed AirWing by name
-- @param #EASYGCICAP self
-- @param #string AirbaseName Airbase name of the home of this wing.
-- @return Ops.AirWing#AIRWING Airwing or nil if not found
function EASYGCICAP:GetAirwing(AirbaseName)
self:T(self.lid.."GetAirwing")
if self.wings[AirbaseName] then
return self.wings[AirbaseName][1]
end
return nil
end
--- Get a table of all managed AirWings
-- @param #EASYGCICAP self
-- @return #table Table of Ops.AirWing#AIRWING Airwings
function EASYGCICAP:GetAirwingTable()
self:T(self.lid.."GetAirwingTable")
local Wingtable = {}
for _,_object in pairs(self.wings or {}) do
table.insert(Wingtable,_object[1])
end
return Wingtable
end
--- Set "fuel low" threshold for CAP and INTERCEPT flights.
-- @param #EASYGCICAP self
-- @param #number Percent RTB if fuel at this percent. Values: 1..100, defaults to 25.
-- @return #EASYGCICAP self
function EASYGCICAP:SetFuelLow(Percent)
self:T(self.lid.."SetFuelLow")
self.FuelLowThreshold = Percent or 25
return self
end
--- Set markers on the map for Patrol Points.
-- @param #EASYGCICAP self
-- @param #boolean onoff Set to true to switch markers on.
-- @return #EASYGCICAP self
function EASYGCICAP:ShowPatrolPointMarkers(onoff)
if onoff then
self.showpatrolpointmarks = true
else
self.showpatrolpointmarks = false
end
return self
end
--- Set "fuel critical" threshold for CAP and INTERCEPT flights.
-- @param #EASYGCICAP self
-- @param #number Percent RTB if fuel at this percent. Values: 1..100, defaults to 10.
-- @return #EASYGCICAP self
function EASYGCICAP:SetFuelCritical(Percent)
self:T(self.lid.."SetFuelCritical")
self.FuelCriticalThreshold = Percent or 10
return self
end
--- Set CAP formation.
-- @param #EASYGCICAP self
-- @param #number Formation Formation to fly, defaults to ENUMS.Formation.FixedWing.FingerFour.Group
@@ -359,7 +434,7 @@ function EASYGCICAP:SetTankerAndAWACSInvisible(Switch)
return self
end
--- Count alive missions in our internal stack.
--- (internal) Count alive missions in our internal stack.
-- @param #EASYGCICAP self
-- @return #number count
function EASYGCICAP:_CountAliveAuftrags()
@@ -536,6 +611,17 @@ function EASYGCICAP:SetCapStartTimeVariation(Start, End)
return self
end
--- Set which target types CAP flights will prefer to engage, defaults to {"Air"}
-- @param #EASYGCICAP self
-- @param #table types Table of comma separated #string entries, defaults to {"Air"} (everything that flies and is not a weapon). Useful other options are e.g. {"Bombers"}, {"Fighters"},
-- or {"Helicopters"} or combinations like {"Bombers", "Fighters", "UAVs"}. See [Hoggit Wiki](https://wiki.hoggitworld.com/view/DCS_enum_attributes).
-- @return #EASYGCICAP self
function EASYGCICAP:SetCAPEngageTargetTypes(types)
self.EngageTargetTypes = types or {"Air"}
return self
end
--- Add an AirWing to the manager
-- @param #EASYGCICAP self
-- @param #string Airbasename
@@ -582,6 +668,13 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias)
local DespawnAfterLanding = self.DespawnAfterLanding
local DespawnAfterHolding = self.DespawnAfterHolding
-- Check STATIC name
local check = STATIC:FindByName(Airbasename,false) or UNIT:FindByName(Airbasename)
if check == nil then
MESSAGE:New(self.lid.."There's no warehouse static on the map (wrong naming?) for airbase "..tostring(Airbasename).."!",30,"CHECK"):ToAllIf(self.debug):ToLog()
return
end
-- Create Airwing
local CAP_Wing = AIRWING:New(Airbasename,Alias)
CAP_Wing:SetVerbosityLevel(0)
@@ -591,6 +684,10 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias)
CAP_Wing:SetRespawnAfterDestroyed()
CAP_Wing:SetNumberCAP(self.capgrouping)
CAP_Wing:SetCapCloseRaceTrack(true)
if self.showpatrolpointmarks then
CAP_Wing:ShowPatrolPointMarkers(true)
end
if self.capOptionVaryStartTime then
CAP_Wing:SetCapStartTimeVariation(self.capOptionVaryStartTime,self.capOptionVaryEndTime)
@@ -621,6 +718,9 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias)
local engagerange = self.engagerange
local GoZoneSet = self.GoZoneSet
local NoGoZoneSet = self.NoGoZoneSet
local FuelLow = self.FuelLowThreshold or 25
local FuelCritical = self.FuelCriticalThreshold or 10
local EngageTypes = self.EngageTargetTypes or {"Air"}
function CAP_Wing:onbeforeFlightOnMission(From, Event, To, Flightgroup, Mission)
local flightgroup = Flightgroup -- Ops.FlightGroup#FLIGHTGROUP
@@ -632,10 +732,15 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias)
flightgroup:SetDestinationbase(AIRBASE:FindByName(Airbasename))
flightgroup:GetGroup():CommandEPLRS(true,5)
flightgroup:GetGroup():SetOptionRadarUsingForContinousSearch()
flightgroup:GetGroup():SetOptionLandingOverheadBreak()
if Mission.type ~= AUFTRAG.Type.TANKER and Mission.type ~= AUFTRAG.Type.AWACS and Mission.type ~= AUFTRAG.Type.RECON then
flightgroup:SetDetection(true)
flightgroup:SetEngageDetectedOn(engagerange,{"Air"},GoZoneSet,NoGoZoneSet)
flightgroup:SetEngageDetectedOn(engagerange,EngageTypes,GoZoneSet,NoGoZoneSet)
flightgroup:SetOutOfAAMRTB()
flightgroup:SetFuelLowRTB(true)
flightgroup:SetFuelLowThreshold(FuelLow)
flightgroup:SetFuelCriticalRTB(true)
flightgroup:SetFuelCriticalThreshold(FuelCritical)
if CapFormation then
flightgroup:GetGroup():SetOption(AI.Option.Air.id.FORMATION,CapFormation)
end
@@ -674,24 +779,30 @@ end
--- Add a CAP patrol point to a Wing
-- @param #EASYGCICAP self
-- @param #string AirbaseName Name of the Wing's airbase
-- @param Core.Point#COORDINATE Coordinate.
-- @param Core.Point#COORDINATE Coordinate. Can be handed as a Core.Zone#ZONE object (e.g. in case you want the point to align with a moving zone).
-- @param #number Altitude Defaults to 25000 feet ASL.
-- @param #number Speed Defaults to 300 knots TAS.
-- @param #number Heading Defaults to 90 degrees (East).
-- @param #number LegLength Defaults to 15 NM.
-- @return #EASYGCICAP self
function EASYGCICAP:AddPatrolPointCAP(AirbaseName,Coordinate,Altitude,Speed,Heading,LegLength)
self:T(self.lid.."AddPatrolPointCAP "..Coordinate:ToStringLLDDM())
local EntryCAP = {} -- #EASYGCICAP.CapPoint
self:T(self.lid.."AddPatrolPointCAP")--..Coordinate:ToStringLLDDM())
local coordinate = Coordinate
local EntryCAP = {} -- #EASYGCICAP.CapPoint
if Coordinate:IsInstanceOf("ZONE_BASE") then
-- adjust coordinate and get the coordinate from the zone
coordinate = Coordinate:GetCoordinate()
EntryCAP.Zone = Coordinate
end
EntryCAP.AirbaseName = AirbaseName
EntryCAP.Coordinate = Coordinate
EntryCAP.Coordinate = coordinate
EntryCAP.Altitude = Altitude or 25000
EntryCAP.Speed = Speed or 300
EntryCAP.Heading = Heading or 90
EntryCAP.LegLength = LegLength or 15
self.ManagedCP[#self.ManagedCP+1] = EntryCAP
if self.debug then
local mark = MARKER:New(Coordinate,self.lid.."Patrol Point"):ToAll()
local mark = MARKER:New(coordinate,self.lid.."Patrol Point"):ToAll()
end
return self
end
@@ -699,7 +810,7 @@ end
--- Add a RECON patrol point to a Wing
-- @param #EASYGCICAP self
-- @param #string AirbaseName Name of the Wing's airbase
-- @param Core.Point#COORDINATE Coordinate.
-- @param Core.Point#COORDINATE Coordinate. Can be handed as a Core.Zone#ZONE object (e.g. in case you want the point to align with a moving zone).
-- @param #number Altitude Defaults to 25000 feet.
-- @param #number Speed Defaults to 300 knots.
-- @param #number Heading Defaults to 90 degrees (East).
@@ -724,7 +835,7 @@ end
--- Add a TANKER patrol point to a Wing
-- @param #EASYGCICAP self
-- @param #string AirbaseName Name of the Wing's airbase
-- @param Core.Point#COORDINATE Coordinate.
-- @param Core.Point#COORDINATE Coordinate. Can be handed as a Core.Zone#ZONE object (e.g. in case you want the point to align with a moving zone).
-- @param #number Altitude Defaults to 25000 feet.
-- @param #number Speed Defaults to 300 knots.
-- @param #number Heading Defaults to 90 degrees (East).
@@ -749,7 +860,7 @@ end
--- Add an AWACS patrol point to a Wing
-- @param #EASYGCICAP self
-- @param #string AirbaseName Name of the Wing's airbase
-- @param Core.Point#COORDINATE Coordinate.
-- @param Core.Point#COORDINATE Coordinate. Can be handed as a Core.Zone#ZONE object (e.g. in case you want the point to align with a moving zone).
-- @param #number Altitude Defaults to 25000 feet.
-- @param #number Speed Defaults to 300 knots.
-- @param #number Heading Defaults to 90 degrees (East).
@@ -778,6 +889,11 @@ function EASYGCICAP:_SetTankerPatrolPoints()
self:T(self.lid.."_SetTankerPatrolPoints")
for _,_data in pairs(self.ManagedTK) do
local data = _data --#EASYGCICAP.CapPoint
self:T("Airbasename = "..data.AirbaseName)
if not self.wings[data.AirbaseName] then
MESSAGE:New(self.lid.."You are trying to create a TANKER point for which there is no wing! "..tostring(data.AirbaseName),30,"CHECK"):ToAllIf(self.debug):ToLog()
return
end
local Wing = self.wings[data.AirbaseName][1] -- Ops.Airwing#AIRWING
local Coordinate = data.Coordinate
local Altitude = data.Altitude
@@ -797,6 +913,11 @@ function EASYGCICAP:_SetAwacsPatrolPoints()
self:T(self.lid.."_SetAwacsPatrolPoints")
for _,_data in pairs(self.ManagedEWR) do
local data = _data --#EASYGCICAP.CapPoint
self:T("Airbasename = "..data.AirbaseName)
if not self.wings[data.AirbaseName] then
MESSAGE:New(self.lid.."You are trying to create an AWACS point for which there is no wing! "..tostring(data.AirbaseName),30,"CHECK"):ToAllIf(self.debug):ToLog()
return
end
local Wing = self.wings[data.AirbaseName][1] -- Ops.Airwing#AIRWING
local Coordinate = data.Coordinate
local Altitude = data.Altitude
@@ -816,13 +937,23 @@ function EASYGCICAP:_SetCAPPatrolPoints()
self:T(self.lid.."_SetCAPPatrolPoints")
for _,_data in pairs(self.ManagedCP) do
local data = _data --#EASYGCICAP.CapPoint
self:T("Airbasename = "..data.AirbaseName)
if not self.wings[data.AirbaseName] then
MESSAGE:New(self.lid.."You are trying to create a CAP point for which there is no wing! "..tostring(data.AirbaseName),30,"CHECK"):ToAllIf(self.debug):ToLog()
return
end
local Wing = self.wings[data.AirbaseName][1] -- Ops.Airwing#AIRWING
local Coordinate = data.Coordinate
local Altitude = data.Altitude
local Speed = data.Speed
local Heading = data.Heading
local LegLength = data.LegLength
Wing:AddPatrolPointCAP(Coordinate,Altitude,Speed,Heading,LegLength)
local Zone = _data.Zone
if Zone then
Wing:AddPatrolPointCAP(Zone,Altitude,Speed,Heading,LegLength)
else
Wing:AddPatrolPointCAP(Coordinate,Altitude,Speed,Heading,LegLength)
end
end
return self
@@ -835,6 +966,11 @@ function EASYGCICAP:_SetReconPatrolPoints()
self:T(self.lid.."_SetReconPatrolPoints")
for _,_data in pairs(self.ManagedREC) do
local data = _data --#EASYGCICAP.CapPoint
self:T("Airbasename = "..data.AirbaseName)
if not self.wings[data.AirbaseName] then
MESSAGE:New(self.lid.."You are trying to create a RECON point for which there is no wing! "..tostring(data.AirbaseName),30,"CHECK"):ToAllIf(self.debug):ToLog()
return
end
local Wing = self.wings[data.AirbaseName][1] -- Ops.Airwing#AIRWING
local Coordinate = data.Coordinate
local Altitude = data.Altitude
@@ -883,7 +1019,7 @@ end
-- @param #string SquadName Squadron name - must be unique!
-- @param #string AirbaseName Name of the airbase the airwing resides on, e.g. AIRBASE.Caucasus.Kutaisi
-- @param #number AirFrames Number of available airframes, e.g. 20.
-- @param #string Skill(optional) Skill level, e.g. AI.Skill.AVERAGE
-- @param #string Skill (optional) Skill level, e.g. AI.Skill.AVERAGE
-- @param #string Modex (optional) Modex to be used,e.g. 402.
-- @param #string Livery (optional) Livery name to be used.
-- @return #EASYGCICAP self
@@ -1088,7 +1224,9 @@ function EASYGCICAP:_AddTankerSquadron(TemplateName, SquadName, AirbaseName, Air
Squadron_One:SetSkill(Skill or AI.Skill.AVERAGE)
Squadron_One:SetMissionRange(self.missionrange)
Squadron_One:SetRadio(Frequency,Modulation)
Squadron_One:AddTacanChannel(TACAN,TACAN)
if TACAN then
Squadron_One:AddTacanChannel(TACAN,TACAN)
end
local wing = self.wings[AirbaseName][1] -- Ops.Airwing#AIRWING
@@ -1172,19 +1310,19 @@ end
-- @return #boolean assigned
-- @return #number leftover
function EASYGCICAP:_TryAssignIntercept(ReadyFlightGroups,InterceptAuftrag,Group,WingSize)
self:I("_TryAssignIntercept for size "..WingSize or 1)
self:T("_TryAssignIntercept for size "..WingSize or 1)
local assigned = false
local wingsize = WingSize or 1
local mindist = 0
local disttable = {}
if Group and Group:IsAlive() then
local gcoord = Group:GetCoordinate() or COORDINATE:New(0,0,0)
self:I(self.lid..string.format("Assignment for %s",Group:GetName()))
self:T(self.lid..string.format("Assignment for %s",Group:GetName()))
for _name,_FG in pairs(ReadyFlightGroups or {}) do
local FG = _FG -- Ops.FlightGroup#FLIGHTGROUP
local fcoord = FG:GetCoordinate()
local dist = math.floor(UTILS.Round(fcoord:Get2DDistance(gcoord)/1000,1))
self:I(self.lid..string.format("FG %s Distance %dkm",_name,dist))
self:T(self.lid..string.format("FG %s Distance %dkm",_name,dist))
disttable[#disttable+1] = { FG=FG, dist=dist}
if dist>mindist then mindist=dist end
end
@@ -1201,7 +1339,7 @@ function EASYGCICAP:_TryAssignIntercept(ReadyFlightGroups,InterceptAuftrag,Group
local cm = FG:GetMissionCurrent()
if cm then cm:Cancel() end
wingsize = wingsize - 1
self:I(self.lid..string.format("Assigned to FG %s Distance %dkm",FG:GetName(),_entry.dist))
self:T(self.lid..string.format("Assigned to FG %s Distance %dkm",FG:GetName(),_entry.dist))
if wingsize == 0 then
assigned = true
break
@@ -1231,7 +1369,7 @@ function EASYGCICAP:_AssignIntercept(Cluster)
local conflictzoneset = self.ConflictZoneSet
local ReadyFlightGroups = self.ReadyFlightGroups
-- Aircraft?
-- Aircraft?
if Cluster.ctype ~= INTEL.Ctype.AIRCRAFT then return end
-- Threatlevel 0..10
local contact = self.Intel:GetHighestThreatContact(Cluster)
@@ -1276,6 +1414,10 @@ function EASYGCICAP:_AssignIntercept(Cluster)
local data = _data -- #EASYGCICAP.CapPoint
local name = data.AirbaseName
local zonecoord = data.Coordinate
if data.Zone then
-- refresh coordinate in case we have a (moving) zone
zonecoord = data.Zone:GetCoordinate()
end
local airwing = wings[name][1]
local coa = AIRBASE:FindByName(name):GetCoalition()
local samecoalitionab = coa == self.coalition and true or false
@@ -1377,7 +1519,7 @@ function EASYGCICAP:_StartIntel()
end
-------------------------------------------------------------------------
-- FSM Functions
-- TODO FSM Functions
-------------------------------------------------------------------------
--- (Internal) FSM Function onafterStart
@@ -1473,7 +1615,7 @@ function EASYGCICAP:onafterStatus(From,Event,To)
local engage = FG:IsEngaging()
local hasmissiles = FG:IsOutOfMissiles() == nil and true or false
local ready = hasmissiles and FG:IsFuelGood() and FG:IsAirborne()
--self:I(string.format("Flightgroup %s Engaging = %s Ready = %s",tostring(name),tostring(engage),tostring(ready)))
--self:T(string.format("Flightgroup %s Engaging = %s Ready = %s",tostring(name),tostring(engage),tostring(ready)))
if ready then
self.ReadyFlightGroups[name] = FG
end
@@ -1508,5 +1650,8 @@ end
function EASYGCICAP:onafterStop(From,Event,To)
self:T({From,Event,To})
self.Intel:Stop()
for _,_wing in pairs(self.wings or {}) do
_wing:Stop()
end
return self
end
File diff suppressed because it is too large Load Diff
+140 -7
View File
@@ -60,6 +60,8 @@
-- @field #boolean jettisonEmptyTanks Allow (true) or disallow (false) AI to jettison empty fuel tanks.
-- @field #boolean jettisonWeapons Allow (true) or disallow (false) AI to jettison weapons if in danger.
-- @field #number holdtime Time [s] flight is holding before going on final. Set to nil for indefinitely.
-- @field #table flightplans Flight plans for this group.
-- @field Navigation.FlightPlan#FLIGHTPLAN flightplan Currently active flight plan.
--
-- @extends Ops.OpsGroup#OPSGROUP
@@ -151,7 +153,8 @@ FLIGHTGROUP = {
playerWarnings = {},
prohibitAB = false,
jettisonEmptyTanks = true,
jettisonWeapons = true, -- that's actually a negative option like prohibitAB
jettisonWeapons = true, -- that's actually a negative option like prohibitAB
flightplans = {},
}
@@ -779,6 +782,61 @@ function FLIGHTGROUP:SetJettisonWeapons(Switch)
return self
end
--- Set the aircraft to land straight in.
-- @param #FLIGHTGROUP self
-- @return #FLIGHTGROUP self
function FLIGHTGROUP:SetOptionLandingStraightIn()
self.OptionLandingStraightIn = true
if self:GetGroup():IsAlive() then
self:GetGroup():SetOptionLandingStraightIn()
end
return self
end
--- Set the aircraft to land in pairs.
-- @param #FLIGHTGROUP self
-- @return #FLIGHTGROUP self
function FLIGHTGROUP:SetOptionLandingForcePair()
self.OptionLandingForcePair = true
if self:GetGroup():IsAlive() then
self:GetGroup():SetOptionLandingForcePair()
end
return self
end
--- Set the aircraft to NOT land in pairs.
-- @param #FLIGHTGROUP self
-- @return #FLIGHTGROUP self
function FLIGHTGROUP:SetOptionLandingRestrictPair()
self.OptionLandingRestrictPair = true
if self:GetGroup():IsAlive() then
self:GetGroup():SetOptionLandingRestrictPair()
end
return self
end
--- Set the aircraft to land after overhead break.
-- @param #FLIGHTGROUP self
-- @return #FLIGHTGROUP self
function FLIGHTGROUP:SetOptionLandingOverheadBreak()
self.OptionLandingOverheadBreak = true
if self:GetGroup():IsAlive() then
self:GetGroup():SetOptionLandingOverheadBreak()
end
return self
end
--- [HELICOPTER] Set the aircraft to prefer takeoff and landing vertically.
-- @param #FLIGHTGROUP self
-- @return #FLIGHTGROUP self
function FLIGHTGROUP:SetOptionPreferVertical()
self.OptionPreferVertical = true
if self:GetGroup():IsAlive() then
self:GetGroup():OptionPreferVerticalLanding()
end
return self
end
--- Set if group is ready for taxi/takeoff if controlled by a `FLIGHTCONTROL`.
-- @param #FLIGHTGROUP self
-- @param #boolean ReadyTO If `true`, flight is ready for takeoff.
@@ -830,6 +888,17 @@ function FLIGHTGROUP:GetFlightControl()
return self.flightcontrol
end
--- Add flightplan.
-- @param #FLIGHTGROUP self
-- @param Navigation.FlightPlan#FLIGHTPLAN FlightPlan The flight plan.
-- @return #FLIGHTGROUP self
function FLIGHTGROUP:AddFlightPlan(FlightPlan)
self:T(self.lid..string.format("Adding flight plan %s", FlightPlan:GetName() ))
table.insert(self.flightplans, FlightPlan)
return self
end
--- Set the homebase.
-- @param #FLIGHTGROUP self
@@ -1654,6 +1723,10 @@ function FLIGHTGROUP:Status()
-- TODO: Not sure why I introduced this here.
self:_CheckGroupDone()
end
if not (self.cargoTransport or mission) then
self:_CheckFlightPlans()
end
end
@@ -2002,6 +2075,9 @@ function FLIGHTGROUP:onafterElementAirborne(From, Event, To, Element)
-- Debug info.
self:T2(self.lid..string.format("Element airborne %s", Element.name))
-- Set parking spot to free. Also for FC. This is usually done after taxiing but doing it here in case the group is teleported.
self:_SetElementParkingFree(Element)
-- Set element status.
self:_UpdateStatus(Element, OPSGROUP.ElementStatus.AIRBORNE)
@@ -3076,7 +3152,7 @@ function FLIGHTGROUP:onbeforeLandAtAirbase(From, Event, To, airbase)
local Tsuspend=nil
if airbase==nil then
self:T(self.lid.."ERROR: Airbase is nil in LandAtAirase() call!")
self:T(self.lid.."ERROR: Airbase is nil in LandAtAirbase() call!")
allowed=false
end
@@ -3741,6 +3817,56 @@ function FLIGHTGROUP:onafterFuelCritical(From, Event, To)
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Flightplan Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Check flightplans.
-- @param #FLIGHTGROUP self
-- @param Navigation.FlightPlan#FLIGHTPLAN FlightPlan Flight plan.
-- @return #FLIGHTGROUP self
function FLIGHTGROUP:_SetFlightPlan(FlightPlan)
self.flightplan=FlightPlan
for i,_fix in pairs(FlightPlan.fixes) do
local fix=_fix --Navigation.NavFix#NAVFIX
--fix.coordinate
local speed=fix.altMin
local altitude=(fix.altMin or fix.altMax)~=nil and fix:GetAltitude() or FlightPlan:GetCruiseAltitude()
local wp=self:AddWaypoint(fix.coordinate, Speed, AfterWaypointWithID, altitude or 10000, false)
end
end
--- Check flightplans.
-- @param #FLIGHTGROUP self
-- @return #FLIGHTGROUP self
function FLIGHTGROUP:_CheckFlightPlans()
if self.flightplan then
return
end
for i,_flightplan in pairs(self.flightplans) do
local flightplan=_flightplan --Navigation.FlightPlan#FLIGHTPLAN
if flightplan then
self:_SetFlightPlan(flightplan)
break
end
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Task functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -4494,6 +4620,11 @@ function FLIGHTGROUP:GetParkingSpot(element, maxdist, airbase)
-- Airbase.
airbase=airbase or self:GetClosestAirbase()
if airbase == nil then
self:T(self.lid.."No airbase found for element "..element.name)
return nil
end
-- Parking table of airbase.
local parking=airbase.parking --:GetParkingSpotsTable()
@@ -4604,10 +4735,12 @@ function FLIGHTGROUP:GetParking(airbase)
local coords={}
for clientname, client in pairs(clients) do
local template=_DATABASE:GetGroupTemplateFromUnitName(clientname)
local units=template.units
for i,unit in pairs(units) do
local coord=COORDINATE:New(unit.x, unit.alt, unit.y)
coords[unit.name]=coord
if template then
local units=template.units
for i,unit in pairs(units) do
local coord=COORDINATE:New(unit.x, unit.alt, unit.y)
coords[unit.name]=coord
end
end
end
return coords
@@ -4964,7 +5097,7 @@ function FLIGHTGROUP:_UpdateMenu(delay)
-- Message to group.
MESSAGE:New(text, 5):ToGroup(self.group)
self:I(self.lid..text)
self:T(self.lid..text)
end
-- Get current position of player.
+27 -9
View File
@@ -662,6 +662,15 @@ function LEGION:CheckMissionQueue()
if mission:IsNotOver() and mission:IsReadyToCancel() then
mission:Cancel()
end
-- Housekeeping
local TNow = timer.getTime()
if mission:IsOver() and mission:IsNotRepeatable() and mission.DeletionTimstamp == nil then
mission.DeletionTimstamp = TNow
end
if mission.DeletionTimstamp ~= nil and TNow - mission.DeletionTimstamp > 1800 then
mission = nil
end
end
-- Check that runway is operational and that carrier is not recovering.
@@ -761,7 +770,7 @@ function LEGION:CheckMissionQueue()
-- Reduce number of reinforcements.
if reinforce then
mission.reinforce=mission.reinforce-#assets
self:I(self.lid..string.format("Reinforced with N=%d Nreinforce=%d", #assets, mission.reinforce))
self:T(self.lid..string.format("Reinforced with N=%d Nreinforce=%d", #assets, mission.reinforce))
end
return true
@@ -1823,6 +1832,7 @@ function LEGION:_CreateFlightGroup(asset)
---
opsgroup=ARMYGROUP:New(asset.spawngroupname)
opsgroup:SetValidateAndRepositionGroundUnits(self.ValidateAndRepositionGroundUnits)
elseif self:IsFleet() then
@@ -1891,7 +1901,7 @@ function LEGION:_TacticalOverview()
for _,mtype in pairs(AUFTRAG.Type) do
local n=self:CountMissionsInQueue(mtype)
if n>0 then
local N=self:CountMissionsInQueue(mtype)
local N=self:CountMissionsInQueue(mtype, true)
text=text..string.format(" - %s: %d [Running=%d]\n", mtype, n, N)
end
end
@@ -2047,18 +2057,23 @@ end
--- Count missions in mission queue.
-- @param #LEGION self
-- @param #table MissionTypes Types on mission to be checked. Default *all* possible types `AUFTRAG.Type`.
-- @param #boolean OnlyRunning If `true`, only count running missions.
-- @return #number Number of missions that are not over yet.
function LEGION:CountMissionsInQueue(MissionTypes)
function LEGION:CountMissionsInQueue(MissionTypes, OnlyRunning)
MissionTypes=MissionTypes or AUFTRAG.Type
local N=0
for _,_mission in pairs(self.missionqueue) do
local mission=_mission --Ops.Auftrag#AUFTRAG
if (not OnlyRunning) or (mission.statusLegion~=AUFTRAG.Status.PLANNED) then
-- Check if this mission type is requested.
if mission:IsNotOver() and AUFTRAG.CheckMissionType(mission.type, MissionTypes) then
N=N+1
-- Check if this mission type is requested.
if mission:IsNotOver() and AUFTRAG.CheckMissionType(mission.type, MissionTypes) then
N=N+1
end
end
end
@@ -2513,9 +2528,12 @@ function LEGION._GetCohorts(Legions, Cohorts, Operation, OpsQueue)
for _,_legion in pairs(Legions or {}) do
local legion=_legion --Ops.Legion#LEGION
-- Check that runway is operational.
local Runway=legion:IsAirwing() and legion:IsRunwayOperational() or true
-- Check that runway is operational.
local Runway=true
if legion:IsAirwing() then
Runway=legion:IsRunwayOperational() and legion.airbase and legion.airbase:GetCoalition() == legion:GetCoalition()
end
-- Legion has to be running.
if legion:IsRunning() and Runway then
+25 -19
View File
@@ -86,12 +86,14 @@ NAVYGROUP = {
-- @field Ops.Target#TARGET Target The target.
-- @field Core.Point#COORDINATE Coordinate Last known coordinate of the target.
-- @field Ops.OpsGroup#OPSGROUP.Waypoint Waypoint the waypoint created to go to the target.
-- @field #number Speed Speed in knots.
-- @field #number Depth Depth of the engagement (submarines).
-- @field #number roe ROE backup.
-- @field #number alarmstate Alarm state backup.
--- NavyGroup version.
-- @field #string version
NAVYGROUP.version="1.0.3"
NAVYGROUP.version="1.0.4"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@@ -1081,7 +1083,7 @@ function NAVYGROUP:onafterSpawned(From, Event, To)
text=text..string.format("Elements = %d\n", #self.elements)
text=text..string.format("Waypoints = %d\n", #self.waypoints)
text=text..string.format("Radio = %.1f MHz %s %s\n", self.radio.Freq, UTILS.GetModulationName(self.radio.Modu), tostring(self.radio.On))
text=text..string.format("Ammo = %d (G=%d/R=%d/M=%d/T=%d)\n", self.ammo.Total, self.ammo.Guns, self.ammo.Rockets, self.ammo.Missiles, self.ammo.Torpedos)
text=text..string.format("Ammo = %d (G=%d/C=%d/R=%d/M=%d/T=%d)\n", self.ammo.Total, self.ammo.Guns, self.ammo.Cannons, self.ammo.Rockets, self.ammo.Missiles, self.ammo.Torpedos)
text=text..string.format("FSM state = %s\n", self:GetState())
text=text..string.format("Is alive = %s\n", tostring(self:IsAlive()))
text=text..string.format("LateActivate = %s\n", tostring(self:IsLateActivated()))
@@ -1602,8 +1604,10 @@ end
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Wrapper.Group#GROUP Group the group to be engaged.
function NAVYGROUP:onafterEngageTarget(From, Event, To, Target)
-- @param Ops.Target#TARGET Target The target to be engaged. Can also be a GROUP or UNIT object.
-- @param #number Speed Attack speed in knots.
-- @param #number Depth The depth in meters. Only for submarins.
function NAVYGROUP:onafterEngageTarget(From, Event, To, Target, Speed, Depth)
self:T(self.lid.."Engaging Target")
if Target:IsInstanceOf("TARGET") then
@@ -1615,11 +1619,9 @@ function NAVYGROUP:onafterEngageTarget(From, Event, To, Target)
-- Target coordinate.
self.engage.Coordinate=UTILS.DeepCopy(self.engage.Target:GetCoordinate())
local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.9)
-- Get a coordinate close to the target.
local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.8)
-- Backup ROE and alarm state.
self.engage.roe=self:GetROE()
self.engage.alarmstate=self:GetAlarmstate()
@@ -1629,11 +1631,17 @@ function NAVYGROUP:onafterEngageTarget(From, Event, To, Target)
self:SwitchROE(ENUMS.ROE.OpenFire)
-- ID of current waypoint.
local uid=self:GetWaypointCurrent().uid
local uid=self:GetWaypointCurrentUID()
-- Set formation.
self.engage.Depth=Depth or 0
-- Set speed.
self.engage.Speed=Speed
-- Add waypoint after current.
self.engage.Waypoint=self:AddWaypoint(intercoord, nil, uid, Formation, true)
self.engage.Waypoint=self:AddWaypoint(intercoord, Speed, uid, Depth, true)
-- Set if we want to resume route after reaching the detour waypoint.
self.engage.Waypoint.detour=1
@@ -1656,24 +1664,22 @@ function NAVYGROUP:_UpdateEngageTarget()
-- Check if target moved more than 100 meters.
if dist>100 then
--env.info("FF Update Engage Target Moved "..self.engage.Target:GetName())
-- Update new position.
self.engage.Coordinate:UpdateFromVec3(vec3)
-- ID of current waypoint.
local uid=self:GetWaypointCurrent().uid
local uid=self:GetWaypointCurrentUID()
-- Remove current waypoint
self:RemoveWaypointByID(self.engage.Waypoint.uid)
local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.9)
local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.8)
-- Add waypoint after current.
self.engage.Waypoint=self:AddWaypoint(intercoord, nil, uid, Formation, true)
self.engage.Waypoint=self:AddWaypoint(intercoord, self.engage.Speed, uid, self.engage.Depth, true)
-- Set if we want to resume route after reaching the detour waypoint.
self.engage.Waypoint.detour=0
self.engage.Waypoint.detour=1
end
@@ -1709,8 +1715,8 @@ function NAVYGROUP:onafterDisengage(From, Event, To)
local task=self:GetTaskCurrent()
-- Get if current task is ground attack.
if task and task.dcstask.id==AUFTRAG.SpecialTask.GROUNDATTACK then
self:T(self.lid.."Disengage with current task GROUNDATTACK ==> Task Done!")
if task and (task.dcstask.id==AUFTRAG.SpecialTask.GROUNDATTACK or task.dcstask.id==AUFTRAG.SpecialTask.NAVALENGAGEMENT) then
self:T(self.lid.."Disengage with current task GROUNDATTACK/NAVALENGAGEMENT ==> Task Done!")
self:TaskDone(task)
end
+134 -35
View File
@@ -405,7 +405,9 @@ OPSGROUP.TaskType={
--- Ammo data.
-- @type OPSGROUP.Ammo
-- @field #number Total Total amount of ammo.
-- @field #number Guns Amount of gun shells.
-- @field #number Shells Amount of shells (guns + cannons).
-- @field #number Guns Amount of gun shells (caliber < 25).
-- @field #number Cannons Amount of cannon shells (caliber >= 25).
-- @field #number Bombs Amount of bombs.
-- @field #number Rockets Amount of rockets.
-- @field #number Torpedos Amount of torpedos.
@@ -445,6 +447,7 @@ OPSGROUP.TaskType={
-- @field Wrapper.Marker#MARKER marker Marker on the F10 map.
-- @field #string formation Ground formation. Similar to action but on/off road.
-- @field #number missionUID Mission UID (Auftragsnr) this waypoint belongs to.
-- @field Navigation.FlightPlan#FLIGHTPLAN flightplan Flightplan this waypoint belongs to.
--- Cargo Carrier status.
-- @type OPSGROUP.CarrierStatus
@@ -573,6 +576,11 @@ function OPSGROUP:New(group)
-- Set DCS group and controller.
self.dcsgroup=self:GetDCSGroup()
if not self.dcsgroup then
return
end
self.controller=self.dcsgroup:getController()
-- Category.
@@ -1189,8 +1197,8 @@ function OPSGROUP:GetDCSObject()
return self.dcsgroup
end
--- Set detection on or off.
-- If detection is on, detected targets of the group will be evaluated and FSM events triggered.
--- Make a target (unit, group, opsgroup) known to this group.
-- This is useing the DCS function `controller.knowTarget`.
-- @param #OPSGROUP self
-- @param Wrapper.Positionable#POSITIONABLE TargetObject The target object.
-- @param #boolean KnowType Make type known.
@@ -4300,7 +4308,27 @@ function OPSGROUP:_UpdateTask(Task, Mission)
Mission=Mission or self:GetMissionByTaskID(self.taskcurrent)
if Task.dcstask.id==AUFTRAG.SpecialTask.FORMATION then
if Mission.type == AUFTRAG.Type.RESCUEHELO then
self:T("**********")
self:T("** RESCUEHELO USED")
self:T("**********")
local param=Task.dcstask.params
local followUnit=UNIT:FindByName(param.unitname)
local helogroupname = self:GetGroup():GetName()
Task.formation = RESCUEHELO:New(followUnit,helogroupname)
Task.formation:SetRespawnOnOff(false)
Task.formation.respawninair=false
Task.formation:SetTakeoffCold()
Task.formation:SetHomeBase(followUnit)
Task.formation.helo = self:GetGroup()
-- Start formation FSM.
Task.formation:Start()
if self:IsFlightgroup() then
self:SetDespawnAfterLanding()
end
else
-- Set of group(s) to follow Mother.
local followSet=SET_GROUP:New():AddGroup(self.group)
@@ -4309,7 +4337,7 @@ function OPSGROUP:_UpdateTask(Task, Mission)
local followUnit=UNIT:FindByName(param.unitname)
-- Define AI Formation object.
Task.formation=AI_FORMATION:New(followUnit, followSet, AUFTRAG.SpecialTask.FORMATION, "Follow X at given parameters.")
Task.formation=FORMATION:New(followUnit, followSet, AUFTRAG.SpecialTask.FORMATION)
-- Formation parameters.
Task.formation:FormationCenterWing(-param.offsetX, 50, math.abs(param.altitude), 50, param.offsetZ, 50)
@@ -4318,11 +4346,13 @@ function OPSGROUP:_UpdateTask(Task, Mission)
Task.formation:SetFollowTimeInterval(param.dtFollow)
-- Formation mode.
Task.formation:SetFlightModeFormation(self.group)
--Task.formation:SetFlightModeFormation(self.group)
-- Start formation FSM.
Task.formation:Start()
end
elseif Task.dcstask.id==AUFTRAG.SpecialTask.PATROLZONE then
---
@@ -4513,6 +4543,25 @@ function OPSGROUP:_UpdateTask(Task, Mission)
if target then
self:EngageTarget(target, speed, Task.dcstask.params.formation)
end
elseif Task.dcstask.id==AUFTRAG.SpecialTask.NAVALENGAGEMENT then
---
-- Task "Naval Engagement" Mission.
---
-- Engage target.
local target=Task.dcstask.params.target --Ops.Target#TARGET
-- Set speed. Default max.
local speed=self.speedMax and UTILS.KmphToKnots(self.speedMax) or nil
if Task.dcstask.params.speed then
speed=UTILS.MpsToKnots(Task.dcstask.params.speed)
end
if target then
self:EngageTarget(target, speed, Task.dcstask.params.altitude)
end
elseif Task.dcstask.id==AUFTRAG.SpecialTask.PATROLRACETRACK then
@@ -4631,7 +4680,12 @@ function OPSGROUP:_UpdateTask(Task, Mission)
self:T(self.lid..string.format("Zone %s captured ==> Task DONE!", zoneCurr:GetName()))
-- Task done.
self:TaskDone(Task)
if Task.StayInZoneTime then
local stay = Task.StayInZoneTime
self:__TaskDone(stay,Task)
else
self:TaskDone(Task)
end
else
-- Current zone NOT captured yet ==> Find Target
@@ -4734,7 +4788,7 @@ function OPSGROUP:_UpdateTask(Task, Mission)
elseif weaponType==ENUMS.WeaponFlag.AnyRocket then
nAmmo=ammo.Rockets
elseif weaponType==ENUMS.WeaponFlag.Cannons then
nAmmo=ammo.Guns
nAmmo=ammo.Cannons
end
--TODO: Update target location while we're at it anyway.
@@ -4881,7 +4935,7 @@ function OPSGROUP:onafterTaskCancel(From, Event, To, Task)
done=true
elseif Task.dcstask.id==AUFTRAG.SpecialTask.ONGUARD or Task.dcstask.id==AUFTRAG.SpecialTask.ARMOREDGUARD then
done=true
elseif Task.dcstask.id==AUFTRAG.SpecialTask.GROUNDATTACK or Task.dcstask.id==AUFTRAG.SpecialTask.ARMORATTACK then
elseif Task.dcstask.id==AUFTRAG.SpecialTask.GROUNDATTACK or Task.dcstask.id==AUFTRAG.SpecialTask.ARMORATTACK or Task.dcstask.id==AUFTRAG.SpecialTask.NAVALENGAGEMENT then
done=true
elseif Task.dcstask.id==AUFTRAG.SpecialTask.NOTHING then
done=true
@@ -5589,10 +5643,13 @@ function OPSGROUP:onafterUnpauseMission(From, Event, To)
-- Debug info.
self:T(self.lid..string.format("Unpausing mission %s [%s]", mission:GetName(), mission:GetType()))
-- Set state of mission, e.g. for not teleporting again
mission.unpaused=true
-- Start mission.
self:MissionStart(mission)
-- Remove mission from
-- Remove mission from pausedmissions queue
for i,mid in pairs(self.pausedmissions) do
--self:T(self.lid..string.format("Checking paused mission", mid))
if mid==mission.auftragsnummer then
@@ -5710,7 +5767,7 @@ end
function OPSGROUP:onafterMissionDone(From, Event, To, Mission)
-- Debug info.
local text=string.format("Mission %s DONE!", Mission.name)
local text=string.format("Mission DONE %s!", Mission.name)
self:T(self.lid..text)
-- Set group status.
@@ -5727,7 +5784,7 @@ function OPSGROUP:onafterMissionDone(From, Event, To, Mission)
-- Decrease patrol data.
if Mission.patroldata then
Mission.patroldata.noccupied=Mission.patroldata.noccupied-1
AIRWING.UpdatePatrolPointMarker(Mission.patroldata)
AIRWING.UpdatePatrolPointMarker(self,Mission.patroldata)
end
-- Switch auto engage detected off. This IGNORES that engage detected had been activated for the group!
@@ -6223,16 +6280,17 @@ function OPSGROUP:RouteToMission(mission, delay)
self:T(self.lid.."Already in mission zone ==> TaskExecute()")
self:TaskExecute(waypointtask)
-- TODO: Calling PassingWaypoint here is probably better as it marks the mission waypoint as passed!
--self:PassingWaypoint(waypoint)
self:PassingWaypoint(waypoint)
return
elseif d<25 then
self:T(self.lid.."Already within 25 meters of mission waypoint ==> TaskExecute()")
self:TaskExecute(waypointtask)
self:PassingWaypoint(waypoint)
return
end
-- Check if group is mobile. Note that some immobile units report a speed of 1 m/s = 3.6 km/h.
if self.speedMax<=3.6 or mission.teleport then
if (self.speedMax<=3.6 or mission.teleport) and not mission.unpaused then
-- Teleport to waypoint coordinate. Mission will not be paused.
self:Teleport(waypointcoord, nil, true)
@@ -7531,7 +7589,7 @@ end
function OPSGROUP:onafterElementDead(From, Event, To, Element)
-- Debug info.
self:I(self.lid..string.format("Element dead %s at t=%.3f", Element.name, timer.getTime()))
self:T(self.lid..string.format("Element dead %s at t=%.3f", Element.name, timer.getTime()))
-- Set element status.
self:_UpdateStatus(Element, OPSGROUP.ElementStatus.DEAD)
@@ -7842,10 +7900,15 @@ function OPSGROUP:_Spawn(Delay, Template)
self:ScheduleOnce(Delay, OPSGROUP._Spawn, self, 0, Template)
else
-- Debug output.
self:T2({Template=Template})
--self:T2({Template=Template})
if self:IsArmygroup() and self.ValidateAndRepositionGroundUnits then
UTILS.ValidateAndRepositionGroundUnits(Template.units)
end
-- Spawn new group.
self.group=_DATABASE:Spawn(Template)
self.group:SetValidateAndRepositionGroundUnits(self.ValidateAndRepositionGroundUnits)
--local countryID=self.group:GetCountry()
--local categoryID=self.group:GetCategory()
--local dcsgroup=coalition.addGroup(countryID, categoryID, Template)
@@ -7985,11 +8048,16 @@ function OPSGROUP:onafterDead(From, Event, To)
-- Get asset.
local asset=self.legion:GetAssetByName(self.groupname)
if asset then
-- Get request.
local request=self.legion:GetRequestByID(asset.rid)
-- Trigger asset dead event.
self.legion:AssetDead(asset, request)
end
end
-- Stop in 5 sec to give possible respawn attempts a chance.
@@ -8082,7 +8150,7 @@ function OPSGROUP:onafterStop(From, Event, To)
_DATABASE.FLIGHTGROUPS[self.groupname]=nil
-- Debug output.
self:I(self.lid.."STOPPED! Unhandled events, cleared scheduler and removed from _DATABASE")
self:T(self.lid.."STOPPED! Unhandled events, cleared scheduler and removed from _DATABASE")
end
--- On after "OutOfAmmo" event.
@@ -11177,11 +11245,11 @@ function OPSGROUP:_CheckAmmoStatus()
self:OutOfAmmo()
end
-- Guns.
if self.outofGuns and ammo.Guns>0 then
-- Guns (changed to shells)
if self.outofGuns and ammo.Shells>0 then
self.outofGuns=false
end
if ammo.Guns==0 and self.ammo.Guns>0 and not self.outofGuns then
if ammo.Shells==0 and self.ammo.Shells>0 and not self.outofGuns then
self.outofGuns=true
self:OutOfGuns()
end
@@ -11349,9 +11417,9 @@ function OPSGROUP:_SimpleTaskFunction(Function, uid)
return DCSTask
end
--- Enhance waypoint table.
--- Enhanced waypoint table.
-- @param #OPSGROUP self
-- @param #OPSGROUP.Waypoint Waypoint data.
-- @param #OPSGROUP.Waypoint waypoint Waypoint data.
-- @return #OPSGROUP.Waypoint Modified waypoint data.
function OPSGROUP:_CreateWaypoint(waypoint)
@@ -12682,7 +12750,7 @@ end
-- @return #OPSGROUP self
function OPSGROUP:SetDefaultCallsign(CallsignName, CallsignNumber)
self:T(self.lid..string.format("Setting Default callsing %s-%s", tostring(CallsignName), tostring(CallsignNumber)))
self:T(self.lid..string.format("Setting Default callsign %s-%s", tostring(CallsignName), tostring(CallsignNumber)))
self.callsignDefault={} --#OPSGROUP.Callsign
self.callsignDefault.NumberSquad=CallsignName
@@ -12779,7 +12847,7 @@ function OPSGROUP:_UpdatePosition()
self.positionLast=self.position or self:GetVec3()
self.headingLast=self.heading or self:GetHeading()
self.orientXLast=self.orientX or self:GetOrientationX()
self.velocityLast=self.velocity or self.group:GetVelocityMPS()
self.velocityLast=self.velocity or self.group:GetVelocityMPS()
-- Current state.
self.position=self:GetVec3()
@@ -13357,7 +13425,9 @@ function OPSGROUP:GetAmmoTot()
local Ammo={} --#OPSGROUP.Ammo
Ammo.Total=0
Ammo.Shells=0
Ammo.Guns=0
Ammo.Cannons=0
Ammo.Rockets=0
Ammo.Bombs=0
Ammo.Torpedos=0
@@ -13378,7 +13448,9 @@ function OPSGROUP:GetAmmoTot()
-- Add up total.
Ammo.Total=Ammo.Total+ammo.Total
Ammo.Shells=Ammo.Shells+ammo.Shells
Ammo.Guns=Ammo.Guns+ammo.Guns
Ammo.Cannons=Ammo.Cannons+ammo.Cannons
Ammo.Rockets=Ammo.Rockets+ammo.Rockets
Ammo.Bombs=Ammo.Bombs+ammo.Bombs
Ammo.Torpedos=Ammo.Torpedos+ammo.Torpedos
@@ -13411,6 +13483,8 @@ function OPSGROUP:GetAmmoUnit(unit, display)
-- Init counter.
local nammo=0
local nshells=0
local nguns=0
local ncannons=0
local nrockets=0
local nmissiles=0
local nmissilesAA=0
@@ -13433,10 +13507,10 @@ function OPSGROUP:GetAmmoUnit(unit, display)
local ammotable=unit:GetAmmo()
if ammotable then
local weapons=#ammotable
--self:I(ammotable)
--self:I(ammotable)
--UTILS.PrintTableToLog(ammotable)
-- Loop over all weapons.
for w=1,weapons do
@@ -13444,9 +13518,9 @@ function OPSGROUP:GetAmmoUnit(unit, display)
-- Number of current weapon.
local Nammo=ammotable[w]["count"]
-- Range in meters. Seems only to exist for missiles (not shells).
local rmin=ammotable[w]["desc"]["rangeMin"] or 0
local rmax=ammotable[w]["desc"]["rangeMaxAltMin"] or 0
-- Range in meters. Seems only to exist for missiles (not shells).
local rmin=ammotable[w]["desc"]["rangeMin"] or 0
local rmax=ammotable[w]["desc"]["rangeMaxAltMin"] or 0
-- Type name of current weapon.
local Tammo=ammotable[w]["desc"]["typeName"]
@@ -13468,6 +13542,16 @@ function OPSGROUP:GetAmmoUnit(unit, display)
-- Add up all shells.
nshells=nshells+Nammo
-- Add small and large caliber shells for guns and cannons
if ammotable[w]["desc"]["warhead"] and ammotable[w]["desc"]["warhead"]["caliber"] then
local caliber=ammotable[w]["desc"]["warhead"]["caliber"]
if caliber<25 then
nguns=nguns+Nammo
else
ncannons=ncannons+Nammo
end
end
-- Debug info.
text=text..string.format("- %d shells of type %s, range=%d - %d meters\n", Nammo, _weaponName, rmin, rmax)
@@ -13546,7 +13630,9 @@ function OPSGROUP:GetAmmoUnit(unit, display)
local ammo={} --#OPSGROUP.Ammo
ammo.Total=nammo
ammo.Guns=nshells
ammo.Shells=nshells
ammo.Guns=nguns
ammo.Cannons=ncannons
ammo.Rockets=nrockets
ammo.Bombs=nbombs
ammo.Torpedos=ntorps
@@ -13607,20 +13693,24 @@ end
-- @return Core.Point#COORDINATE The coordinate of the object.
function OPSGROUP:_CoordinateFromObject(Object)
env.info("FF coordfrom object")
if Object then
if Object:IsInstanceOf("COORDINATE") then
if VECTOR._IsVector(Object) then
env.info("FF VECTOR")
return Object:GetCoordinate()
elseif Object:IsInstanceOf("COORDINATE") then
return Object
else
if Object:IsInstanceOf("POSITIONABLE") or Object:IsInstanceOf("ZONE_BASE") then
self:T(self.lid.."WARNING: Coordinate is not a COORDINATE but a POSITIONABLE or ZONE. Trying to get coordinate")
self:E(self.lid.."WARNING: Coordinate is not a COORDINATE but a POSITIONABLE or ZONE. Trying to get coordinate")
local coord=Object:GetCoordinate()
return coord
else
self:T(self.lid.."ERROR: Coordinate is neither a COORDINATE nor any POSITIONABLE or ZONE!")
self:E(self.lid.."ERROR: Coordinate is neither a COORDINATE nor any POSITIONABLE or ZONE!")
end
end
else
self:T(self.lid.."ERROR: Object passed is nil!")
self:E(self.lid.."ERROR: Object passed is nil!")
end
return nil
@@ -13952,6 +14042,15 @@ function OPSGROUP:_GetDetectedTarget()
return targetgroup, targetdist
end
--- This function uses Disposition and other fallback logic to find better ground positions for ground units.
--- NOTE: This is not a spawn randomizer.
--- It will try to find clear ground locations avoiding trees, water, roads, runways, map scenery, statics and other units in the area and modifies the provided positions table.
--- Maintains the original layout and unit positions as close as possible by searching for the next closest valid position to each unit.
--- Uses UTILS.ValidateAndRepositionGroundUnits.
-- @param #boolean Enabled Enable/disable the feature.
function OPSGROUP:SetValidateAndRepositionGroundUnits(Enabled)
self.ValidateAndRepositionGroundUnits = Enabled
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+2 -2
View File
@@ -1544,7 +1544,7 @@ end
-- @param #PLAYERRECCE self
-- @param #number Frequency Frequency to be used. Can also be given as a table of multiple frequencies, e.g. 271 or {127,251}. There needs to be exactly the same number of modulations!
-- @param #number Modulation Modulation to be used. Can also be given as a table of multiple modulations, e.g. radio.modulation.AM or {radio.modulation.FM,radio.modulation.AM}. There needs to be exactly the same number of frequencies!
-- @param #string PathToSRS Defaults to "C:\\Program Files\\DCS-SimpleRadio-Standalone"
-- @param #string PathToSRS Defaults to "C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio"
-- @param #string Gender (Optional) Defaults to "male"
-- @param #string Culture (Optional) Defaults to "en-US"
-- @param #number Port (Optional) Defaults to 5002
@@ -1556,7 +1556,7 @@ end
-- @return #PLAYERRECCE self
function PLAYERRECCE:SetSRS(Frequency,Modulation,PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey,Backend)
self:T(self.lid.."SetSRS")
self.PathToSRS = PathToSRS or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone" --
self.PathToSRS = PathToSRS or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio" --
self.Gender = Gender or MSRS.gender or "male" --
self.Culture = Culture or MSRS.culture or "en-US" --
self.Port = Port or MSRS.port or 5002 --
+134 -39
View File
@@ -21,7 +21,7 @@
-- ===
-- @module Ops.PlayerTask
-- @image OPS_PlayerTask.jpg
-- @date Last Update April 2025
-- @date Last Update Oct 2025
do
@@ -59,6 +59,8 @@ do
-- @field #string FinalState
-- @field #string TypeName
-- @field #number PreviousCount
-- @field #boolean CanSmoke
-- @field #boolean ShowThreatDetails
-- @extends Core.Fsm#FSM
@@ -94,11 +96,13 @@ PLAYERTASK = {
NextTaskFailure = {},
FinalState = "none",
PreviousCount = 0,
CanSmoke = true,
ShowThreatDetails = true,
}
--- PLAYERTASK class version.
-- @field #string version
PLAYERTASK.version="0.1.26"
PLAYERTASK.version="0.1.29"
--- Generic task condition.
-- @type PLAYERTASK.Condition
@@ -231,6 +235,7 @@ function PLAYERTASK:New(Type, Target, Repeat, Times, TTSType)
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #boolen Silent If true, suppress message output on cancel.
--- On After "Planned" event. Task has been planned.
-- @function [parent=#PLAYERTASK] OnAfterPilotPlanned
@@ -387,6 +392,14 @@ function PLAYERTASK:_CheckCaptureOpsZoneSuccess(OpsZone, CaptureSquadGroupNamePr
return OpsZone:GetOwner() == Coalition and isClientInZone and isCaptureGroupInZone
end
--- [User] Override this function in order to implement custom logic if a player can join a task or not.
-- @param #PLAYERTASK self
-- @param Wrapper.Group#GROUP Group
-- @param Wrapper.Client#CLIENT Client
-- @return #boolean Outcome True if player can join the task, false if not
function PLAYERTASK:CanJoinTask(Group, Client)
return true
end
--- [Internal] Add a PLAYERTASKCONTROLLER for this task
-- @param #PLAYERTASK self
@@ -460,6 +473,26 @@ function PLAYERTASK:SetSubType(Type)
return self
end
--- [USER] Set if a task can have a smoke marker.
-- @param #PLAYERTASK self
-- @param #boolean OnOff If true (default) it can be smoke, false if not.
-- @return #PLAYERTASK self
function PLAYERTASK:SetCanSmoke(OnOff)
self:T(self.lid.."AddSSetCanSmokeubType")
self.CanSmoke = OnOff
return self
end
--- [USER] Set if a task can show threat details.
-- @param #PLAYERTASK self
-- @param #boolean OnOff If true (default) it can be shown, false if not.
-- @return #PLAYERTASK self
function PLAYERTASK:SetShowThreatDetails(OnOff)
self:T(self.lid.."SetShowThreatDetails")
self.ShowThreatDetails = OnOff
return self
end
--- [USER] Get task sub type description from this task.
-- @param #PLAYERTASK self
-- @return #string Type or nil
@@ -1165,11 +1198,12 @@ end
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #boolean Silent
-- @return #PLAYERTASK self
function PLAYERTASK:onafterCancel(From, Event, To)
function PLAYERTASK:onafterCancel(From, Event, To, Silent)
self:T({From, Event, To})
if self.TaskController then
self.TaskController:__TaskCancelled(-1,self)
self.TaskController:__TaskCancelled(-1,self, Silent)
end
self.timestamp = timer.getAbsTime()
self.FinalState = "Cancelled"
@@ -1227,7 +1261,10 @@ function PLAYERTASK:onafterFailed(From, Event, To)
self.TargetMarker:Remove()
end
self.FinalState = "Failed"
self:__Done(-1)
if self.TaskController then
self.TaskController:__TaskFailed(-1,self)
end
self:__Done(-1.5)
end
if self.TaskController.Scoring then
local clients,count = self:GetClientObjects()
@@ -1440,9 +1477,9 @@ do
-- taskmanager:AddRejectZone(ZONE:FindByName("RejectZone"))
--
-- -- Set up using SRS for messaging
-- local hereSRSPath = "C:\\Program Files\\DCS-SimpleRadio-Standalone"
-- local hereSRSPath = "C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio"
-- local hereSRSPort = 5002
-- -- local hereSRSGoogle = "C:\\Program Files\\DCS-SimpleRadio-Standalone\\yourkey.json"
-- -- local hereSRSGoogle = "C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio\\yourkey.json"
-- taskmanager:SetSRS({130,255},{radio.modulation.AM,radio.modulation.AM},hereSRSPath,"female","en-GB",hereSRSPort,"Microsoft Hazel Desktop",0.7,hereSRSGoogle)
--
-- -- Controller will announce itself under these broadcast frequencies, handy to use cold-start frequencies here of your aircraft
@@ -1595,7 +1632,7 @@ do
--
-- The event is triggered when a task was cancelled manually. Use @{#PLAYERTASKCONTROLLER.OnAfterTaskCancelled}()` to link into this event:
--
-- function taskmanager:OnAfterTaskCancelled(From, Event, To, Task)
-- function taskmanager:OnAfterTaskCancelled(From, Event, To, Task, Silent)
-- ... your code here ...
-- end
--
@@ -1759,12 +1796,15 @@ PLAYERTASKCONTROLLER.Messages = {
THREATMEDIUM = "medium",
THREATLOW = "low",
THREATTEXT = "%s\nThreat: %s\nTargets left: %d\nCoord: %s",
NOTHREATTEXT = "%s\nNo target information available.",
ELEVATION = "\nTarget Elevation: %s %s",
METER = "meter",
FEET = "feet",
THREATTEXTTTS = "%s, %s. Target information for %s. Threat level %s. Targets left %d. Target location %s.",
NOTHREATTEXTTTS = "%s, %s. No target information available.",
MARKTASK = "%s, %s, copy, task %03d location marked on map!",
SMOKETASK = "%s, %s, copy, task %03d location smoked!",
NOSMOKETASK = "%s, %s, negative, task %03d location cannot be smoked!",
FLARETASK = "%s, %s, copy, task %03d location illuminated!",
ABORTTASK = "All stations, %s, %s has aborted %s task %03d!",
UNKNOWN = "Unknown",
@@ -1843,12 +1883,15 @@ PLAYERTASKCONTROLLER.Messages = {
THREATMEDIUM = "mittel",
THREATLOW = "niedrig",
THREATTEXT = "%s\nGefahrstufe: %s\nZiele: %d\nKoord: %s",
NOTHREATTEXT = "%s\nKeine Zielinformation verfügbar.",
ELEVATION = "\nZiel Höhe: %s %s",
METER = "Meter",
FEET = "Fuss",
THREATTEXTTTS = "%s, %s. Zielinformation zu %s. Gefahrstufe %s. Ziele %d. Zielposition %s.",
NOTHREATTEXTTTS = "%s, %s. Keine Zielinformation verfügbar.",
MARKTASK = "%s, %s, verstanden, Zielposition %03d auf der Karte markiert!",
SMOKETASK = "%s, %s, verstanden, Zielposition %03d mit Rauch markiert!",
NOSMOKETASK = "%s, %s, negativ, Zielposition %03d kann nicht markiert werden!",
FLARETASK = "%s, %s, verstanden, Zielposition %03d beleuchtet!",
ABORTTASK = "%s, an alle, %s hat Auftrag %s %03d abgebrochen!",
UNKNOWN = "Unbekannt",
@@ -1909,7 +1952,7 @@ PLAYERTASKCONTROLLER.Messages = {
--- PLAYERTASK class version.
-- @field #string version
PLAYERTASKCONTROLLER.version="0.1.70"
PLAYERTASKCONTROLLER.version="0.1.71"
--- Create and run a new TASKCONTROLLER instance.
-- @param #PLAYERTASKCONTROLLER self
@@ -1951,7 +1994,7 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter)
self.taskinfomenu = false
self.activehasinfomenu = false
self.MenuName = nil
self.menuitemlimit = 5
self.menuitemlimit = 6
self.holdmenutime = 30
self.MarkerReadOnly = false
@@ -2050,6 +2093,7 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter)
-- @param #string Event Event.
-- @param #string To To state.
-- @param Ops.PlayerTask#PLAYERTASK Task
-- @param #boolean Silent If true suppress message output.
--- On After "TaskFailed" event. Task has failed.
-- @function [parent=#PLAYERTASKCONTROLLER] OnAfterTaskFailed
@@ -2581,7 +2625,7 @@ function PLAYERTASKCONTROLLER:SetMenuOptions(InfoMenu,ItemLimit,HoldTime)
if self.activehasinfomenu then
self:EnableTaskInfoMenu()
end
self.menuitemlimit = ItemLimit or 5
self.menuitemlimit = ItemLimit+1 or 6
self.holdmenutime = HoldTime or 30
return self
end
@@ -2730,11 +2774,12 @@ end
--- [User] Manually cancel a specific task
-- @param #PLAYERTASKCONTROLLER self
-- @param Ops.PlayerTask#PLAYERTASK Task The task to be cancelled
-- @param Ops.PlayerTask#PLAYERTASK Task The task to be cancelled.
-- @param #boolean Silent If true suppress message output.
-- @return #PLAYERTASKCONTROLLER self
function PLAYERTASKCONTROLLER:CancelTask(Task)
function PLAYERTASKCONTROLLER:CancelTask(Task,Silent)
self:T(self.lid.."CancelTask")
Task:__Cancel(-1)
Task:__Cancel(-1,Silent)
return self
end
@@ -3486,7 +3531,7 @@ end
-- @param #PLAYERTASKCONTROLLER self
-- @param Ops.PlayerTask#PLAYERTASK PlayerTask
-- @param #boolean Silent If true, make no "has new task" announcement
-- @param #boolen TaskFilter If true, apply the white/black-list task filters here, also
-- @param #boolean TaskFilter If true, apply the white/black-list task filters here, also
-- @return #PLAYERTASKCONTROLLER self
-- @usage
-- Example to create a PLAYERTASK of type CTLD and give Players 10 minutes to complete:
@@ -3530,6 +3575,16 @@ function PLAYERTASKCONTROLLER:AddPlayerTaskToQueue(PlayerTask,Silent,TaskFilter)
return self
end
--- [User] Override this function in order to implement custom logic if a player can join a task or not.
-- @param #PLAYERTASKCONTROLLER self
-- @param Ops.PlayerTask#PLAYERTASK Task
-- @param Wrapper.Group#GROUP Group
-- @param Wrapper.Client#CLIENT Client
-- @return #boolean Outcome True if player can join the task, false if not
function PLAYERTASKCONTROLLER:CanJoinTask(Task, Group, Client)
return true
end
--- [Internal] Join a player to a task
-- @param #PLAYERTASKCONTROLLER self
-- @param Ops.PlayerTask#PLAYERTASK Task
@@ -3540,6 +3595,15 @@ end
function PLAYERTASKCONTROLLER:_JoinTask(Task, Force, Group, Client)
self:T({Force, Group, Client})
self:T(self.lid.."_JoinTask")
if not self:CanJoinTask(Task, Group, Client) then
return self
end
if not Task:CanJoinTask(Group, Client) then
return self
end
local force = false
if type(Force) == "boolean" then
force = Force
@@ -3704,6 +3768,7 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Task, Group, Client)
local Elevation = Coordinate:GetLandHeight() or 0 -- meters
local CoordText = ""
local CoordTextLLDM = nil
local ShowThreatInfo = task.ShowThreatDetails
local LasingDrone = self:_FindLasingDroneForTaskID(task.PlayerTaskNr)
if self.Type ~= PLAYERTASKCONTROLLER.Type.A2A then
CoordText = Coordinate:ToStringA2G(Client,nil,self.ShowMagnetic)
@@ -3727,7 +3792,12 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Task, Group, Client)
local clientlist, clientcount = task:GetClients()
local ThreatGraph = "[" .. string.rep( "", ThreatLevel ) .. string.rep( "", 10 - ThreatLevel ) .. "]: "..ThreatLevel
local ThreatLocaleText = self.gettext:GetEntry("THREATTEXT",self.locale)
text = string.format(ThreatLocaleText, taskname, ThreatGraph, targets, CoordText)
if ShowThreatInfo == true then
text = string.format(ThreatLocaleText, taskname, ThreatGraph, targets, CoordText)
else
ThreatLocaleText = self.gettext:GetEntry("NOTHREATTEXT",self.locale)
text = string.format(ThreatLocaleText, taskname)
end
local settings = _DATABASE:GetPlayerSettings(playername) or _SETTINGS -- Core.Settings#SETTINGS
local elevationmeasure = self.gettext:GetEntry("FEET",self.locale)
if settings:IsMetric() then
@@ -3850,9 +3920,19 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Task, Group, Client)
end
--self:I(self.lid.." | ".. CoordText)
end
local ttstext
local ThreatLocaleTextTTS = self.gettext:GetEntry("THREATTEXTTTS",self.locale)
local ttstext = string.format(ThreatLocaleTextTTS,ttsplayername,self.MenuName or self.Name,ttstaskname,ThreatLevelText, targets, CoordText)
-- THREATTEXT = "%s\nThreat: %s\nTargets left: %d\nCoord: %s",
-- THREATTEXTTTS = "%s, %s. Target information for %s. Threat level %s. Targets left %d. Target location %s.",
if ShowThreatInfo == true then
ttstext = string.format(ThreatLocaleTextTTS,ttsplayername,self.MenuName or self.Name,ttstaskname,ThreatLevelText, targets, CoordText)
else
ThreatLocaleTextTTS = self.gettext:GetEntry("NOTHREATTEXTTTS",self.locale)
ttstext = string.format(ThreatLocaleTextTTS,ttsplayername,self.MenuName or self.Name)
end
-- POINTERTARGETLASINGTTS = ". Pointer over target and lasing."
if task.Type == AUFTRAG.Type.PRECISIONBOMBING and self.precisionbombing then
if LasingDrone and LasingDrone.playertask.inreach and LasingDrone:IsLasing() then
local lasingtext = self.gettext:GetEntry("POINTERTARGETLASINGTTS",self.locale)
@@ -3921,15 +4001,25 @@ function PLAYERTASKCONTROLLER:_SmokeTask(Group, Client)
local text = ""
if self.TasksPerPlayer:HasUniqueID(playername) then
local task = self.TasksPerPlayer:ReadByID(playername) -- Ops.PlayerTask#PLAYERTASK
task:SmokeTarget()
local textmark = self.gettext:GetEntry("SMOKETASK",self.locale)
text = string.format(textmark, ttsplayername, self.MenuName or self.Name, task.PlayerTaskNr)
self:T(self.lid..text)
--local m=MESSAGE:New(text,"10","Tasking"):ToAll()
if self.UseSRS then
self.SRSQueue:NewTransmission(text,nil,self.SRS,nil,2)
if task.CanSmoke == true then
task:SmokeTarget()
local textmark = self.gettext:GetEntry("SMOKETASK",self.locale)
text = string.format(textmark, ttsplayername, self.MenuName or self.Name, task.PlayerTaskNr)
self:T(self.lid..text)
--local m=MESSAGE:New(text,"10","Tasking"):ToAll()
if self.UseSRS then
self.SRSQueue:NewTransmission(text,nil,self.SRS,nil,2)
end
self:__TaskTargetSmoked(5,task)
else
local textmark = self.gettext:GetEntry("NOSMOKETASK",self.locale)
text = string.format(textmark, ttsplayername, self.MenuName or self.Name, task.PlayerTaskNr)
self:T(self.lid..text)
--local m=MESSAGE:New(text,"10","Tasking"):ToAll()
if self.UseSRS then
self.SRSQueue:NewTransmission(text,nil,self.SRS,nil,2)
end
end
self:__TaskTargetSmoked(5,task)
else
text = self.gettext:GetEntry("NOACTIVETASK",self.locale)
end
@@ -4606,7 +4696,7 @@ end
-- @param #PLAYERTASKCONTROLLER self
-- @param #number Frequency Frequency to be used. Can also be given as a table of multiple frequencies, e.g. 271 or {127,251}. There needs to be exactly the same number of modulations!
-- @param #number Modulation Modulation to be used. Can also be given as a table of multiple modulations, e.g. radio.modulation.AM or {radio.modulation.FM,radio.modulation.AM}. There needs to be exactly the same number of frequencies!
-- @param #string PathToSRS Defaults to "C:\\Program Files\\DCS-SimpleRadio-Standalone"
-- @param #string PathToSRS Defaults to "C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio"
-- @param #string Gender (Optional) Defaults to "male"
-- @param #string Culture (Optional) Defaults to "en-US"
-- @param #number Port (Optional) Defaults to 5002
@@ -4620,7 +4710,7 @@ end
-- @return #PLAYERTASKCONTROLLER self
function PLAYERTASKCONTROLLER:SetSRS(Frequency,Modulation,PathToSRS,Gender,Culture,Port,Voice,Volume,PathToGoogleKey,AccessKey,Coordinate,Backend)
self:T(self.lid.."SetSRS")
self.PathToSRS = PathToSRS or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone" --
self.PathToSRS = PathToSRS or MSRS.path or "C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio" --
self.Gender = Gender or MSRS.gender or "male" --
self.Culture = Culture or MSRS.culture or "en-US" --
self.Port = Port or MSRS.port or 5002 --
@@ -4763,22 +4853,27 @@ end
-- @param #string Event
-- @param #string To
-- @param Ops.PlayerTask#PLAYERTASK Task
-- @param #boolean Silent If true, suppress message output on cancel.
-- @return #PLAYERTASKCONTROLLER self
function PLAYERTASKCONTROLLER:onafterTaskCancelled(From, Event, To, Task)
function PLAYERTASKCONTROLLER:onafterTaskCancelled(From, Event, To, Task, Silent)
self:T({From, Event, To})
self:T(self.lid.."TaskCancelled")
local canceltxt = self.gettext:GetEntry("TASKCANCELLED",self.locale)
local canceltxttts = self.gettext:GetEntry("TASKCANCELLEDTTS",self.locale)
local taskname = string.format(canceltxt, Task.PlayerTaskNr, tostring(Task.Type))
if not self.NoScreenOutput then
self:_SendMessageToClients(taskname,15)
--local m = MESSAGE:New(taskname,15,"Tasking"):ToCoalition(self.Coalition)
if Silent ~= true then
local canceltxt = self.gettext:GetEntry("TASKCANCELLED",self.locale)
local canceltxttts = self.gettext:GetEntry("TASKCANCELLEDTTS",self.locale)
local taskname = string.format(canceltxt, Task.PlayerTaskNr, tostring(Task.Type))
if self.NoScreenOutput ~= true then
self:_SendMessageToClients(taskname,15)
--local m = MESSAGE:New(taskname,15,"Tasking"):ToCoalition(self.Coalition)
end
if self.UseSRS then
taskname = string.format(canceltxttts, self.MenuName or self.Name, Task.PlayerTaskNr, tostring(Task.TTSType))
self.SRSQueue:NewTransmission(taskname,nil,self.SRS,nil,2)
end
end
if self.UseSRS then
taskname = string.format(canceltxttts, self.MenuName or self.Name, Task.PlayerTaskNr, tostring(Task.TTSType))
self.SRSQueue:NewTransmission(taskname,nil,self.SRS,nil,2)
end
local clients=Task:GetClientObjects()
for _,client in pairs(clients) do
self:_RemoveMenuEntriesForTask(Task,client)
+27 -18
View File
@@ -20,7 +20,6 @@
-- ===
--
-- ### Author: **funkyfranky**
-- ### Contributions: Flightcontrol (@{AI.AI_Formation} class being used here)
--
-- @module Ops.RescueHelo
-- @image Ops_RescueHelo.png
@@ -37,7 +36,7 @@
-- @field #number takeoff Takeoff type.
-- @field Wrapper.Airbase#AIRBASE airbase The airbase object acting as home base of the helo.
-- @field Core.Set#SET_GROUP followset Follow group set.
-- @field AI.AI_Formation#AI_FORMATION formation AI_FORMATION object.
-- @field Functional.Formation#FORMATION formation FORMATION object.
-- @field #number lowfuel Low fuel threshold of helo in percent.
-- @field #number altitude Altitude of helo in meters.
-- @field #number offsetX Offset in meters to carrier in longitudinal direction.
@@ -235,7 +234,7 @@ _RESCUEHELOID=0
--- Class version.
-- @field #string version
RESCUEHELO.version="1.1.0"
RESCUEHELO.version="1.2.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@@ -248,6 +247,7 @@ RESCUEHELO.version="1.1.0"
-- DONE: Possibility to add already present/spawned aircraft, e.g. for warehouse.
-- DONE: Add rescue event when aircraft crashes.
-- DONE: Make offset input parameter.
-- DONE: Make useable for AUFTRAG
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
@@ -337,6 +337,9 @@ function RESCUEHELO:New(carrierunit, helogroupname)
self:AddTransition("*", "Status", "*")
self:AddTransition("*", "Stop", "Stopped")
self:I(self.lid.."Started.")
--- Triggers the FSM event "Start" that starts the rescue helo. Initializes parameters and starts event handlers.
-- @function [parent=#RESCUEHELO] Start
@@ -871,15 +874,25 @@ function RESCUEHELO:onafterStart(From, Event, To)
-- Delay before formation is started.
local delay=120
-- Spawn helo. We need to introduce an alias in case this class is used twice. This would confuse the spawn routine.
local Spawn=SPAWN:NewWithAlias(self.helogroupname, self.alias)
-- Set modex for spawn.
Spawn:InitModex(self.modex)
local UsesAliveGroup=false
local Spawn = GROUP:FindByName(self.helogroupname)
if Spawn and Spawn:IsAlive() then
self.helo=Spawn
UsesAliveGroup = true
delay = 1
else
-- Spawn helo. We need to introduce an alias in case this class is used twice. This would confuse the spawn routine.
local Spawn=SPAWN:NewWithAlias(self.helogroupname, self.alias)
-- Set modex for spawn.
Spawn:InitModex(self.modex)
end
-- Spawn in air or at airbase.
if self.takeoff==SPAWN.Takeoff.Air then
if UsesAliveGroup==false and self.takeoff==SPAWN.Takeoff.Air then
-- Carrier heading
local hdg=self.carrier:GetHeading()
@@ -899,7 +912,7 @@ function RESCUEHELO:onafterStart(From, Event, To)
-- Start formation in 1 seconds
delay=1
else
elseif UsesAliveGroup==false and self.uncontrolledac then
-- Check if an uncontrolled helo group was requested.
if self.uncontrolledac then
@@ -919,9 +932,9 @@ function RESCUEHELO:onafterStart(From, Event, To)
-- No group of that name!
self:E(string.format("ERROR: No uncontrolled (alive) rescue helo group with name %s could be found!", self.helogroupname))
return
end
end
else
elseif UsesAliveGroup==false then
-- Spawn at airbase.
self.helo=Spawn:SpawnAtAirbase(self.airbase, self.takeoff, nil, AIRBASE.TerminalType.HelicopterUsable)
@@ -947,17 +960,13 @@ function RESCUEHELO:onafterStart(From, Event, To)
self.HeloFuel0=self.helo:GetFuel()
-- Define AI Formation object.
self.formation=AI_FORMATION:New(self.carrier, self.followset, "Helo Formation with Carrier", "Follow Carrier at given parameters.")
self.formation=FORMATION:New(self.carrier, self.followset, "Helo Formation with Carrier")
-- Formation parameters.
self.formation:FormationCenterWing(-self.offsetX, 50, math.abs(self.altitude), 50, self.offsetZ, 50)
-- Set follow time interval.
self.formation:SetFollowTimeInterval(self.dtFollow)
-- Formation mode.
self.formation:SetFlightModeFormation(self.helo)
-- Start formation FSM.
self.formation:__Start(delay)
+12 -6
View File
@@ -2006,9 +2006,10 @@ end
--- Count alive objects.
-- @param #TARGET self
-- @param #TARGET.Object Target Target objective.
-- @param #table Coalitions (Optional) Only count targets of the given coalition(s).
-- @param #table Coalitions (Optional) Only count targets of the given coalition(s).
-- @param #boolean OnlyReallyAlive (Optional) If `true`, count only really alive targets (units, groups) but not coordinates or zones.
-- @return #number Number of alive target objects.
function TARGET:CountObjectives(Target, Coalitions)
function TARGET:CountObjectives(Target, Coalitions, OnlyReallyAlive)
local N=0
@@ -2067,13 +2068,17 @@ function TARGET:CountObjectives(Target, Coalitions)
-- No target, where we can check the alive status, so we assume it is alive. Changed this because otherwise target count is 0 if we pass a coordinate.
-- This is also more consitent with the life and is alive status.
N=N+1
if not OnlyReallyAlive then
N=N+1
end
elseif Target.Type==TARGET.ObjectType.ZONE then
-- No target, where we can check the alive status, so we assume it is alive. Changed this because otherwise target count is 0 if we pass a coordinate.
-- This is also more consitent with the life and is alive status.
N=N+1
if not OnlyReallyAlive then
N=N+1
end
elseif Target.Type==TARGET.ObjectType.OPSZONE then
@@ -2093,15 +2098,16 @@ end
--- Count alive targets.
-- @param #TARGET self
-- @param #table Coalitions (Optional) Only count targets of the given coalition(s).
-- @param #boolean OnlyReallyAlive (Optional) If `true`, count only really alive targets (units, groups) but not coordinates or zones.
-- @return #number Number of alive target objects.
function TARGET:CountTargets(Coalitions)
function TARGET:CountTargets(Coalitions, OnlyReallyAlive)
local N=0
for _,_target in pairs(self.targets) do
local Target=_target --#TARGET.Object
N=N+self:CountObjectives(Target, Coalitions)
N=N+self:CountObjectives(Target, Coalitions, OnlyReallyAlive)
end
+11 -1
View File
@@ -72,7 +72,7 @@ end
--- Checks if a point is contained within the circle.
-- @param #table point The point to check
-- @return #bool True if the point is contained, false otherwise
-- @return #boolean True if the point is contained, false otherwise
function CIRCLE:ContainsPoint(point)
if ((point.x - self.CenterVec2.x) ^ 2 + (point.y - self.CenterVec2.y) ^ 2) ^ 0.5 <= self.Radius then
return true
@@ -226,6 +226,11 @@ end
--- Returns a random Vec2 within the circle.
-- @return #table The random Vec2
function CIRCLE:GetRandomVec2()
math.random()
math.random()
math.random()
local angle = math.random() * 2 * math.pi
local rx = math.random(0, self.Radius) * math.cos(angle) + self.CenterVec2.x
@@ -237,6 +242,11 @@ end
--- Returns a random Vec2 on the border of the circle.
-- @return #table The random Vec2
function CIRCLE:GetRandomVec2OnBorder()
math.random()
math.random()
math.random()
local angle = math.random() * 2 * math.pi
local rx = self.Radius * math.cos(angle) + self.CenterVec2.x
@@ -352,6 +352,7 @@ end
--- Returns a random Vec2 within the polygon. The Vec2 is weighted by the areas of the triangles that make up the polygon.
-- @return #table The random Vec2
function POLYGON:GetRandomVec2()
local weights = {}
for _, triangle in pairs(self.Triangles) do
weights[triangle] = triangle.SurfaceArea / self.SurfaceArea
@@ -73,6 +73,11 @@ end
-- @param #table points The points of the triangle, or 3 other points if you're just using the TRIANGLE class without an object of it
-- @return #table The random Vec2
function TRIANGLE:GetRandomVec2(points)
math.random()
math.random()
math.random()
points = points or self.Points
local pt = {math.random(), math.random()}
table.sort(pt)
+205 -67
View File
@@ -443,28 +443,32 @@ MSRS.Voices = {
["en_AU_Standard_B"] = 'en-AU-Standard-B', -- [2] MALE
["en_AU_Standard_C"] = 'en-AU-Standard-C', -- [3] FEMALE
["en_AU_Standard_D"] = 'en-AU-Standard-D', -- [4] MALE
["en_IN_Standard_A"] = 'en-IN-Standard-A', -- [5] FEMALE
["en_IN_Standard_B"] = 'en-IN-Standard-B', -- [6] MALE
["en_IN_Standard_C"] = 'en-IN-Standard-C', -- [7] MALE
["en_IN_Standard_D"] = 'en-IN-Standard-D', -- [8] FEMALE
-- IN
["en_IN_Standard_A"] = 'en-IN-Standard-A', -- Female
["en_IN_Standard_B"] = 'en-IN-Standard-B', -- Male
["en_IN_Standard_C"] = 'en-IN-Standard-C', -- Male
["en_IN_Standard_D"] = 'en-IN-Standard-D', -- Female
["en_IN_Standard_E"] = 'en-IN-Standard-E', -- Female
["en_IN_Standard_F"] = 'en-IN-Standard-F', -- Male
-- 2025 changes
["en_GB_Standard_A"] = 'en-GB-Standard-N', -- [9] FEMALE
["en_GB_Standard_B"] = 'en-GB-Standard-O', -- [10] MALE
["en_GB_Standard_C"] = 'en-GB-Standard-N', -- [11] FEMALE
["en_GB_Standard_D"] = 'en-GB-Standard-O', -- [12] MALE
["en_GB_Standard_F"] = 'en-GB-Standard-N', -- [13] FEMALE
["en_GB_Standard_O"] = 'en-GB-Standard-O', -- [12] MALE
["en_GB_Standard_N"] = 'en-GB-Standard-N', -- [13] FEMALE
["en_US_Standard_A"] = 'en-US-Standard-A', -- [14] MALE
["en_US_Standard_B"] = 'en-US-Standard-B', -- [15] MALE
["en_US_Standard_C"] = 'en-US-Standard-C', -- [16] FEMALE
["en_US_Standard_D"] = 'en-US-Standard-D', -- [17] MALE
["en_US_Standard_E"] = 'en-US-Standard-E', -- [18] FEMALE
["en_US_Standard_F"] = 'en-US-Standard-F', -- [19] FEMALE
["en_US_Standard_G"] = 'en-US-Standard-G', -- [20] FEMALE
["en_US_Standard_H"] = 'en-US-Standard-H', -- [21] FEMALE
["en_US_Standard_I"] = 'en-US-Standard-I', -- [22] MALE
["en_US_Standard_J"] = 'en-US-Standard-J', -- [23] MALE
["en_GB_Standard_A"] = 'en-GB-Standard-A', -- Female
["en_GB_Standard_B"] = 'en-GB-Standard-B', -- Male
["en_GB_Standard_C"] = 'en-GB-Standard-C', -- Female
["en_GB_Standard_D"] = 'en-GB-Standard-D', -- Male
["en_GB_Standard_F"] = 'en-GB-Standard-F', -- Female
["en_GB_Standard_N"] = 'en-GB-Standard-N', -- Female
["en_GB_Standard_O"] = 'en-GB-Standard-O', -- Male
-- US
["en_US_Standard_A"] = 'en-US-Standard-A', -- Male
["en_US_Standard_B"] = 'en-US-Standard-B', -- Male
["en_US_Standard_C"] = 'en-US-Standard-C', -- Female
["en_US_Standard_D"] = 'en-US-Standard-D', -- Male
["en_US_Standard_E"] = 'en-US-Standard-E', -- Female
["en_US_Standard_F"] = 'en-US-Standard-F', -- Female
["en_US_Standard_G"] = 'en-US-Standard-G', -- Female
["en_US_Standard_H"] = 'en-US-Standard-H', -- Female
["en_US_Standard_I"] = 'en-US-Standard-I', -- Male
["en_US_Standard_J"] = 'en-US-Standard-J', -- Male
-- 2025 catalog changes
["fr_FR_Standard_A"] = "fr-FR-Standard-F", -- Female
["fr_FR_Standard_B"] = "fr-FR-Standard-G", -- Male
@@ -474,14 +478,15 @@ MSRS.Voices = {
["fr_FR_Standard_G"] = "fr-FR-Standard-G", -- Male
["fr_FR_Standard_F"] = "fr-FR-Standard-F", -- Female
-- 2025 catalog changes
["de_DE_Standard_A"] = "de-DE-Standard-G", -- Female
["de_DE_Standard_B"] = "de-DE-Standard-H", -- Male
["de_DE_Standard_C"] = "de-DE-Standard-G", -- Female
["de_DE_Standard_D"] = "de-DE-Standard-H", -- Male
["de_DE_Standard_E"] = "de-DE-Standard-H", -- Male
["de_DE_Standard_F"] = "de-DE-Standard-G", -- Female
["de_DE_Standard_H"] = "de-DE-Standard-H", -- Male
["de_DE_Standard_G"] = "de-DE-Standard-G", -- Female
["de_DE_Standard_A"] = 'de-DE-Standard-A', -- Female
["de_DE_Standard_B"] = 'de-DE-Standard-B', -- Male
["de_DE_Standard_C"] = 'de-DE-Standard-C', -- Female
["de_DE_Standard_D"] = 'de-DE-Standard-D', -- Male
["de_DE_Standard_E"] = 'de-DE-Standard-E', -- Male
["de_DE_Standard_F"] = 'de-DE-Standard-F', -- Female
["de_DE_Standard_G"] = 'de-DE-Standard-G', -- Female
["de_DE_Standard_H"] = 'de-DE-Standard-H', -- Male
-- ES
["es_ES_Standard_A"] = "es-ES-Standard-E", -- Female
["es_ES_Standard_B"] = "es-ES-Standard-F", -- Male
["es_ES_Standard_C"] = "es-ES-Standard-E", -- Female
@@ -497,32 +502,36 @@ MSRS.Voices = {
["it_IT_Standard_F"] = "it-IT-Standard-F", -- Male
},
Wavenet = {
["en_AU_Wavenet_A"] = 'en-AU-Wavenet-A', -- [1] FEMALE
["en_AU_Wavenet_B"] = 'en-AU-Wavenet-B', -- [2] MALE
["en_AU_Wavenet_C"] = 'en-AU-Wavenet-C', -- [3] FEMALE
["en_AU_Wavenet_D"] = 'en-AU-Wavenet-D', -- [4] MALE
["en_IN_Wavenet_A"] = 'en-IN-Wavenet-A', -- [5] FEMALE
["en_IN_Wavenet_B"] = 'en-IN-Wavenet-B', -- [6] MALE
["en_IN_Wavenet_C"] = 'en-IN-Wavenet-C', -- [7] MALE
["en_IN_Wavenet_D"] = 'en-IN-Wavenet-D', -- [8] FEMALE
["en_AU_Wavenet_A"] = 'en-AU-Wavenet-A', -- Female
["en_AU_Wavenet_B"] = 'en-AU-Wavenet-B', -- Male
["en_AU_Wavenet_C"] = 'en-AU-Wavenet-C', -- Female
["en_AU_Wavenet_D"] = 'en-AU-Wavenet-D', -- Male
-- IN
["en_IN_Wavenet_A"] = 'en-IN-Wavenet-A', -- Female
["en_IN_Wavenet_B"] = 'en-IN-Wavenet-B', -- Male
["en_IN_Wavenet_C"] = 'en-IN-Wavenet-C', -- Male
["en_IN_Wavenet_D"] = 'en-IN-Wavenet-D', -- Female
["en_IN_Wavenet_E"] = 'en-IN-Wavenet-E', -- Female
["en_IN_Wavenet_F"] = 'en-IN-Wavenet-F', -- Male
-- 2025 changes
["en_GB_Wavenet_A"] = 'en-GB-Wavenet-N', -- [9] FEMALE
["en_GB_Wavenet_B"] = 'en-GB-Wavenet-O', -- [10] MALE
["en_GB_Wavenet_C"] = 'en-GB-Wavenet-N', -- [11] FEMALE
["en_GB_Wavenet_D"] = 'en-GB-Wavenet-O', -- [12] MALE
["en_GB_Wavenet_F"] = 'en-GB-Wavenet-N', -- [13] FEMALE
["en_GB_Wavenet_A"] = 'en-GB-Wavenet-A', -- [9] FEMALE
["en_GB_Wavenet_B"] = 'en-GB-Wavenet-B', -- [10] MALE
["en_GB_Wavenet_C"] = 'en-GB-Wavenet-C', -- [11] FEMALE
["en_GB_Wavenet_D"] = 'en-GB-Wavenet-D', -- [12] MALE
["en_GB_Wavenet_F"] = 'en-GB-Wavenet-F', -- [13] FEMALE
["en_GB_Wavenet_O"] = 'en-GB-Wavenet-O', -- [12] MALE
["en_GB_Wavenet_N"] = 'en-GB-Wavenet-N', -- [13] FEMALE
["en_US_Wavenet_A"] = 'en-US-Wavenet-N', -- [14] MALE
["en_US_Wavenet_B"] = 'en-US-Wavenet-B', -- [15] MALE
["en_US_Wavenet_C"] = 'en-US-Wavenet-C', -- [16] FEMALE
["en_US_Wavenet_D"] = 'en-US-Wavenet-D', -- [17] MALE
["en_US_Wavenet_E"] = 'en-US-Wavenet-E', -- [18] FEMALE
["en_US_Wavenet_F"] = 'en-US-Wavenet-F', -- [19] FEMALE
["en_US_Wavenet_G"] = 'en-US-Wavenet-G', -- [20] FEMALE
["en_US_Wavenet_H"] = 'en-US-Wavenet-H', -- [21] FEMALE
["en_US_Wavenet_I"] = 'en-US-Wavenet-I', -- [22] MALE
["en_US_Wavenet_J"] = 'en-US-Wavenet-J', -- [23] MALE
["en_GB_Wavenet_N"] = 'en-GB-Wavenet-N', -- [13] FEMALE
-- US
["en_US_Wavenet_A"] = 'en-US-Wavenet-A', -- Male
["en_US_Wavenet_B"] = 'en-US-Wavenet-B', -- Male
["en_US_Wavenet_C"] = 'en-US-Wavenet-C', -- Female
["en_US_Wavenet_D"] = 'en-US-Wavenet-D', -- Male
["en_US_Wavenet_E"] = 'en-US-Wavenet-E', -- Female
["en_US_Wavenet_F"] = 'en-US-Wavenet-F', -- Female
["en_US_Wavenet_G"] = 'en-US-Wavenet-G', -- Female
["en_US_Wavenet_H"] = 'en-US-Wavenet-H', -- Female
["en_US_Wavenet_I"] = 'en-US-Wavenet-I', -- Male
["en_US_Wavenet_J"] = 'en-US-Wavenet-J', -- Male
-- 2025 catalog changes
["fr_FR_Wavenet_A"] = "fr-FR-Wavenet-F", -- Female
["fr_FR_Wavenet_B"] = "fr-FR-Wavenet-G", -- Male
@@ -532,14 +541,15 @@ MSRS.Voices = {
["fr_FR_Wavenet_G"] = "fr-FR-Wavenet-G", -- Male
["fr_FR_Wavenet_F"] = "fr-FR-Wavenet-F", -- Female
-- 2025 catalog changes
["de_DE_Wavenet_A"] = "de-DE-Wavenet-G", -- Female
["de_DE_Wavenet_B"] = "de-DE-Wavenet-H", -- Male
["de_DE_Wavenet_C"] = "de-DE-Wavenet-G", -- Female
["de_DE_Wavenet_D"] = "de-DE-Wavenet-H", -- Male
["de_DE_Wavenet_E"] = "de-DE-Wavenet-H", -- Male
["de_DE_Wavenet_F"] = "de-DE-Wavenet-G", -- Female
["de_DE_Wavenet_H"] = "de-DE-Wavenet-H", -- Male
["de_DE_Wavenet_G"] = "de-DE-Wavenet-G", -- Female
["de_DE_Wavenet_A"] = 'de-DE-Wavenet-A', -- Female
["de_DE_Wavenet_B"] = 'de-DE-Wavenet-B', -- Male
["de_DE_Wavenet_C"] = 'de-DE-Wavenet-C', -- Female
["de_DE_Wavenet_D"] = 'de-DE-Wavenet-D', -- Male
["de_DE_Wavenet_E"] = 'de-DE-Wavenet-E', -- Male
["de_DE_Wavenet_F"] = 'de-DE-Wavenet-F', -- Female
["de_DE_Wavenet_G"] = 'de-DE-Wavenet-G', -- Female
["de_DE_Wavenet_H"] = 'de-DE-Wavenet-H', -- Male
-- ES
["es_ES_Wavenet_B"] = "es-ES-Wavenet-E", -- Male
["es_ES_Wavenet_C"] = "es-ES-Wavenet-F", -- Female
["es_ES_Wavenet_D"] = "es-ES-Wavenet-E", -- Female
@@ -553,6 +563,134 @@ MSRS.Voices = {
["it_IT_Wavenet_E"] = "it-IT-Wavenet-E", -- Female
["it_IT_Wavenet_F"] = "it-IT-Wavenet-F", -- Male
} ,
Chirp3HD = {
["en_GB_Chirp3_HD_Aoede"] = 'en-GB-Chirp3-HD-Aoede', -- Female
["en_GB_Chirp3_HD_Charon"] = 'en-GB-Chirp3-HD-Charon', -- Male
["en_GB_Chirp3_HD_Fenrir"] = 'en-GB-Chirp3-HD-Fenrir', -- Male
["en_GB_Chirp3_HD_Kore"] = 'en-GB-Chirp3-HD-Kore', -- Female
["en_GB_Chirp3_HD_Leda"] = 'en-GB-Chirp3-HD-Leda', -- Female
["en_GB_Chirp3_HD_Orus"] = 'en-GB-Chirp3-HD-Orus', -- Male
["en_GB_Chirp3_HD_Puck"] = 'en-GB-Chirp3-HD-Puck', -- Male
["en_GB_Chirp3_HD_Zephyr"] = 'en-GB-Chirp3-HD-Zephyr', -- Female
--["de_DE_Chirp3_HD_Aoede"] = 'de-DE-Chirp3-HD-Aoede', -- Female (Datenfehler im Original)
["en_US_Chirp3_HD_Charon"] = 'en-US-Chirp3-HD-Charon', -- Male
["en_US_Chirp3_HD_Fenrir"] = 'en-US-Chirp3-HD-Fenrir', -- Male
["en_US_Chirp3_HD_Kore"] = 'en-US-Chirp3-HD-Kore', -- Female
["en_US_Chirp3_HD_Leda"] = 'en-US-Chirp3-HD-Leda', -- Female
["en_US_Chirp3_HD_Orus"] = 'en-US-Chirp3-HD-Orus', -- Male
["en_US_Chirp3_HD_Puck"] = 'en-US-Chirp3-HD-Puck', -- Male
--["de_DE_Chirp3_HD_Zephyr"] = 'de-DE-Chirp3-HD-Zephyr', -- Female (Datenfehler im Original)
-- DE
["de_DE_Chirp3_HD_Aoede"] = 'de-DE-Chirp3-HD-Aoede', -- Female
["de_DE_Chirp3_HD_Charon"] = 'de-DE-Chirp3-HD-Charon', -- Male
["de_DE_Chirp3_HD_Fenrir"] = 'de-DE-Chirp3-HD-Fenrir', -- Male
["de_DE_Chirp3_HD_Kore"] = 'de-DE-Chirp3-HD-Kore', -- Female
["de_DE_Chirp3_HD_Leda"] = 'de-DE-Chirp3-HD-Leda', -- Female
["de_DE_Chirp3_HD_Orus"] = 'de-DE-Chirp3-HD-Orus', -- Male
["de_DE_Chirp3_HD_Puck"] = 'de-DE-Chirp3-HD-Puck', -- Male
["de_DE_Chirp3_HD_Zephyr"] = 'de-DE-Chirp3-HD-Zephyr', -- Female
-- AU
["en_AU_Chirp3_HD_Aoede"] = 'en-AU-Chirp3-HD-Aoede', -- Female
["en_AU_Chirp3_HD_Charon"] = 'en-AU-Chirp3-HD-Charon', -- Male
["en_AU_Chirp3_HD_Fenrir"] = 'en-AU-Chirp3-HD-Fenrir', -- Male
["en_AU_Chirp3_HD_Kore"] = 'en-AU-Chirp3-HD-Kore', -- Female
["en_AU_Chirp3_HD_Leda"] = 'en-AU-Chirp3-HD-Leda', -- Female
["en_AU_Chirp3_HD_Orus"] = 'en-AU-Chirp3-HD-Orus', -- Male
["en_AU_Chirp3_HD_Puck"] = 'en-AU-Chirp3-HD-Puck', -- Male
["en_AU_Chirp3_HD_Zephyr"] = 'en-AU-Chirp3-HD-Zephyr', -- Female
-- IN
["en_IN_Chirp3_HD_Aoede"] = 'en-IN-Chirp3-HD-Aoede', -- Female
["en_IN_Chirp3_HD_Charon"] = 'en-IN-Chirp3-HD-Charon', -- Male
["en_IN_Chirp3_HD_Fenrir"] = 'en-IN-Chirp3-HD-Fenrir', -- Male
["en_IN_Chirp3_HD_Kore"] = 'en-IN-Chirp3-HD-Kore', -- Female
["en_IN_Chirp3_HD_Leda"] = 'en-IN-Chirp3-HD-Leda', -- Female
["en_IN_Chirp3_HD_Orus"] = 'en-IN-Chirp3-HD-Orus', -- Male
},
ChirpHD = {
["en_US_Chirp_HD_D"] = 'en-US-Chirp-HD-D', -- Male
["en_US_Chirp_HD_F"] = 'en-US-Chirp-HD-F', -- Female
["en_US_Chirp_HD_O"] = 'en-US-Chirp-HD-O', -- Female
-- DE
["de_DE_Chirp_HD_D"] = 'de-DE-Chirp-HD-D', -- Male
["de_DE_Chirp_HD_F"] = 'de-DE-Chirp-HD-F', -- Female
["de_DE_Chirp_HD_O"] = 'de-DE-Chirp-HD-O', -- Female
-- AU
["en_AU_Chirp_HD_D"] = 'en-AU-Chirp-HD-D', -- Male
["en_AU_Chirp_HD_F"] = 'en-AU-Chirp-HD-F', -- Female
["en_AU_Chirp_HD_O"] = 'en-AU-Chirp-HD-O', -- Female
-- IN
["en_IN_Chirp_HD_D"] = 'en-IN-Chirp-HD-D', -- Male
["en_IN_Chirp_HD_F"] = 'en-IN-Chirp-HD-F', -- Female
["en_IN_Chirp_HD_O"] = 'en-IN-Chirp-HD-O', -- Female
},
},
Neural2 = {
["en_GB_Neural2_A"] = 'en-GB-Neural2-A', -- Female
["en_GB_Neural2_B"] = 'en-GB-Neural2-B', -- Male
["en_GB_Neural2_C"] = 'en-GB-Neural2-C', -- Female
["en_GB_Neural2_D"] = 'en-GB-Neural2-D', -- Male
["en_GB_Neural2_F"] = 'en-GB-Neural2-F', -- Female
["en_GB_Neural2_N"] = 'en-GB-Neural2-N', -- Female
["en_GB_Neural2_O"] = 'en-GB-Neural2-O', -- Male
-- US
["en_US_Neural2_A"] = 'en-US-Neural2-A', -- Male
["en_US_Neural2_C"] = 'en-US-Neural2-C', -- Female
["en_US_Neural2_D"] = 'en-US-Neural2-D', -- Male
["en_US_Neural2_E"] = 'en-US-Neural2-E', -- Female
["en_US_Neural2_F"] = 'en-US-Neural2-F', -- Female
["en_US_Neural2_G"] = 'en-US-Neural2-G', -- Female
["en_US_Neural2_H"] = 'en-US-Neural2-H', -- Female
["en_US_Neural2_I"] = 'en-US-Neural2-I', -- Male
["en_US_Neural2_J"] = 'en-US-Neural2-J', -- Male
-- DE
["de_DE_Neural2_G"] = 'de-DE-Neural2-G', -- Female
["de_DE_Neural2_H"] = 'de-DE-Neural2-H', -- Male
-- AU
["en_AU_Neural2_A"] = 'en-AU-Neural2-A', -- Female
["en_AU_Neural2_B"] = 'en-AU-Neural2-B', -- Male
["en_AU_Neural2_C"] = 'en-AU-Neural2-C', -- Female
["en_AU_Neural2_D"] = 'en-AU-Neural2-D', -- Male
-- IN
["en_IN_Neural2_A"] = 'en-IN-Neural2-A', -- Female
["en_IN_Neural2_B"] = 'en-IN-Neural2-B', -- Male
["en_IN_Neural2_C"] = 'en-IN-Neural2-C', -- Male
["en_IN_Neural2_D"] = 'en-IN-Neural2-D', -- Female
},
News = {
["en_GB_News_G"] = 'en-GB-News-G', -- Female
["en_GB_News_H"] = 'en-GB-News-H', -- Female
["en_GB_News_I"] = 'en-GB-News-I', -- Female
["en_GB_News_J"] = 'en-GB-News-J', -- Male
["en_GB_News_K"] = 'en-GB-News-K', -- Male
["en_GB_News_L"] = 'en-GB-News-L', -- Male
["en_GB_News_M"] = 'en-GB-News-M', -- Male
-- US
["en_US_News_K"] = 'en-US-News-K', -- Female
["en_US_News_L"] = 'en-US-News-L', -- Female
["en_US_News_N"] = 'en-US-News-N', -- Male
-- AU
["en_AU_News_E"] = 'en-AU-News-E', -- Female
["en_AU_News_F"] = 'en-AU-News-F', -- Female
["en_AU_News_G"] = 'en-AU-News-G', -- Male
},
Casual = {
["en_US_Casual_K"] = 'en-US-Casual-K', -- Male
},
Polyglot = {
["en_US_Polyglot_1"] = 'en-US-Polyglot-1', -- Male
["de_DE_Polyglot_1"] = 'de-DE-Polyglot-1', -- Male
["en_AU_Polyglot_1"] = 'en-AU-Polyglot-1', -- Male
},
Studio = {
-- Englisch (UK) - Studio
["en_GB_Studio_B"] = 'en-GB-Studio-B', -- Male
["en_GB_Studio_C"] = 'en-GB-Studio-C', -- Female
-- Englisch (USA) - Studio
["en_US_Studio_O"] = 'en-US-Studio-O', -- Female
["en_US_Studio_Q"] = 'en-US-Studio-Q', -- Male
-- DE
["de_DE_Studio_B"] = 'de-DE-Studio-B', -- Male
["de_DE_Studio_C"] = 'de-DE-Studio-C', -- Female
},
}
@@ -632,7 +770,7 @@ end
-- set the path to the exe file via @{#MSRS.SetPath}.
--
-- @param #MSRS self
-- @param #string Path Path to SRS directory. Default `C:\\Program Files\\DCS-SimpleRadio-Standalone`.
-- @param #string Path Path to SRS directory. Default `C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio`.
-- @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 #string Backend Backend used: `MSRS.Backend.SRSEXE` (default) or `MSRS.Backend.GRPC`.
@@ -767,13 +905,13 @@ end
--- Set path to SRS install directory. More precisely, path to where the `DCS-SR-ExternalAudio.exe` is located.
-- @param #MSRS self
-- @param #string Path Path to the directory, where the sound file is located. Default is `C:\\Program Files\\DCS-SimpleRadio-Standalone`.
-- @param #string Path Path to the directory, where the sound file is located. Default is `C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio`.
-- @return #MSRS self
function MSRS:SetPath(Path)
self:F( {Path=Path} )
-- Set path.
self.path=Path or "C:\\Program Files\\DCS-SimpleRadio-Standalone"
self.path=Path or "C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio"
-- Remove (back)slashes.
local n=1 ; local nmax=1000
@@ -1817,7 +1955,7 @@ end
--
-- -- Moose MSRS default Config
-- MSRS_Config = {
-- Path = "C:\\Program Files\\DCS-SimpleRadio-Standalone", -- Path to SRS install directory.
-- Path = "C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio", -- Path to SRS install directory.
-- Port = 5002, -- Port of SRS server. Default 5002.
-- Backend = "srsexe", -- Interface to SRS: "srsexe" or "grpc".
-- Frequency = {127, 243}, -- Default frequences. Must be a table 1..n entries!
@@ -1837,7 +1975,7 @@ end
-- -- Google Cloud
-- gcloud = {
-- voice = "en-GB-Standard-A", -- The Google Cloud voice to use (see https://cloud.google.com/text-to-speech/docs/voices).
-- credentials="C:\\Program Files\\DCS-SimpleRadio-Standalone\\yourfilename.json", -- Full path to credentials JSON file (only for SRS-TTS.exe backend)
-- credentials="C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio\\yourfilename.json", -- Full path to credentials JSON file (only for SRS-TTS.exe backend)
-- key="Your access Key", -- Google API access key (only for DCS-gRPC backend)
-- },
-- -- Amazon Web Service
@@ -1905,7 +2043,7 @@ function MSRS:LoadConfigFile(Path,Filename)
local Self = self or MSRS --#MSRS
Self.path = MSRS_Config.Path or "C:\\Program Files\\DCS-SimpleRadio-Standalone"
Self.path = MSRS_Config.Path or "C:\\Program Files\\DCS-SimpleRadio-Standalone\\ExternalAudio"
Self.port = MSRS_Config.Port or 5002
Self.backend = MSRS_Config.Backend or MSRS.Backend.SRSEXE
Self.frequencies = MSRS_Config.Frequency or {127,243}
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
@@ -168,16 +168,25 @@
-- * @{#CONTROLLABLE.OptionAlarmStateGreen}
-- * @{#CONTROLLABLE.OptionAlarmStateRed}
--
-- ## 5.4) Jettison weapons:
-- ## 5.4) [AIR] Jettison weapons:
--
-- * @{#CONTROLLABLE.OptionAllowJettisonWeaponsOnThreat}
-- * @{#CONTROLLABLE.OptionKeepWeaponsOnThreat}
--
-- ## 5.5) Air-2-Air missile attack range:
-- ## 5.5) [AIR] Air-2-Air missile attack range:
-- * @{#CONTROLLABLE.OptionAAAttackRange}(): Defines the usage of A2A missiles against possible targets.
--
-- # 6) [GROUND] IR Maker Beacons for GROUPs and UNITs
-- * @{#CONTROLLABLE:NewIRMarker}(): Create a blinking IR Marker on a GROUP or UNIT.
--
-- # 7) [HELICOPTER] Units prefer vertical landing and takeoffs:
-- * @{#CONTROLLABLE.OptionPreferVerticalLanding}(): Set aircraft to prefer vertical landing and takeoff.
--
-- # 8) [AIRCRAFT] Landing approach options
-- * @{#CONTROLLABLE.SetOptionLandingStraightIn}(): Landing approach straight in.
-- * @{#CONTROLLABLE.SetOptionLandingForcePair}(): Landing approach in pairs for groups > 1 unit.
-- * @{#CONTROLLABLE.SetOptionLandingRestrictPair}(): Landing approach single.
-- * @{#CONTROLLABLE.SetOptionLandingOverheadBreak}(): Landing approach overhead break.
--
-- @field #CONTROLLABLE
CONTROLLABLE = {
@@ -400,7 +409,7 @@ function CONTROLLABLE:SetTask( DCSTask, WaitTime )
local Controller = self:_GetController()
-- self:I( "Before SetTask" )
Controller:setTask( DCSTask )
-- AI_FORMATION class (used by RESCUEHELO) calls SetTask twice per second! hence spamming the DCS log file ==> setting this to trace.
-- FORMATION class (used by RESCUEHELO) calls SetTask twice per second! hence spamming the DCS log file ==> setting this to trace.
self:T( { ControllableName = self:GetName(), DCSTask = DCSTask } )
else
BASE:E( { DCSControllableName .. " is not alive anymore.", DCSTask = DCSTask } )
@@ -1432,7 +1441,7 @@ end
-- @param #number Speed The speed [m/s] flying when holding the position.
-- @return #CONTROLLABLE self
function CONTROLLABLE:TaskOrbitCircleAtVec2( Point, Altitude, Speed )
self:F2( { self.ControllableName, Point, Altitude, Speed } )
--self:F2( { self.ControllableName, Point, Altitude, Speed } )
local DCSTask = {
id = 'Orbit',
@@ -2846,7 +2855,7 @@ do -- Route methods
-- @param Core.Zone#ZONE Zone The zone where to route to.
-- @param #boolean Randomize Defines whether to target point gets randomized within the Zone.
-- @param #number Speed The speed in m/s. Default is 5.555 m/s = 20 km/h.
-- @param Core.Base#FORMATION Formation The formation string.
-- @param DCS#FORMATION Formation The formation string.
function CONTROLLABLE:TaskRouteToZone( Zone, Randomize, Speed, Formation )
self:F2( Zone )
@@ -2906,7 +2915,7 @@ do -- Route methods
-- @param #CONTROLLABLE self
-- @param DCS#Vec2 Vec2 The Vec2 where to route to.
-- @param #number Speed The speed in m/s. Default is 5.555 m/s = 20 km/h.
-- @param Core.Base#FORMATION Formation The formation string.
-- @param DCS#FORMATION Formation The formation string.
function CONTROLLABLE:TaskRouteToVec2( Vec2, Speed, Formation )
local DCSControllable = self:GetDCSObject()
@@ -3629,6 +3638,26 @@ function CONTROLLABLE:OptionROTPassiveDefense()
return nil
end
--- Helicopter - prefer vertical landing.
-- @param #CONTROLLABLE self
-- @return #CONTROLLABLE self
function CONTROLLABLE:OptionPreferVerticalLanding()
self:F2( { self.ControllableName } )
local DCSControllable = self:GetDCSObject()
if DCSControllable then
local Controller = self:_GetController()
if self:IsAir() then
Controller:setOption( AI.Option.Air.id.PREFER_VERTICAL, true )
end
return self
end
return nil
end
--- Can the CONTROLLABLE evade on enemy fire?
-- @param #CONTROLLABLE self
-- @return #boolean
@@ -4134,7 +4163,7 @@ function CONTROLLABLE:OptionRestrictBurner( RestrictBurner )
end
--- Sets Controllable Option for A2A attack range for AIR FIGHTER units.
--- [AIR] Sets Controllable Option for A2A attack range for AIR FIGHTER units.
-- @param #CONTROLLABLE self
-- @param #number range Defines the range
-- @return #CONTROLLABLE self
@@ -4159,6 +4188,66 @@ function CONTROLLABLE:OptionAAAttackRange( range )
return nil
end
--- [GROUND/AAA] Sets Controllable Option for Ground AAA minimum firing height.
-- @param #CONTROLLABLE self
-- @param #number meters The minimum height in meters.
-- @return #CONTROLLABLE self
function CONTROLLABLE:OptionAAAMinFiringHeightMeters(meters)
self:F2( { self.ControllableName } )
local meters = meters or 20
local DCSControllable = self:GetDCSObject()
if DCSControllable then
local Controller = self:_GetController()
if Controller then
if self:IsGround() then
self:SetOption(27, meters)
end
end
return self
end
return nil
end
--- [GROUND/AAA] Sets Controllable Option for Ground AAA maximum firing height.
-- @param #CONTROLLABLE self
-- @param #number meters The maximum height in meters.
-- @return #CONTROLLABLE self
function CONTROLLABLE:OptionAAAMaxFiringHeightMeters(meters)
self:F2( { self.ControllableName } )
local meters = meters or 1000
local DCSControllable = self:GetDCSObject()
if DCSControllable then
local Controller = self:_GetController()
if Controller then
if self:IsGround() then
self:SetOption(29, meters)
end
end
return self
end
return nil
end
--- [GROUND/AAA] Sets Controllable Option for Ground AAA minimum firing height.
-- @param #CONTROLLABLE self
-- @param #number feet The minimum height in feet.
-- @return #CONTROLLABLE self
function CONTROLLABLE:OptionAAAMinFiringHeightFeet(feet)
self:F2( { self.ControllableName } )
local feet = feet or 60
return self:OptionAAAMinFiringHeightMeters(UTILS.FeetToMeters(feet))
end
--- [GROUND/AAA] Sets Controllable Option for Ground AAA maximum firing height.
-- @param #CONTROLLABLE self
-- @param #number feet The maximum height in feet.
-- @return #CONTROLLABLE self
function CONTROLLABLE:OptionAAAMaxFiringHeightfeet(feet)
self:F2( { self.ControllableName } )
local feet = feet or 3000
return self:OptionAAAMaxFiringHeightMeters(UTILS.FeetToMeters(feet))
end
--- Defines the range at which a GROUND unit/group is allowed to use its weapons automatically.
-- @param #CONTROLLABLE self
-- @param #number EngageRange Engage range limit in percent (a number between 0 and 100). Default 100.
@@ -4183,6 +4272,50 @@ function CONTROLLABLE:OptionEngageRange( EngageRange )
return nil
end
--- [AIR] Set how the AI lands on an airfield. Here: Straight in.
-- @param #CONTROLLABLE self
-- @return #CONTROLLABLE self
function CONTROLLABLE:SetOptionLandingStraightIn()
self:F2( { self.ControllableName } )
if self:IsAir() then
self:SetOption("36","0")
end
return self
end
--- [AIR] Set how the AI lands on an airfield. Here: In pairs (if > 1 aircraft in group)
-- @param #CONTROLLABLE self
-- @return #CONTROLLABLE self
function CONTROLLABLE:SetOptionLandingForcePair()
self:F2( { self.ControllableName } )
if self:IsAir() then
self:SetOption("36","1")
end
return self
end
--- [AIR] Set how the AI lands on an airfield. Here: No landing in pairs.
-- @param #CONTROLLABLE self
-- @return #CONTROLLABLE self
function CONTROLLABLE:SetOptionLandingRestrictPair()
self:F2( { self.ControllableName } )
if self:IsAir() then
self:SetOption("36","2")
end
return self
end
--- [AIR] Set how the AI lands on an airfield. Here: Overhead break.
-- @param #CONTROLLABLE self
-- @return #CONTROLLABLE self
function CONTROLLABLE:SetOptionLandingOverheadBreak()
self:F2( { self.ControllableName } )
if self:IsAir() then
self:SetOption("36","3")
end
return self
end
--- [AIR] Set how the AI uses the onboard radar.
-- @param #CONTROLLABLE self
-- @param #number Option Options are: `NEVER = 0, FOR_ATTACK_ONLY = 1,FOR_SEARCH_IF_REQUIRED = 2, FOR_CONTINUOUS_SEARCH = 3`
@@ -108,6 +108,9 @@ DYNAMICCARGO.State = {
-- @type DYNAMICCARGO.AircraftTypes
DYNAMICCARGO.AircraftTypes = {
["CH-47Fbl1"] = "CH-47Fbl1",
["Mi-8MTV2"] = "Mi-8MTV2",
["Mi-8MT"] = "Mi-8MT",
["C-130J-30"] = "C-130J-30",
}
--- Helo types possible.
@@ -120,17 +123,36 @@ DYNAMICCARGO.AircraftDimensions = {
["length"] = 11,
["ropelength"] = 30,
},
["Mi-8MTV2"] = {
["width"] = 6,
["height"] = 6,
["length"] = 15,
["ropelength"] = 30,
},
["Mi-8MT"] = {
["width"] = 6,
["height"] = 6,
["length"] = 15,
["ropelength"] = 30,
},
["C-130J-30"] = {
["width"] = 4,
["height"] = 12,
["length"] = 35,
["ropelength"] = 0,
},
}
--- DYNAMICCARGO class version.
-- @field #string version
DYNAMICCARGO.version="0.0.7"
DYNAMICCARGO.version="0.1.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: A lot...
-- DONE: Added Mi-8 type and dimensions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
@@ -512,7 +534,10 @@ function DYNAMICCARGO:_UpdatePosition()
---------------
-- REMOVED Cargo
---------------
if self.timer and self.timer:IsRunning() then self.timer:Stop() end
if self.timer and self.timer:IsRunning() then
self.timer:Stop()
self.timer=nil
end
self:T(self.lid.." dead! " ..self.CargoState.."-> REMOVED")
self.CargoState = DYNAMICCARGO.State.REMOVED
_DATABASE:CreateEventDynamicCargoRemoved(self)
@@ -520,6 +545,24 @@ function DYNAMICCARGO:_UpdatePosition()
return self
end
--- [USER] Destroy a DYNAMICCARGO object.
-- @param #DYNAMICCARGO self
-- @param #boolean GenerateEvent Set to false to remove an item silently. Defaults to true.
-- @return #boolean Return Returns nil if the object could not be found, else returns true.
function DYNAMICCARGO:Destroy(GenerateEvent)
local DCSObject = self:GetDCSObject()
if DCSObject then
local GenerateEvent = (GenerateEvent ~= nil and GenerateEvent == false) and false or true
if GenerateEvent and GenerateEvent == true then
self:CreateEventDead( timer.getTime(), DCSObject )
end
DCSObject:destroy()
self:_UpdatePosition()
return true
end
return nil
end
--- [Internal] Track helos for loaded/unloaded decision making.
-- @param Wrapper.Client#CLIENT client
-- @return #boolean IsIn
+108 -12
View File
@@ -230,6 +230,8 @@ GROUP.Attribute = {
GROUND_EWR="Ground_EWR",
GROUND_AAA="Ground_AAA",
GROUND_SAM="Ground_SAM",
GROUND_SHORAD="Ground_SHORAD",
GROUND_BALLISTICMISSILE="Ground_BallisticMissile",
GROUND_OTHER="Ground_OtherGround",
NAVAL_AIRCRAFTCARRIER="Naval_AircraftCarrier",
NAVAL_WARSHIP="Naval_WarShip",
@@ -1056,9 +1058,12 @@ function GROUP:GetTypeName()
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local GroupTypeName = DCSGroup:getUnit(1):getTypeName()
--self:T3( GroupTypeName )
return( GroupTypeName )
local unit = DCSGroup:getUnit(1)
if unit then
local GroupTypeName = unit:getTypeName()
--self:T3( GroupTypeName )
return( GroupTypeName )
end
end
return nil
@@ -1073,9 +1078,12 @@ function GROUP:GetNatoReportingName()
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local GroupTypeName = DCSGroup:getUnit(1):getTypeName()
--self:T3( GroupTypeName )
return UTILS.GetReportingName(GroupTypeName)
local unit = DCSGroup:getUnit(1)
if unit then
local GroupTypeName = unit:getTypeName()
--self:T3( GroupTypeName )
return UTILS.GetReportingName(GroupTypeName)
end
end
return "Bogey"
@@ -1091,9 +1099,12 @@ function GROUP:GetPlayerName()
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local PlayerName = DCSGroup:getUnit(1):getPlayerName()
--self:T3( PlayerName )
return( PlayerName )
local unit = DCSGroup:getUnit(1)
if unit then
local PlayerName = unit:getPlayerName()
--self:T3( PlayerName )
return( PlayerName )
end
end
return nil
@@ -1109,9 +1120,12 @@ function GROUP:GetCallsign()
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local GroupCallSign = DCSGroup:getUnit(1):getCallsign()
--self:T3( GroupCallSign )
return GroupCallSign
local unit = DCSGroup:getUnit(1)
if unit then
local GroupCallSign = unit:getCallsign()
--self:T3( GroupCallSign )
return GroupCallSign
end
end
BASE:E( { "Cannot GetCallsign", Positionable = self, Alive = self:IsAlive() } )
@@ -1150,6 +1164,23 @@ function GROUP:GetVec3()
return nil
end
--- Returns the current {@Core.Vector#VECTOR} of the first Unit in the GROUP.
-- @param #GROUP self
-- @return Core.Vector#VECTOR Vector of the first Unit of the GROUP or nil if cannot be found.
function GROUP:GetVector()
-- Get first unit.
local unit=self:GetUnit(1)
if unit then
local vector=unit:GetVector()
return vector
end
self:E("ERROR: Cannot get Vector of group "..tostring(self.GroupName))
return nil
end
--- Returns the average Vec3 vector of the Units in the GROUP.
-- @param #GROUP self
-- @return DCS#Vec3 Current Vec3 of the GROUP or nil if cannot be found.
@@ -2230,6 +2261,10 @@ function GROUP:Respawn( Template, Reset )
--UTILS.PrintTableToLog(Template)
if self.ValidateAndRepositionGroundUnits then
UTILS.ValidateAndRepositionGroundUnits(Template.units)
end
-- Spawn new group.
self:ScheduleOnce(0.1,_DATABASE.Spawn,_DATABASE,Template)
--_DATABASE:Spawn(Template)
@@ -2638,6 +2673,8 @@ function GROUP:GetAttribute()
local artillery=self:HasAttribute("Artillery")
local tank=self:HasAttribute("Old Tanks") or self:HasAttribute("Modern Tanks") or self:HasAttribute("Tanks")
local aaa=self:HasAttribute("AAA") and (not self:HasAttribute("SAM elements"))
local ballisticMissile=artillery and self:HasAttribute("SS_missile")
local shorad=self:HasAttribute("SR SAM")
local ewr=self:HasAttribute("EWR")
local ifv=self:HasAttribute("IFV")
local sam=self:HasAttribute("SAM elements") or self:HasAttribute("Optical Tracker")
@@ -2679,6 +2716,8 @@ function GROUP:GetAttribute()
attribute=GROUP.Attribute.GROUND_SAM
elseif aaa then
attribute=GROUP.Attribute.GROUND_AAA
elseif artillery and ballisticMissile then
attribute=GROUP.Attribute.GROUND_BALLISTICMISSILE
elseif artillery then
attribute=GROUP.Attribute.GROUND_ARTILLERY
elseif tank then
@@ -3191,3 +3230,60 @@ function GROUP:IsAAA()
end
return isAAA
end
--- This function uses Disposition and other fallback logic to find better ground positions for ground units.
--- NOTE: This is not a spawn randomizer.
--- It will try to find clear ground locations avoiding trees, water, roads, runways, map scenery, statics and other units in the area and modifies the provided positions table.
--- Maintains the original layout and unit positions as close as possible by searching for the next closest valid position to each unit.
--- Uses UTILS.ValidateAndRepositionGroundUnits.
-- @param #GROUP self
-- @param #boolean Enabled Enable/disable the feature.
function GROUP:SetValidateAndRepositionGroundUnits(Enabled)
self.ValidateAndRepositionGroundUnits = Enabled
end
--- Get the bounding box of the group combining UNIT:GetBoundingBox() units.
-- @param #GROUP self
-- @return DCS#Box3 The bounding box of the GROUP.
-- @return #nil The GROUP does not have any alive units.
function GROUP:GetBoundingBox()
local bbox = { min = { x = math.huge, y = math.huge, z = math.huge },
max = { x = -math.huge, y = -math.huge, z = -math.huge }
}
local Units = self:GetUnits() or {}
if #Units == 0 then
return nil
end
for _, unit in pairs(Units) do
if unit and unit:IsAlive() then
local ubox = unit:GetBoundingBox()
if ubox then
if ubox.min.x < bbox.min.x then
bbox.min.x = ubox.min.x
end
if ubox.min.y < bbox.min.y then
bbox.min.y = ubox.min.y
end
if ubox.min.z < bbox.min.z then
bbox.min.z = ubox.min.z
end
if ubox.max.x > bbox.max.x then
bbox.max.x = ubox.max.x
end
if ubox.max.y > bbox.max.y then
bbox.max.y = ubox.max.y
end
if ubox.max.z > bbox.max.z then
bbox.max.z = ubox.max.z
end
end
end
end
return bbox
end
@@ -170,6 +170,27 @@ function IDENTIFIABLE:GetCoalition()
return nil
end
--- Returns true if identifiable is of RED coalition.
-- @param #IDENTIFIABLE self
-- @return #boolean If the identifiable is red.
function IDENTIFIABLE:IsRed()
return self:GetCoalition() == coalition.side.RED
end
--- Returns true if identifiable is of BLUE coalition.
-- @param #IDENTIFIABLE self
-- @return #boolean If the identifiable is blue.
function IDENTIFIABLE:IsBlue()
return self:GetCoalition() == coalition.side.BLUE
end
--- Returns true if identifiable is of NEUTRAL coalition.
-- @param #IDENTIFIABLE self
-- @return #boolean If the identifiable is neutral.
function IDENTIFIABLE:IsNeutral()
return self:GetCoalition() == coalition.side.NEUTRAL
end
--- Returns the name of the coalition of the Identifiable.
-- @param #IDENTIFIABLE self
-- @return #string The name of the coalition.
@@ -219,6 +219,25 @@ function POSITIONABLE:GetOrientationZ()
end
end
--- Returns the vectors of the orientation of the object:
-- X is the orientation parallel to the movement of the object, Z perpendicular and Y vertical orientation.
-- @param #POSITIONABLE self
-- @return Core.Vector#VECTOR X orientation, i.e. parallel to the direction of movement.
-- @return Core.Vector#VECTOR Y orientation, i.e. vertical.
-- @return Core.Vector#VECTOR Z orientation, i.e. perpendicular to the direction of movement.
function POSITIONABLE:GetOrientationVectors()
local position = self:GetPosition()
if position then
local vecx=VECTOR:NewFromVec(position.x)
local vecy=VECTOR:NewFromVec(position.y)
local vecz=VECTOR:NewFromVec(position.z)
return vecx, vecy, vecz
else
BASE:E( { "Cannot GetOrientation", Positionable = self, Alive = self:IsAlive() } )
return nil, nil, nil
end
end
--- Returns the @{DCS#Position3} position vectors indicating the point and direction vectors in 3D of the POSITIONABLE within the mission.
-- @param #POSITIONABLE self
-- @return DCS#Position The 3D position vectors of the POSITIONABLE.
@@ -246,18 +265,20 @@ end
function POSITIONABLE:GetVec3()
local DCSPositionable = self:GetDCSObject()
if DCSPositionable then
--local status, vec3 = pcall(
-- function()
-- local vec3 = DCSPositionable:getPoint()
-- return vec3
--end
--)
local vec3 = DCSPositionable:getPoint()
--if status then
return vec3
--else
--self:E( { "Cannot get Vec3 from DCS Object", Positionable = self, Alive = self:IsAlive() } )
--end
if not vec3 then
local pos = DCSPositionable:getPosition()
if pos and pos.p then
vec3 = pos.p
else
self:E( { "Cannot get the position from DCS Object for GetVec3", Positionable = self, Alive = self:IsAlive() } )
end
end
return vec3
end
-- ERROR!
self:E( { "Cannot get the Positionable DCS Object for GetVec3", Positionable = self, Alive = self:IsAlive() } )
@@ -284,6 +305,27 @@ function POSITIONABLE:GetVec2()
return nil
end
--- Returns the @{Core.Vector#VECTOR} indicating the 3D position of the object on the map.
-- @param #POSITIONABLE self
-- @return Core.Vector#VECTOR The 3D vector.
function POSITIONABLE:GetVector()
local DCSPositionable = self:GetDCSObject()
if DCSPositionable then
local Vec3 = DCSPositionable:getPoint() -- DCS#Vec3
local vector=VECTOR:NewFromVec(Vec3)
return vector
end
self:E( { "Cannot GetVec2", Positionable = self, Alive = self:IsAlive() } )
return nil
end
--- Returns a COORDINATE object indicating the point in 2D of the POSITIONABLE within the mission.
-- @param #POSITIONABLE self
-- @return Core.Point#COORDINATE The 3D point vector of the POSITIONABLE.
@@ -359,15 +401,17 @@ function POSITIONABLE:GetCoord()
-- Get the current position.
local PositionableVec3 = self:GetVec3()
if self.coordinate then
-- Update COORDINATE from 3D vector.
self.coordinate:UpdateFromVec3( PositionableVec3 )
else
-- New COORDINATE.
self.coordinate = COORDINATE:NewFromVec3( PositionableVec3 )
end
if PositionableVec3 then
if self.coordinate then
-- Update COORDINATE from 3D vector.
self.coordinate:UpdateFromVec3( PositionableVec3 )
else
-- New COORDINATE.
self.coordinate = COORDINATE:NewFromVec3( PositionableVec3 )
end
return self.coordinate
return self.coordinate
end
end
-- Error message.
@@ -388,13 +432,13 @@ function POSITIONABLE:GetCoordinate()
-- Get the current position.
local PositionableVec3 = self:GetVec3()
local coord=COORDINATE:NewFromVec3(PositionableVec3)
local heading = self:GetHeading()
coord.Heading = heading
-- Return a new coordiante object.
return coord
if PositionableVec3 then
local coord=COORDINATE:NewFromVec3(PositionableVec3)
local heading = self:GetHeading()
coord.Heading = heading
-- Return a new coordiante object.
return coord
end
end
-- Error message.
@@ -910,6 +954,24 @@ function POSITIONABLE:GetVelocityVec3()
return nil
end
--- Returns the velocity vector.
-- @param #POSITIONABLE self
-- @return Core.Vector#VECTOR The velocity vector or `nil` if the object does not exist.
function POSITIONABLE:GetVelocityVector()
local DCSPositionable = self:GetDCSObject()
if DCSPositionable and DCSPositionable:isExist() then
local vec3 = DCSPositionable:getVelocity()
local vector=VECTOR:NewFromVec(vec3)
return vector
end
BASE:E( { "Cannot GetVelocityVector", Positionable = self, Alive = self:IsAlive() } )
return nil
end
--- Get relative velocity with respect to another POSITIONABLE.
-- @param #POSITIONABLE self
-- @param #POSITIONABLE Positionable Other POSITIONABLE.
@@ -1861,6 +1923,7 @@ do -- Cargo
["HL_DSHK"] = 6*POSITIONABLE.DefaultInfantryWeight,
["CCKW_353"] = 16*POSITIONABLE.DefaultInfantryWeight, --GMC CCKW 2½-ton 6×6 truck, estimating 16 soldiers,
["MaxxPro_MRAP"] = 7*POSITIONABLE.DefaultInfantryWeight,
["Sd_Kfz_251"] = 10*POSITIONABLE.DefaultInfantryWeight,
}
}
+166 -27
View File
@@ -18,7 +18,13 @@
-- @field #string SceneryName Name of the scenery object.
-- @field DCS#Object SceneryObject DCS scenery object.
-- @field #number Life0 Initial life points.
-- @field #table Properties
-- @field #table Properties.
-- @field #number ID.
-- @field Core.Zone#ZONE_POLYGON SceneryZone.
-- @field Core.Point#COORDINATE Coordinate.
-- @field DCS#Vec2 Vec2.
-- @field DCS#Vec3 Vec3.
-- @field Core.Vector#VECTOR Vector.
-- @extends Wrapper.Positionable#POSITIONABLE
@@ -35,27 +41,79 @@ SCENERY = {
ClassName = "SCENERY",
}
_SCENERY = {}
--- Register scenery object as POSITIONABLE.
--@param #SCENERY self
--@param #string SceneryName Scenery name.
--@param DCS#Object SceneryObject DCS scenery object.
--@param Core.Zone#ZONE_POLYGON SceneryZone (optional) The zone object.
--@return #SCENERY Scenery object.
function SCENERY:Register( SceneryName, SceneryObject )
function SCENERY:Register( SceneryName, SceneryObject, SceneryZone )
local ID = (SceneryObject and SceneryObject.getID) and SceneryObject:getID() or SceneryName
if _SCENERY[ID] and _SCENERY[ID].SceneryObject == nil then
_SCENERY[ID].SceneryObject=SceneryObject
SCENERY._UpdateFromDCSObject(_SCENERY[ID])
end
if _SCENERY[ID] then return _SCENERY[ID] end
local self = BASE:Inherit( self, POSITIONABLE:New( SceneryName ) )
self.SceneryName = tostring(SceneryName)
self.ID = ID
self.SceneryObject = SceneryObject
self.SceneryZone = SceneryZone
if SceneryZone then
self.Vec3 = SceneryZone:GetVec3()
self.Vec2 = SceneryZone:GetVec2()
self.Vector = (self.Vec3 and VECTOR) and VECTOR:NewFromVec(self.Vec3) or nil
end
if SceneryObject then
local vec3 = SceneryObject:getPoint()
self.Vec3 = { x = vec3.x, y = vec3.y, z = vec3.z }
self.Vec2 = { x = vec3.x, y = vec3.z }
self.Vector = (self.Vec3 and VECTOR) and VECTOR:NewFromVec(self.Vec3) or nil
end
if self.SceneryObject and self.SceneryObject.getLife then -- fix some objects do not have all functions
self.Life0 = self.SceneryObject:getLife() or 0
self.Life0 = self.SceneryObject:getLife() or 1
else
self.Life0 = 0
self.Life0 = 1
end
self.Properties = {}
_SCENERY[self.ID] = self
return self
end
--- [INTERNAL] Update data
-- @param Wrapper.Scenery#SCENERY Scenery The object to update.
function SCENERY._UpdateFromDCSObject(Scenery)
env.info("APPLE _UpdateFromDCSObject "..tostring(Scenery.SceneryName))
local self=Scenery
if self.Vec2 == nil and self.SceneryObject ~= nil then
self.Vec3 = self.SceneryObject:getPoint()
if self.Vec3 then
self.Vec2 = {x=self.Vec3.x,y=self.Vec3.z}
self.Vector = VECTOR:NewFromVec(self.Vec3)
end
end
if not self.Life0 or self.Life0 == 1 then
if self.SceneryObject and self.SceneryObject.getLife() then
self.Life = self.SceneryObject:getLife() or 1
self.Life0 = self.Life
end
end
return self
end
@@ -99,6 +157,39 @@ function SCENERY:GetName()
return self.SceneryName
end
--- Obtain object coordinate.
--@param #SCENERY self
--@return Core.Point#COORDINATE Coordinate
function SCENERY:GetCoordinate()
if self.Coordinate then
return self.Coordinate
elseif self.Vec3 then
self.Coordinate = COORDINATE:NewFromVec3(self.Vec3):SetAlt()
end
return self.Coordinate
end
--- Obtain object coordinate.
--@param #SCENERY self
--@return DCS#Vec3 Vec3
function SCENERY:GetVec3()
return self.Vec3
end
--- Obtain object coordinate.
--@param #SCENERY self
--@return DCS#Vec2 Vec2
function SCENERY:GetVec2()
return self.Vec2
end
--- Obtain object coordinate.
--@param #SCENERY self
--@return Core.Vector#VECTOR Vector
function SCENERY:GetVector()
return self.Vector
end
--- Obtain DCS Object from the SCENERY Object.
--@param #SCENERY self
--@return DCS#Object DCS scenery object.
@@ -106,6 +197,13 @@ function SCENERY:GetDCSObject()
return self.SceneryObject
end
--- Obtain object ID.
--@param #SCENERY self
--@return #string Name
function SCENERY:GetID()
return self.ID
end
--- Get current life points from the SCENERY Object. Note - Some scenery objects always have 0 life points.
-- **CAVEAT**: Some objects change their life value or "hitpoints" **after** the first hit. Hence we will adjust the life0 value to 120%
-- of the last life value if life exceeds life0 (initial life) at any point. Thus will will get a smooth percentage decrease, if you use this e.g. as success
@@ -113,12 +211,14 @@ end
--@param #SCENERY self
--@return #number life
function SCENERY:GetLife()
local life = 0
local life = 1
if self.SceneryObject and self.SceneryObject.getLife then
life = self.SceneryObject:getLife()
if life > self.Life0 then
self.Life0 = math.floor(life * 1.2)
end
elseif self.Life then
life = self.Life
end
return life
end
@@ -176,12 +276,19 @@ end
--- Find a SCENERY object from its name or id. Since SCENERY isn't registered in the Moose database (just too many objects per map), we need to do a scan first
-- to find the correct object.
--@param #SCENERY self
--@param #string Name The name/id of the scenery object as taken from the ME. Ex. '595785449'
--@param Core.Point#COORDINATE Coordinate Where to find the scenery object
--@param #number Radius (optional) Search radius around coordinate, defaults to 100
--@return #SCENERY Scenery Object or `nil` if it cannot be found
function SCENERY:FindByName(Name, Coordinate, Radius, Role)
--@param #string Name The name/id of the scenery object as taken from the ME. Ex. '595785449'.
--@param Core.Point#COORDINATE Coordinate Where to find the scenery object.
--@param #number Radius (optional) Search radius around coordinate, defaults to 100.
--@param #string Role (optional) The role if set on the zone object.
--@param Core.Zone#ZONE_POLYGON Zone (optional) The Zone where the scenery is located.
--@return #SCENERY Scenery Object or `nil` if it cannot be found.
function SCENERY:FindByName(Name, Coordinate, Radius, Role, Zone)
--BASE:I("Coordinate x = "..Coordinate.x .. " y = "..Coordinate.y.." z = "..Coordinate.z)
local findme = self:_FindByName(Name)
if findme then return findme end
local radius = Radius or 100
local name = Name or "unknown"
local scenery = nil
@@ -193,12 +300,13 @@ function SCENERY:FindByName(Name, Coordinate, Radius, Role)
local function SceneryScan(scoordinate, sradius, sname)
if scoordinate ~= nil then
local Vec2 = scoordinate:GetVec2()
local scanzone = ZONE_RADIUS:New("Zone-"..sname,Vec2,sradius,true)
local scanzone = ZONE_RADIUS:New("Zone-"..sname,Vec2,sradius)
scanzone:Scan({Object.Category.SCENERY})
local scanned = scanzone:GetScannedSceneryObjects()
local rscenery = nil -- Wrapper.Scenery#SCENERY
for _,_scenery in pairs(scanned) do
local scenery = _scenery -- Wrapper.Scenery#SCENERY
--BASE:I({tostring(scenery.SceneryName),tostring(sname)})
if tostring(scenery.SceneryName) == tostring(sname) then
rscenery = scenery
if Role then rscenery:SetProperty("ROLE",Role) end
@@ -211,13 +319,36 @@ function SCENERY:FindByName(Name, Coordinate, Radius, Role)
end
if Coordinate then
--BASE:I("Coordinate Scenery Scan")
scenery = SceneryScan(Coordinate, radius, name)
end
if not scenery then scenery = SCENERY:Register(Name,nil,Zone) end
return scenery
end
--- Find a SCENERY object that was previously registered(!) by it's ID.
-- @param #SCENERY self
-- @param #number ID
-- @return Wrapper.Scenery#SCENERY Scenery or nil if it could not be found
function SCENERY:FindByID(ID)
return _SCENERY[ID]
end
--- Find a SCENERY object that was previously registered(!) by it's name.
-- @param #SCENERY self
-- @param #string Name
-- @return Wrapper.Scenery#SCENERY Scenery or nil if it could not be found
function SCENERY:_FindByName(Name)
for _id,_object in pairs(_SCENERY) do
if _object and _object.GetName and _object:GetName() then
local name = _object:GetName()
if Name == name then return _object end
end
end
return nil
end
--- Find a SCENERY object from its name or id. Since SCENERY isn't registered in the Moose database (just too many objects per map), we need to do a scan first
-- to find the correct object.
--@param #SCENERY self
@@ -231,8 +362,8 @@ function SCENERY:FindByNameInZone(Name, Zone, Radius)
if type(Zone) == "string" then
Zone = ZONE:FindByName(Zone)
end
local coordinate = Zone:GetCoordinate()
return self:FindByName(Name,coordinate,Radius,Zone:GetProperty("ROLE"))
local coordinate = Zone:GetCoordinate():SetAlt()
return self:FindByName(Name,coordinate,Radius,Zone:GetProperty("ROLE"),Zone)
end
--- Find a SCENERY object from its zone name. Since SCENERY isn't registered in the Moose database (just too many objects per map), we need to do a scan first
@@ -241,14 +372,18 @@ end
--@param #string ZoneName The name of the scenery zone as created with a right-click on the map in the mission editor and select "assigned to...". Can be handed over as ZONE object.
--@return #SCENERY First found Scenery Object or `nil` if it cannot be found
function SCENERY:FindByZoneName( ZoneName )
--BASE:I(ZoneName)
local zone = ZoneName -- Core.Zone#ZONE_BASE
if type(ZoneName) == "string" then
zone = ZONE:FindByName(ZoneName) -- Core.Zone#ZONE_POLYGON
end
local _id = zone:GetProperty('OBJECT ID')
--local properties = zone:GetAllProperties() or {}
--BASE:I(string.format("Object ID is: %s",_id or "none"))
--BASE:T("Object ID ".._id)
--BASE:I("Object ID ".._id)
if not _id then
-- this zone has no object ID
BASE:E("**** Zone without object ID: "..ZoneName.." | Type: "..tostring(zone.ClassName))
@@ -257,23 +392,27 @@ function SCENERY:FindByZoneName( ZoneName )
local scanned = zone:GetScannedSceneryObjects()
for _,_scenery in (scanned) do
local scenery = _scenery -- Wrapper.Scenery#SCENERY
if scenery:IsAlive() then
--if scenery:IsAlive() then
local role = zone:GetProperty("ROLE")
if role then scenery:SetProperty("ROLE",role) end
return scenery
end
--end
end
return nil
else
return self:FindByName(_id, zone:GetCoordinate(),nil,zone:GetProperty("ROLE"))
local coordinate = zone:GetCoordinate()
coordinate:SetAlt()
return self:FindByName(_id, coordinate,nil,zone:GetProperty("ROLE"),zone)
end
else
return self:FindByName(_id, zone:GetCoordinate(),nil,zone:GetProperty("ROLE"))
local coordinate = zone:GetCoordinate()
coordinate:SetAlt()
return self:FindByName(_id, coordinate,nil,zone:GetProperty("ROLE"),zone)
end
end
--- Scan and find all SCENERY objects from a zone by zone-name. Since SCENERY isn't registered in the Moose database (just too many objects per map), we need to do a scan first
-- to find the correct object.
-- to find the correct object. Might return nil!
--@param #SCENERY self
--@param #string ZoneName The name of the zone, can be handed as ZONE_RADIUS or ZONE_POLYGON object
--@return #table of SCENERY Objects, or `nil` if nothing found
@@ -295,7 +434,7 @@ function SCENERY:FindAllByZoneName( ZoneName )
return nil
end
else
local obj = self:FindByName(_id, zone:GetCoordinate(),nil,zone:GetProperty("ROLE"))
local obj = self:FindByName(_id, zone:GetCoordinate():SetAlt(),nil,zone:GetProperty("ROLE"), zone)
if obj then
return {obj}
else
@@ -64,6 +64,9 @@ function STATIC:Register( StaticName )
else
self:E(string.format("Static object %s does not exist!", tostring(self.StaticName)))
end
-- Cache position
self._vec3 = self:GetVec3()
return self
end
@@ -86,6 +89,30 @@ function STATIC:GetLife()
return nil
end
--- Get the position of the STATIC even if it is not alive.
-- @param #STATIC self
-- @return DCS#Vec2 The 2D point vector of the POSITIONABLE.
-- @return #nil The position was not cached.
function STATIC:GetVec2Cached()
local vec2 = self:GetVec2()
if not vec2 and self._vec3 then
vec2 = {x = self._vec3.x, y = self._vec3.z }
end
return vec2
end
--- Get the position of the STATIC even if it is not alive.
-- @param #STATIC self
-- @return DCS#Vec3 The 3D point vector of the POSITIONABLE.
-- @return #nil The position was not cached.
function STATIC:GetVec3Cached()
local vec3 = self:GetVec3()
if not vec3 and self._vec3 then
vec3 = self._vec3
end
return vec3
end
--- Finds a STATIC from the _DATABASE using a DCSStatic object.
-- @param #STATIC self
-- @param DCS#StaticObject DCSStatic An existing DCS Static object reference.
@@ -232,6 +259,8 @@ function STATIC:SpawnAt(Coordinate, Heading, Delay)
local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName)
SpawnStatic:SpawnFromPointVec2( Coordinate, Heading, self.StaticName )
-- Cache position
self._vec3 = self:GetVec3()
end
@@ -255,6 +284,8 @@ function STATIC:ReSpawn(CountryID, Delay)
local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName, CountryID)
SpawnStatic:Spawn(nil, self.StaticName)
-- Cache position
self._vec3 = self:GetVec3()
end
@@ -278,6 +309,8 @@ function STATIC:ReSpawnAt(Coordinate, Heading, Delay)
local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName, self:GetCountry())
SpawnStatic:SpawnFromCoordinate(Coordinate, Heading, self.StaticName)
-- Cache position
self._vec3 = self:GetVec3()
end
+3 -3
View File
@@ -753,7 +753,7 @@ function STORAGE:LoadFromFile(Path,Filename)
end
end
else
self:E("File for Liquids could not be found: "..tostring(Path).."\\"..tostring(Filename"_Liquids.csv"))
self:E("File for Liquids could not be found: "..tostring(Path).."\\"..tostring(Filename).."_Liquids.csv")
end
end
@@ -773,7 +773,7 @@ function STORAGE:LoadFromFile(Path,Filename)
end
end
else
self:E("File for Aircraft could not be found: "..tostring(Path).."\\"..tostring(Filename"_Aircraft.csv"))
self:E("File for Aircraft could not be found: "..tostring(Path).."\\"..tostring(Filename).."_Aircraft.csv")
end
end
@@ -805,7 +805,7 @@ function STORAGE:LoadFromFile(Path,Filename)
end
end
else
self:E("File for Weapons could not be found: "..tostring(Path).."\\"..tostring(Filename"_Weapons.csv"))
self:E("File for Weapons could not be found: "..tostring(Path).."\\"..tostring(Filename).."_Weapons.csv")
end
end
+29
View File
@@ -377,6 +377,10 @@ function UNIT:ReSpawnAt(Coordinate, Heading)
--self:T( SpawnGroupTemplate )
if self.ValidateAndRepositionGroundUnits then
UTILS.ValidateAndRepositionGroundUnits(SpawnGroupTemplate.units)
end
_DATABASE:Spawn(SpawnGroupTemplate)
end
@@ -1924,3 +1928,28 @@ function UNIT:IsAAA()
end
return false
end
--- Set the relative life points of a UNIT object
-- @param #UNIT self
-- @param #number Percent Percent to set, can be 0..100.
function UNIT:SetLife(Percent)
net.dostring_in("mission",string.format("a_unit_set_life_percentage(%d, %f)", self:GetID(), Percent))
end
--- Set the carrier illumination mode. -2: OFF, -1: AUTO, 0: NAVIGATION, 1: AC LAUNCH, 2: AC RECOVERY
-- @param #UNIT self
-- @param #number Mode Illumination mode, can be -2: OFF, -1: AUTO, 0: NAVIGATION, 1: AC LAUNCH, 2: AC RECOVERY
function UNIT:SetCarrierIlluminationMode(Mode)
UTILS.SetCarrierIlluminationMode(self:GetID(), Mode)
end
--- This function uses Disposition and other fallback logic to find better ground positions for ground units.
--- NOTE: This is not a spawn randomizer.
--- It will try to find clear ground locations avoiding trees, water, roads, runways, map scenery, statics and other units in the area and modifies the provided positions table.
--- Maintains the original layout and unit positions as close as possible by searching for the next closest valid position to each unit.
--- Uses UTILS.ValidateAndRepositionGroundUnits.
-- @param #UNIT self
-- @param #boolean Enabled Enable/disable the feature.
function UNIT:SetValidateAndRepositionGroundUnits(Enabled)
self.ValidateAndRepositionGroundUnits = Enabled
end
+1
View File
@@ -75,6 +75,7 @@ Functional/ZoneGoalCargo.lua
Functional/Tiresias.lua
Functional/Stratego.lua
Functional/ClientWatch.lua
Functional/Formation.lua
Ops/Airboss.lua
Ops/RecoveryTanker.lua