Compare commits

...

393 Commits

Author SHA1 Message Date
Frank
e3cee8dafe VECTOR 2025-10-28 23:07:59 +01:00
Frank
3686e0de63 Vector 2025-10-26 23:00:32 +01:00
Frank
2931b32ce6 Vector 0.0.3 2025-10-26 20:54:52 +01:00
Frank
42221f406a Update Radios.lua 2025-10-26 15:19:34 +01:00
Frank
654f752ca0 Merge branch 'develop' into FF/Ops 2025-10-25 21:17:01 +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
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
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
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
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
54 changed files with 8974 additions and 2530 deletions

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

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

View File

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

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

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()
--

View File

@@ -59,6 +59,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
--
@@ -773,7 +777,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 +839,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
@@ -2124,21 +2150,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 +2185,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,7 +3819,26 @@ do -- COORDINATE
function COORDINATE:GetRandomPointVec3InRadius( OuterRadius, InnerRadius )
return COORDINATE:NewFromVec3( self:GetRandomVec3InRadius( OuterRadius, InnerRadius ) )
end
--- 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
do

View File

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

View File

@@ -958,7 +958,26 @@ do -- SET_BASE
return ObjectNames
end
--- Get a *new* set table that only contains alive objects.
-- @param #SET_BASE self
-- @return #table Set table of alive objects.
function SET_BASE:GetAliveSet()
--self:F2()
local AliveSet = {}
-- Clean the Set before returning with only the alive Objects.
for ObjectName, Object in pairs( self.Set ) do
if Object then
if Object:IsAlive() then
AliveSet[#AliveSet+1] = Object
end
end
end
return AliveSet or {}
end
end
do
@@ -1125,25 +1144,25 @@ do
end
--- Get a *new* set that only contains alive groups.
--- Get a *new* set table that only contains alive groups.
-- @param #SET_GROUP self
-- @return #SET_GROUP Set of alive groups.
-- @return #table Set of alive groups.
function SET_GROUP:GetAliveSet()
--self:F2()
local AliveSet = SET_GROUP:New()
--local AliveSet = SET_GROUP:New()
local AliveSet = {}
-- Clean the Set before returning with only the alive Groups.
for GroupName, GroupObject in pairs( self.Set ) do
local GroupObject = GroupObject -- Wrapper.Group#GROUP
if GroupObject then
if GroupObject:IsAlive() then
AliveSet:Add( GroupName, GroupObject )
AliveSet[GroupName] = GroupObject
end
end
end
return AliveSet.Set or {}
return AliveSet or {}
end
--- Returns a report of of unit types.
@@ -2595,18 +2614,16 @@ do -- SET_UNIT
--- Gets the alive set.
-- @param #SET_UNIT self
-- @return #table Table of SET objects
-- @return #table Table of alive UNIT objects
-- @return #SET_UNIT AliveSet
function SET_UNIT:GetAliveSet()
local AliveSet = SET_UNIT:New()
-- Clean the Set before returning with only the alive Groups.
for GroupName, GroupObject in pairs(self.Set) do
local GroupObject=GroupObject --Wrapper.Client#CLIENT
for GroupName, GroupObject in pairs(self.Set) do
if GroupObject and GroupObject:IsAlive() then
AliveSet:Add(GroupName, GroupObject)
AliveSet[GroupName] = GroupObject
end
end
@@ -4784,18 +4801,16 @@ do -- SET_CLIENT
-- @return #table Table of SET objects
function SET_CLIENT:GetAliveSet()
local AliveSet = SET_CLIENT:New()
local AliveSet = {}
-- Clean the Set before returning with only the alive Groups.
for GroupName, GroupObject in pairs(self.Set) do
local GroupObject=GroupObject --Wrapper.Client#CLIENT
for GroupName, GroupObject in pairs(self.Set) do
if GroupObject and GroupObject:IsAlive() then
AliveSet:Add(GroupName, GroupObject)
AliveSet[GroupName] = GroupObject
end
end
return AliveSet.Set or {}
return AliveSet or {}
end
--- [User] Add a custom condition function.
@@ -6676,6 +6691,8 @@ do -- SET_ZONE
--
-- -- Stop watching after 1 hour
-- zoneset:__TriggerStop(3600)
-- -- Call :SetPartlyInside() on any zone (or SET_ZONE) if you want GROUPs to count as inside when any of their units enters even if they are far apart.
-- -- Make sure to call :SetPartlyInside() before :Trigger()!.
function SET_ZONE:Trigger(Objects)
--self:I("Added Set_Zone Trigger")
self:AddTransition("*","TriggerStart","TriggerRunning")
@@ -6726,6 +6743,20 @@ do -- SET_ZONE
-- @param Core.Zone#ZONE_BASE Zone The zone left.
end
--- Toggle “partly-inside” handling for every zone in the set when those zones are used with :Trigger().
-- * Call with no argument or **true** → enable for all.
-- * Call with **false** → disable again (handy if it was enabled before).
-- @param #SET_ZONE self
-- @return #SET_ZONE self
function SET_ZONE:SetPartlyInside(state)
for _,Zone in pairs(self.Set) do
if Zone.SetPartlyInside then
Zone:SetPartlyInside(state)
end
end
return self
end
--- (Internal) Check the assigned objects for being in/out of the zone
-- @param #SET_ZONE self
-- @param #boolean fromstart If true, do the init of the objects
@@ -6761,8 +6792,13 @@ do -- SET_ZONE
-- has not been tagged previously - wasn't in set!
obj.TriggerInZone[_zone.ZoneName] = false
end
-- is obj in zone?
local inzone = _zone:IsCoordinateInZone(obj:GetCoordinate())
-- is obj in this zone?
local inzone
if _zone.PartlyInside and obj.ClassName == "GROUP" then
inzone = obj:IsAnyInZone(_zone) -- TRUE as soon as any unit is inside
else
inzone = _zone:IsCoordinateInZone(obj:GetCoordinate()) -- original centroid test
end
--self:I("Object "..obj:GetName().." is in zone: "..tostring(inzone))
if inzone and not obj.TriggerInZone[_zone.ZoneName] then
-- wasn't in zone before
@@ -7829,6 +7865,28 @@ do -- SET_OPSGROUP
return self
end
--- Iterate the SET_OPSGROUP and count how many GROUPs and UNITs are alive.
-- @param #SET_GROUP self
-- @return #number The number of GROUPs alive.
-- @return #number The number of UNITs alive.
function SET_OPSGROUP:CountAlive()
local CountG = 0
local CountU = 0
local Set = self:GetSet()
for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP
if GroupData and GroupData:IsAlive() then
CountG = CountG + 1
-- Count Units.
CountU = CountU + GroupData:GetGroup():CountAliveUnits()
end
end
return CountG, CountU
end
--- Finds an OPSGROUP based on the group name.
-- @param #SET_OPSGROUP self
-- @param #string GroupName Name of the group.

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

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

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
@@ -534,6 +535,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 +613,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 +683,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 +731,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
@@ -1149,15 +1180,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 +1538,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 +1569,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 +1934,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 +2086,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 +2571,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 +2969,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 +3313,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 +3337,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 +3374,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 {}

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

View File

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

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

View File

@@ -321,7 +321,9 @@ function SCORING:New( GameName, SavePath, AutoSave )
-- Create the CSV file.
self.AutoSavePath = SavePath
self.AutoSave = AutoSave or true
self:OpenCSV( GameName )
if self.AutoSave == true then
self:OpenCSV( GameName )
end
return self
@@ -985,6 +987,7 @@ function SCORING:_EventOnHit( Event )
local TargetUnitCoalition = nil
local TargetUnitCategory = nil
local TargetUnitType = nil
local TargetIsScenery = false
if Event.IniDCSUnit then
@@ -1025,6 +1028,12 @@ function SCORING:_EventOnHit( Event )
TargetCategory = Event.TgtCategory
TargetType = Event.TgtTypeName
-- Scenery hit
if (not TargetCategory) and TargetUNIT ~= nil and TargetUnit:IsInstanceOf("SCENERY") then
TargetCategory = Unit.Category.STRUCTURE
TargetIsScenery = true
end
TargetUnitCoalition = _SCORINGCoalition[TargetCoalition]
TargetUnitCategory = _SCORINGCategory[TargetCategory]
TargetUnitType = TargetType
@@ -1117,17 +1126,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() )
@@ -1923,7 +1937,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

View File

@@ -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.
@@ -4247,6 +4247,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.
@@ -5965,6 +5975,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
@@ -6893,7 +6907,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

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

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' )
@@ -186,4 +187,9 @@ __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/Point.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Navigation/Beacons.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' )

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' )

View File

@@ -0,0 +1,395 @@
--- **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 NAVIGATION_Beacons.png
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- 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 desinged 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.
--
-- # 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.0.4"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- 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 BECAONS 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 BECAONS 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
--- 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`.
-- @return #table Table of beacons. Each element is of type #BEACON.Beacon.
function BEACONS:GetBeacons(TypeID)
local beacons={}
for _,_beacon in pairs(self.beacons) do
local bc=_beacon --#BEACONS.Beacon
if TypeID==nil or TypeID==bc.type 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
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

View File

@@ -0,0 +1,587 @@
--- **NAVIGATION** - Navigation Airspace Points, Fixes and Aids.
--
-- **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-%20NavFix).
--
-- ===
--
-- ### Author: **funkyfranky**
--
-- ===
-- @module Navigation.Point
-- @image NAVIGATION_Point.png
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- 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.0.1"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- 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
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- 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 compusory 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.0.1"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- 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.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

View File

@@ -0,0 +1,330 @@
--- **NAVIGATION** - Airbase radios.
--
-- **Main Features:**
--
-- * Get radio frequencies of airbases
--
-- ===
--
-- ## 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 NAVIGATION_Radios.png
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- 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 desinged 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.
--
-- # 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>\radios.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.
--- 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.0.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()
for _,_radio in pairs(RadioTable) do
local radio=_radio --#RADIOS.Radio
--UTILS.PrintTableToLog(radio)
--UTILS.PrintTableToLog(radio.callsign)
-- The table structure of callsign is a bit awkward. We need to get the airbase name.
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
--UTILS.PrintTableToLog(radio.callsign)
radio.name=self:_GetAirbaseName(airbasenames, radio.name)
radio.airbase=AIRBASE:FindByName(radio.name)
if radio.airbase then
radio.coordinate=radio.airbase:GetCoordinate()
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
--- 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
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

View File

@@ -0,0 +1,296 @@
--- **NAVIGATION** - Beacons of the map/theatre.
--
-- **Main Features:**
--
-- * Find towns of map
-- * Road and rail connections
--
-- ===
--
-- ## 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 NAVIGATION_Towns.png
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- 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 desinged 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.
--
-- # 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>\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.0.1"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: A lot...
-- TODO: Road connection
-- TODO: Rail connection
-- TODO: 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 connection between two towns.
-- @param #TOWNS self
-- @param #TOWNS.Town townA The town data structure.
-- @param #TOWNS.Town townB The town data structure.
-- @return #table Table containing path coordinates.
function TOWNS:GetConnectionRoad(townA, townB)
local path=townA.coordRoad:GetPathOnRoad(townB.coordRoad)
return path
end
--- Find closest town to a given coordinate.
-- @param #TOWNS self
-- @param Core.Point#COORDINATE Coordinate The reference coordinate.
-- @return #TOWNS.Town The closest town.
function TOWNS:GetClosestTown(Coordinate)
local Town=nil --#TOWNS.Town
local distmin=math.huge
for _,_town in pairs(self.towns) do
local town=_town --#TOWNS.Town
local dist=Coordinate:Get2DDistance(town.coordinate)
if dist<distmin then
distmin=dist
Town=town
end
end
return Town
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
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

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

View File

@@ -397,6 +397,7 @@ AUFTRAG = {
conditionPush = {},
conditionSuccessSet = false,
conditionFailureSet = false,
repeatDelay = 1,
}
--- Global mission counter.
@@ -1320,13 +1321,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 +1435,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 +1722,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 +1795,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 +1814,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
@@ -2279,8 +2324,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 +2340,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
@@ -2966,6 +3013,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 +4018,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.
@@ -4765,6 +4839,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
@@ -4822,6 +4898,11 @@ function AUFTRAG:CheckGroupsDone()
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
if (self:IsStarted() or self:IsExecuting()) and (fsmState == AUFTRAG.Status.STARTED or fsmState == AUFTRAG.Status.EXECUTING) and self:CountOpsGroups()>0 then
self:T(self.lid..string.format("CheckGroupsDone: Mission is STARTED 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 +5241,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 +5283,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
@@ -6110,7 +6191,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 +6231,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 +6394,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
--------------------

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

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

View File

@@ -25,7 +25,7 @@
-- @module Ops.CTLD
-- @image OPS_CTLD.jpg
-- Last Update May 2025
-- Last Update Oct 2025
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -147,6 +147,7 @@ CTLD_CARGO = {
Location = ZONE:New(Location)
end
self.Location = Location
self.NoMoveToZone = false
return self
end
@@ -783,6 +784,7 @@ do
-- my_ctld:AddCratesCargo("Humvee",{"Humvee"},CTLD_CARGO.Enum.VEHICLE,2,2775,10)
-- -- additionally, you can limit **where** the stock is available (one location only!) - this one is available in a zone called "Vehicle Store".
-- my_ctld:AddCratesCargo("Humvee",{"Humvee"},CTLD_CARGO.Enum.VEHICLE,2,2775,10,nil,nil,"Vehicle Store")
-- -- Tip: if you want the spawned/built group NOT to move to a MOVE zone, replace AddCratesCargo with AddCratesCargoNoMove (same parameters).
--
-- -- add infantry unit called "Forward Ops Base" using template "FOB", of type FOB, size 4, i.e. needs four crates to be build:
-- my_ctld:AddCratesCargo("Forward Ops Base",{"FOB"},CTLD_CARGO.Enum.FOB,4)
@@ -867,7 +869,9 @@ do
-- my_ctld.TroopUnloadDistHoverHook = 5 -- When hovering, unload troops this far behind the Chinook
-- my_ctld.showstockinmenuitems = false -- When set to true, the menu lines will also show the remaining items in stock (that is, if you set any), downside is that the menu for all will be build every 30 seconds anew.
-- my_ctld.onestepmenu = false -- When set to true, the menu will create Drop and build, Get and load, Pack and remove, Pack and load, Pack. it will be a 1 step solution.
--
-- my_ctld.VehicleMoveFormation = AI.Task.VehicleFormation.VEE -- When a group moves to a MOVE zone, then it takes this formation. Can be a table of formations, which are then randomly chosen. Defaults to "Vee".
-- my_ctld.validateAndRepositionUnits = false -- Uses Disposition and other logic to find better ground positions for ground units avoiding trees, water, roads, runways, map scenery, statics and other units in the area. (Default is false)
--
-- ## 2.1 CH-47 Chinook support
--
-- The Chinook comes with the option to use the ground crew menu to load and unload cargo into the Helicopter itself for better immersion. As well, it can sling-load cargo from ground. The cargo you can actually **create**
@@ -1294,6 +1298,7 @@ CTLD = {
LoadedGroupsTable = {},
keeploadtable = true,
allowCATransport = false,
VehicleMoveFormation = AI.Task.VehicleFormation.VEE,
}
------------------------------
@@ -1393,6 +1398,7 @@ CTLD.UnitTypeCapabilities = {
["Hercules"] = {type="Hercules", crates=true, troops=true, cratelimit = 7, trooplimit = 64, length = 25, cargoweightlimit = 19000}, -- 19t cargo, 64 paratroopers.
--Actually it's longer, but the center coord is off-center of the model.
["UH-60L"] = {type="UH-60L", crates=true, troops=true, cratelimit = 2, trooplimit = 20, length = 16, cargoweightlimit = 3500}, -- 4t cargo, 20 (unsec) seats
["UH-60L_DAP"] = {type="UH-60L_DAP", crates=false, troops=true, cratelimit = 0, trooplimit = 2, length = 16, cargoweightlimit = 500}, -- UH-60L DAP is an attack helo but can do limited CSAR and CTLD
["MH-60R"] = {type="MH-60R", crates=true, troops=true, cratelimit = 2, trooplimit = 20, length = 16, cargoweightlimit = 3500}, -- 4t cargo, 20 (unsec) seats
["SH-60B"] = {type="SH-60B", crates=true, troops=true, cratelimit = 2, trooplimit = 20, length = 16, cargoweightlimit = 3500}, -- 4t cargo, 20 (unsec) seats
["AH-64D_BLK_II"] = {type="AH-64D_BLK_II", crates=false, troops=true, cratelimit = 0, trooplimit = 2, length = 17, cargoweightlimit = 200}, -- 2 ppl **outside** the helo
@@ -1414,7 +1420,7 @@ CTLD.FixedWingTypes = {
--- CTLD class version.
-- @field #string version
CTLD.version="1.3.34"
CTLD.version="1.3.38"
--- Instantiate a new CTLD.
-- @param #CTLD self
@@ -1481,6 +1487,7 @@ function CTLD:New(Coalition, Prefixes, Alias)
self:AddTransition("*", "CratesRepaired", "*") -- CTLD repair event.
self:AddTransition("*", "CratesBuildStarted", "*") -- CTLD build event.
self:AddTransition("*", "CratesRepairStarted", "*") -- CTLD repair event.
self:AddTransition("*", "CratesPacked", "*") -- CTLD repack event.
self:AddTransition("*", "HelicopterLost", "*") -- CTLD lost event.
self:AddTransition("*", "Load", "*") -- CTLD load event.
self:AddTransition("*", "Loaded", "*") -- CTLD load event.
@@ -1551,15 +1558,20 @@ function CTLD:New(Coalition, Prefixes, Alias)
self.smokedistance = 2000
self.movetroopstowpzone = true
self.movetroopsdistance = 5000
self.returntroopstobase = true -- if set to false, troops would stay after deployment inside a load zone.
self.troopdropzoneradius = 100
self.VehicleMoveFormation = AI.Task.VehicleFormation.VEE
-- added support Hercules Mod
self.enableHercules = false -- deprecated
self.enableFixedWing = false
self.FixedMinAngels = 165 -- for troop/cargo drop via chute
self.FixedMaxAngels = 2000 -- for troop/cargo drop via chute
self.FixedMaxSpeed = 77 -- 280 kph or 150kn eq 77 mps
self.validateAndRepositionUnits = false -- 280 kph or 150kn eq 77 mps
-- message suppression
self.suppressmessages = false
@@ -1759,6 +1771,17 @@ function CTLD:New(Coalition, Prefixes, Alias)
-- @param Wrapper.Unit#UNIT Unit Unit Object.
-- @param Wrapper.Group#GROUP Vehicle The #GROUP object of the vehicle or FOB repaired.
-- @return #CTLD self
--- FSM Function OnBeforeCratesPacked.
-- @function [parent=#CTLD] OnBeforeCratesPacked
-- @param #CTLD self
-- @param #string From State.
-- @param #string Event Trigger.
-- @param #string To State.
-- @param Wrapper.Group#GROUP Group Group Object.
-- @param Wrapper.Unit#UNIT Unit Unit Object.
-- @param #CTLD_CARGO Cargo Cargo crate that was repacked.
-- @return #CTLD self
--- FSM Function OnBeforeTroopsRTB.
-- @function [parent=#CTLD] OnBeforeTroopsRTB
@@ -1846,6 +1869,7 @@ function CTLD:New(Coalition, Prefixes, Alias)
-- @param #string To State.
-- @param Wrapper.Group#GROUP Group Group Object.
-- @param Wrapper.Unit#UNIT Unit Unit Object.
-- @param CargoName The name of the cargo being built.
-- @return #CTLD self
--- FSM Function OnAfterCratesRepairStarted. Info event that a repair has been started.
@@ -1888,6 +1912,17 @@ function CTLD:New(Coalition, Prefixes, Alias)
-- @param Wrapper.Unit#UNIT Unit Unit Object.
-- @param Wrapper.Group#GROUP Vehicle The #GROUP object of the vehicle or FOB repaired.
-- @return #CTLD self
--- FSM Function OnAfterCratesPacked.
-- @function [parent=#CTLD] OnAfterCratesPacked
-- @param #CTLD self
-- @param #string From State.
-- @param #string Event Trigger.
-- @param #string To State.
-- @param Wrapper.Group#GROUP Group Group Object.
-- @param Wrapper.Unit#UNIT Unit Unit Object.
-- @param #CTLD_CARGO Cargo Cargo crate that was repacked.
-- @return #CTLD self
--- FSM Function OnAfterTroopsRTB.
-- @function [parent=#CTLD] OnAfterTroopsRTB
@@ -2075,6 +2110,9 @@ function CTLD:_EventHandler(EventData)
local _group = event.IniGroup
local _unit = event.IniUnit
self:_RefreshLoadCratesMenu(_group, _unit)
if self:IsFixedWing(_unit) and self.enableFixedWing then
self:_RefreshDropCratesMenu(_group, _unit)
end
end
elseif event.id == EVENTS.PlayerLeaveUnit or event.id == EVENTS.UnitLost then
-- remove from pilot table
@@ -2824,8 +2862,12 @@ function CTLD:_GetCrates(Group, Unit, Cargo, number, drop, pack)
if cratedistance > self.CrateDistance then cratedistance = self.CrateDistance end
-- altered heading logic
-- DONE: right standard deviation?
rheading = UTILS.RandomGaussian(0,30,-90,90,100)
rheading = math.fmod((heading + rheading), 360)
if self:IsUnitInAir(Unit) and self:IsFixedWing(Unit) then
rheading = math.random(20,60)
else
rheading = UTILS.RandomGaussian(0, 30, -90, 90, 100)
end
rheading=math.fmod((heading+rheading),360)
cratecoord = position:Translate(cratedistance,rheading)
else
cratedistance = (row-1)*6
@@ -3290,6 +3332,7 @@ function CTLD:_LoadCratesNearby(Group, Unit)
self:_RefreshLoadCratesMenu(Group, Unit)
-- clean up real world crates
self:_CleanupTrackedCrates(crateidsloaded)
self:__CratesPickedUp(1, Group, Unit, loaded.Cargo)
end
end
return self
@@ -3636,7 +3679,7 @@ function CTLD:_UnloadTroops(Group, Unit)
inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.SHIP)
end
if inzone then
droppingatbase = true
droppingatbase = self.returntroopstobase
end
-- check for hover unload
local hoverunload = self:IsCorrectHover(Unit) --if true we\'re hovering in parameters
@@ -3700,6 +3743,7 @@ function CTLD:_UnloadTroops(Group, Unit)
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias)
:InitDelayOff()
:InitSetUnitAbsolutePositions(Positions)
:InitValidateAndRepositionGroundUnits(self.validateAndRepositionUnits)
:OnSpawnGroup(function(grp) grp.spawntime = timer.getTime() end)
:SpawnFromVec2(randomcoord:GetVec2())
self:__TroopsDeployed(1, Group, Unit, self.DroppedTroops[self.TroopCounter],type)
@@ -3962,7 +4006,7 @@ function CTLD:_BuildCrates(Group, Unit,Engineering,MultiDrop)
local buildtimer = TIMER:New(self._BuildObjectFromCrates,self,Group,Unit,build,false,Group:GetCoordinate(),MultiDrop)
buildtimer:Start(self.buildtime)
self:_SendMessage(string.format("Build started, ready in %d seconds!",self.buildtime),15,false,Group)
self:__CratesBuildStarted(1,Group,Unit)
self:__CratesBuildStarted(1,Group,Unit,build.Name)
self:_RefreshDropTroopsMenu(Group,Unit)
else
self:_BuildObjectFromCrates(Group,Unit,build,false,nil,MultiDrop)
@@ -4004,6 +4048,7 @@ function CTLD:_PackCratesNearby(Group, Unit)
_Group:Destroy() -- if a match is found destroy the Wrapper.Group#GROUP near the player
self:_GetCrates(Group, Unit, _entry, nil, false, true) -- spawn the appropriate crates near the player
self:_RefreshLoadCratesMenu(Group,Unit) -- call the refresher to show the crates in the menu
self:__CratesPacked(1,Group,Unit,_entry)
return true
end
end
@@ -4145,11 +4190,13 @@ function CTLD:_BuildObjectFromCrates(Group,Unit,Build,Repair,RepairLocation,Mult
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias)
--:InitRandomizeUnits(true,20,2)
:InitDelayOff()
:InitValidateAndRepositionGroundUnits(self.validateAndRepositionUnits)
:OnSpawnGroup(function(grp) grp.spawntime = timer.getTime() end)
:SpawnFromVec2(randomcoord)
else -- don't random position of e.g. SAM units build as FOB
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias)
:InitDelayOff()
:InitValidateAndRepositionGroundUnits(self.validateAndRepositionUnits)
:OnSpawnGroup(function(grp) grp.spawntime = timer.getTime() end)
:SpawnFromVec2(randomcoord)
end
@@ -4165,6 +4212,17 @@ function CTLD:_BuildObjectFromCrates(Group,Unit,Build,Repair,RepairLocation,Mult
return self
end
--- (Internal) Function to get a vehicle formation for a moving group
-- @param #CTLD self
-- @return #string Formation
function CTLD:_GetVehicleFormation()
local VehicleMoveFormation = self.VehicleMoveFormation or AI.Task.VehicleFormation.VEE
if type(self.VehicleMoveFormation)=="table" then
VehicleMoveFormation = self.VehicleMoveFormation[math.random(1,#self.VehicleMoveFormation)]
end
return VehicleMoveFormation
end
--- (Internal) Function to move group to WP zone.
-- @param #CTLD self
-- @param Wrapper.Group#GROUP Group The Group to move.
@@ -4179,18 +4237,20 @@ function CTLD:_MoveGroupToZone(Group)
-- yes, we can ;)
local groupname = Group:GetName()
local zonecoord = zone:GetRandomCoordinate(20,125) -- Core.Point#COORDINATE
local coordinate = zonecoord:GetVec2()
local formation = self:_GetVehicleFormation()
--local coordinate = zonecoord:GetVec2()
Group:SetAIOn()
Group:OptionAlarmStateAuto()
Group:OptionDisperseOnAttack(30)
Group:OptionROEOpenFirePossible()
Group:RouteToVec2(coordinate,5)
Group:OptionROEOpenFire()
Group:RouteGroundTo(zonecoord,25,formation)
end
return self
end
--- (Internal) Housekeeping - Cleanup crates when build
-- @param #CTLD self
--
-- @param #table Crates Table of #CTLD_CARGO objects near the unit.
-- @param #CTLD.Buildable Build Table build object.
-- @param #number Number Number of objects in Crates (found) to limit search.
@@ -4456,7 +4516,8 @@ function CTLD:_RefreshF10Menus()
end
for _,cargoObj in pairs(self.Cargo_Crates) do
if not cargoObj.DontShowInMenu then
local txt = string.format("Crate %s (%dkg)",cargoObj.Name,cargoObj.PerCrateMass or 0)
local needed = cargoObj:GetCratesNeeded() or 1
local txt = string.format("%d crate%s %s (%dkg)",needed,needed==1 and "" or "s",cargoObj.Name,cargoObj.PerCrateMass or 0)
if cargoObj.Location then txt = txt.."[R]" end
local stock = cargoObj:GetStock()
if stock>=0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
@@ -4467,7 +4528,8 @@ function CTLD:_RefreshF10Menus()
end
for _,cargoObj in pairs(self.Cargo_Statics) do
if not cargoObj.DontShowInMenu then
local txt = string.format("Crate %s (%dkg)",cargoObj.Name,cargoObj.PerCrateMass or 0)
local needed = cargoObj:GetCratesNeeded() or 1
local txt = string.format("%d crate%s %s (%dkg)",needed,needed==1 and "" or "s",cargoObj.Name,cargoObj.PerCrateMass or 0)
if cargoObj.Location then txt = txt.."[R]" end
local stock = cargoObj:GetStock()
if stock>=0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
@@ -4479,7 +4541,8 @@ function CTLD:_RefreshF10Menus()
else
for _,cargoObj in pairs(self.Cargo_Crates) do
if not cargoObj.DontShowInMenu then
local txt = string.format("Crate %s (%dkg)",cargoObj.Name,cargoObj.PerCrateMass or 0)
local needed = cargoObj:GetCratesNeeded() or 1
local txt = string.format("%d crate%s %s (%dkg)",needed,needed==1 and "" or "s",cargoObj.Name,cargoObj.PerCrateMass or 0)
if cargoObj.Location then txt = txt.."[R]" end
local stock = cargoObj:GetStock()
if stock>=0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
@@ -4490,7 +4553,8 @@ function CTLD:_RefreshF10Menus()
end
for _,cargoObj in pairs(self.Cargo_Statics) do
if not cargoObj.DontShowInMenu then
local txt = string.format("Crate %s (%dkg)",cargoObj.Name,cargoObj.PerCrateMass or 0)
local needed = cargoObj:GetCratesNeeded() or 1
local txt = string.format("%d crate%s %s (%dkg)",needed,needed==1 and "" or "s",cargoObj.Name,cargoObj.PerCrateMass or 0)
if cargoObj.Location then txt = txt.."[R]" end
local stock = cargoObj:GetStock()
if stock>=0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
@@ -4502,14 +4566,15 @@ function CTLD:_RefreshF10Menus()
end
end
else
if self.usesubcats then
if self.usesubcats == true then
local subcatmenus = {}
for catName, _ in pairs(self.subcats) do
subcatmenus[catName] = MENU_GROUP:New(_group, catName, cratesmenu) -- fixed variable case
end
for _, cargoObj in pairs(self.Cargo_Crates) do
if not cargoObj.DontShowInMenu then
local txt = string.format("Crate %s (%dkg)", cargoObj.Name, cargoObj.PerCrateMass or 0)
local needed = cargoObj:GetCratesNeeded() or 1
local txt = string.format("%d crate%s %s (%dkg)", needed, needed==1 and "" or "s", cargoObj.Name, cargoObj.PerCrateMass or 0)
if cargoObj.Location then txt = txt.."[R]" end
local stock = cargoObj:GetStock()
if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
@@ -4518,7 +4583,8 @@ function CTLD:_RefreshF10Menus()
end
for _, cargoObj in pairs(self.Cargo_Statics) do
if not cargoObj.DontShowInMenu then
local txt = string.format("Crate %s (%dkg)", cargoObj.Name, cargoObj.PerCrateMass or 0)
local needed = cargoObj:GetCratesNeeded() or 1
local txt = string.format("%d crate%s %s (%dkg)", needed, needed==1 and "" or "s", cargoObj.Name, cargoObj.PerCrateMass or 0)
if cargoObj.Location then txt = txt.."[R]" end
local stock = cargoObj:GetStock()
if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
@@ -4528,7 +4594,8 @@ function CTLD:_RefreshF10Menus()
else
for _, cargoObj in pairs(self.Cargo_Crates) do
if not cargoObj.DontShowInMenu then
local txt = string.format("Crate %s (%dkg)", cargoObj.Name, cargoObj.PerCrateMass or 0)
local needed = cargoObj:GetCratesNeeded() or 1
local txt = string.format("%d crate%s %s (%dkg)", needed, needed==1 and "" or "s", cargoObj.Name, cargoObj.PerCrateMass or 0)
if cargoObj.Location then txt = txt.."[R]" end
local stock = cargoObj:GetStock()
if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
@@ -4537,7 +4604,8 @@ function CTLD:_RefreshF10Menus()
end
for _, cargoObj in pairs(self.Cargo_Statics) do
if not cargoObj.DontShowInMenu then
local txt = string.format("Crate %s (%dkg)", cargoObj.Name, cargoObj.PerCrateMass or 0)
local needed = cargoObj:GetCratesNeeded() or 1
local txt = string.format("%d crate%s %s (%dkg)", needed, needed==1 and "" or "s", cargoObj.Name, cargoObj.PerCrateMass or 0)
if cargoObj.Location then txt = txt.."[R]" end
local stock = cargoObj:GetStock()
if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end
@@ -4888,7 +4956,17 @@ function CTLD:_UnloadSingleCrateSet(Group, Unit, setIndex)
cObj:SetWasDropped(true)
cObj:SetHasMoved(true)
end
local cname = crateObj:GetName() or "Unknown"
local count = #chunk
if needed > 1 then
if count == needed then
self:_SendMessage(string.format("Dropped %d %s.", 1, cname), 10, false, Group)
else
self:_SendMessage(string.format("Dropped %d/%d crate(s) of %s.", count, needed, cname), 15, false, Group)
end
else
self:_SendMessage(string.format("Dropped %d %s(s).", count, cname), 10, false, Group)
end
-- Rebuild the cargo list to remove the dropped crates
local loadedData = self.Loaded_Cargo[unitName]
if loadedData and loadedData.Cargo then
@@ -5007,8 +5085,10 @@ function CTLD:_RefreshDropCratesMenu(Group, Unit)
--------------------------------------------------------------------
local mAll=MENU_GROUP:New(Group,"Drop ALL crates",dropCratesMenu)
MENU_GROUP_COMMAND:New(Group,"Drop",mAll,self._UnloadCrates,self,Group,Unit)
MENU_GROUP_COMMAND:New(Group,"Drop and build",mAll,self._DropAndBuild,self,Group,Unit)
if not ( self:IsUnitInAir(Unit) and self:IsFixedWing(Unit) ) then
MENU_GROUP_COMMAND:New(Group,"Drop and build",mAll,self._DropAndBuild,self,Group,Unit)
end
self.CrateGroupList=self.CrateGroupList or{}
self.CrateGroupList[Unit:GetName()]={}
@@ -5029,7 +5109,9 @@ function CTLD:_RefreshDropCratesMenu(Group, Unit)
local setIndex=#self.CrateGroupList[Unit:GetName()]
local mSet=MENU_GROUP:New(Group,label,dropCratesMenu)
MENU_GROUP_COMMAND:New(Group,"Drop",mSet,self._UnloadSingleCrateSet,self,Group,Unit,setIndex)
if not ( self:IsUnitInAir(Unit) and self:IsFixedWing(Unit) ) then
MENU_GROUP_COMMAND:New(Group,"Drop and build",mSet,self._DropSingleAndBuild,self,Group,Unit,setIndex)
end
i=i+needed
else
local chunk={}
@@ -5063,7 +5145,7 @@ function CTLD:_UnloadSingleTroopByID(Group, Unit, chunkID)
inzone, zonename, zone, distance = self:IsUnitInZone(Unit, CTLD.CargoZoneType.SHIP)
end
if inzone then
droppingatbase = true
droppingatbase = self.returntroopstobase
end
if self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName()) then
@@ -5148,6 +5230,7 @@ function CTLD:_UnloadSingleTroopByID(Group, Unit, chunkID)
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template, alias)
:InitDelayOff()
:InitSetUnitAbsolutePositions(Positions)
:InitValidateAndRepositionGroundUnits(self.validateAndRepositionUnits)
:OnSpawnGroup(function(grp) grp.spawntime = timer.getTime() end)
:SpawnFromVec2(randomcoord:GetVec2())
self:__TroopsDeployed(1, Group, Unit, self.DroppedTroops[self.TroopCounter], cType)
@@ -5156,6 +5239,8 @@ function CTLD:_UnloadSingleTroopByID(Group, Unit, chunkID)
foundCargo:SetWasDropped(true)
if cType == CTLD_CARGO.Enum.ENGINEERS then
self.Engineers = self.Engineers + 1
local grpname = self.DroppedTroops[self.TroopCounter]:GetName()
self.EngineersInField[self.Engineers] = CTLD_ENGINEERING:New(name, grpname)
self:_SendMessage(string.format("Dropped Engineers %s into action!", name), 10, false, Group)
else
self:_SendMessage(string.format("Dropped Troops %s into action!", name), 10, false, Group)
@@ -5352,6 +5437,52 @@ function CTLD:AddCratesCargo(Name,Templates,Type,NoCrates,PerCrateMass,Stock,Sub
return self
end
--- Identical to AddCratesCargo, but registers the cargo so the spawned/built group does not move to MOVE zones.
--- User function - Add *generic* crate-type loadable as cargo. This type will create crates that need to be loaded, moved, dropped and built.
-- @param #CTLD self
-- @param #string Name Unique name of this type of cargo. E.g. "Humvee".
-- @param #table Templates Table of #string names of late activated Wrapper.Group#GROUP building this cargo.
-- @param #CTLD_CARGO.Enum Type Type of cargo. I.e. VEHICLE or FOB. VEHICLE will move to destination zones when dropped/build, FOB stays put.
-- @param #number NoCrates Number of crates needed to build this cargo.
-- @param #number PerCrateMass Mass in kg of each crate
-- @param #number Stock Number of buildable groups in stock. Nil for unlimited.
-- @param #string SubCategory Name of sub-category (optional).
-- @param #boolean DontShowInMenu (optional) If set to "true" this won't show up in the menu.
-- @param Core.Zone#ZONE Location (optional) If set, the cargo item is **only** available here. Can be a #ZONE object or the name of a zone as #string.
-- @param #string UnitTypes Unit type names (optional). If set, only these unit types can pick up the cargo, e.g. "UH-1H" or {"UH-1H","OH58D"}.
-- @param #string Category Static category name (optional). If set, spawn cargo crate with an alternate category type, e.g. "Cargos".
-- @param #string TypeName Static type name (optional). If set, spawn cargo crate with an alternate type shape, e.g. "iso_container".
-- @param #string ShapeName Static shape name (optional). If set, spawn cargo crate with an alternate type sub-shape, e.g. "iso_container_cargo".
-- @return #CTLD self
function CTLD:AddCratesCargoNoMove(Name,Templates,Type,NoCrates,PerCrateMass,Stock,SubCategory,DontShowInMenu,Location,UnitTypes,Category,TypeName,ShapeName)
self:T(self.lid .. " AddCratesCargoNoMove")
if not self:_CheckTemplates(Templates) then
self:E(self.lid .. "Crates Cargo for " .. Name .. " has missing template(s)!" )
return self
end
self.CargoCounter = self.CargoCounter + 1
local cargo = CTLD_CARGO:New(self.CargoCounter,Name,Templates,Type,false,false,NoCrates,nil,nil,PerCrateMass,Stock,SubCategory,DontShowInMenu,Location)
cargo.NoMoveToZone = true
if UnitTypes then
cargo:AddUnitTypeName(UnitTypes)
end
cargo:SetStaticTypeAndShape("Cargos",self.basetype)
if TypeName then
cargo:SetStaticTypeAndShape(Category,TypeName,ShapeName)
end
table.insert(self.Cargo_Crates,cargo)
self.templateToCargoName = self.templateToCargoName or {}
if type(Templates)=="table" then
for _,t in pairs(Templates) do self.templateToCargoName[t] = Name end
else
self.templateToCargoName[Templates] = Name
end
self.nomovetozone_names = self.nomovetozone_names or {}
self.nomovetozone_names[Name] = true
if SubCategory and self.usesubcats ~= true then self.usesubcats=true end
return self
end
--- User function - Add *generic* static-type loadable as cargo. This type will create cargo that needs to be loaded, moved and dropped.
-- @param #CTLD self
-- @param #string Name Unique name of this type of cargo as set in the mission editor (note: UNIT name!), e.g. "Ammunition-1".
@@ -5596,8 +5727,14 @@ function CTLD:AddCTLDZone(Name, Type, Color, Active, HasBeacon, Shiplength, Ship
return self
end
end
local ctldzone = {} -- #CTLD.CargoZone
local exists = true
local ctldzone = self:GetCTLDZone(Name, Type) -- #CTLD.CargoZone
if not ctldzone then
exists = false
ctldzone = {}
end
ctldzone.active = Active or false
ctldzone.color = Color or SMOKECOLOR.Red
ctldzone.name = Name or "NONE"
@@ -5623,11 +5760,56 @@ function CTLD:AddCTLDZone(Name, Type, Color, Active, HasBeacon, Shiplength, Ship
ctldzone.shiplength = Shiplength or 100
ctldzone.shipwidth = Shipwidth or 10
end
self:AddZone(ctldzone)
if not exists then
self:AddZone(ctldzone)
end
return self
end
--- User function - find #CTLD.CargoZone zone by name.
-- @param #CTLD self
-- @param #string Name Name of this zone.
-- @param #string Type Type of this zone, #CTLD.CargoZoneType
-- @return #CTLD.CargoZone self
function CTLD:GetCTLDZone(Name, Type)
if Type == CTLD.CargoZoneType.LOAD then
for _, z in pairs(self.pickupZones) do
if z.name == Name then
return z
end
end
elseif Type == CTLD.CargoZoneType.DROP then
for _, z in pairs(self.dropOffZones) do
if z.name == Name then
return z
end
end
elseif Type == CTLD.CargoZoneType.SHIP then
for _, z in pairs(self.shipZones) do
if z.name == Name then
return z
end
end
elseif Type == CTLD.CargoZoneType.BEACON then
for _, z in pairs(self.droppedBeacons) do
if z.name == Name then
return z
end
end
else
for _, z in pairs(self.wpZones) do
if z.name == Name then
return z
end
end
end
return nil
end
--- User function - Creates and adds a #CTLD.CargoZone zone for this CTLD instance from an Airbase or FARP name.
-- Zones of type LOAD: Players load crates and troops here.
-- Zones of type DROP: Players can drop crates here. Note that troops can be unloaded anywhere.
@@ -5971,16 +6153,22 @@ function CTLD:SmokeZoneNearBy(Unit, Flare)
for index,cargozone in pairs(zones[i]) do
local CZone = cargozone --#CTLD.CargoZone
local zonename = CZone.name
local zone = nil
local zone = nil -- Core.Zone#ZONE_RADIUS
local airbasezone = false
if i == 4 then
zone = UNIT:FindByName(zonename)
else
zone = ZONE:FindByName(zonename)
if not zone then
zone = AIRBASE:FindByName(zonename):GetZone()
airbasezone = true
end
end
local zonecoord = zone:GetCoordinate()
-- Avoid smoke/flares on runways
if (i==1 or 1==3) and airbasezone==true and zone:IsInstanceOf("ZONE_BASE") then
zonecoord = zone:GetRandomCoordinate(inner,outer,{land.SurfaceType.LAND})
end
if zonecoord then
local active = CZone.active
local color = CZone.color
@@ -6852,6 +7040,7 @@ end
local alias = string.format("%s-%d", _template, math.random(1,100000))
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias)
:InitRandomizeUnits(randompositions,20,2)
:InitValidateAndRepositionGroundUnits(self.validateAndRepositionUnits)
:InitDelayOff()
:OnSpawnGroup(function(grp,TimeStamp) grp.spawntime = TimeStamp or timer.getTime() end,TimeStamp)
:SpawnFromVec2(randomcoord)
@@ -7005,12 +7194,14 @@ end
if canmove then
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias)
:InitRandomizeUnits(true,20,2)
:InitValidateAndRepositionGroundUnits(self.validateAndRepositionUnits)
:InitDelayOff()
:OnSpawnGroup(function(grp,TimeStamp) grp.spawntime = TimeStamp or timer.getTime() end,TimeStamp)
:SpawnFromVec2(randomcoord)
else -- don't random position of e.g. SAM units build as FOB
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias)
:InitDelayOff()
:InitValidateAndRepositionGroundUnits(self.validateAndRepositionUnits)
:OnSpawnGroup(function(grp,TimeStamp) grp.spawntime = TimeStamp or timer.getTime() end,TimeStamp)
:SpawnFromVec2(randomcoord)
end
@@ -7080,6 +7271,16 @@ end
local filepath = self.filepath
self:__Save(interval,filepath,filename)
end
if type(self.VehicleMoveFormation) == "table" then
local Formations = {}
for _,_formation in pairs(self.VehicleMoveFormation) do
table.insert(Formations,_formation)
end
self.VehicleMoveFormation = nil
self.VehicleMoveFormation = Formations
end
return self
end
@@ -7350,8 +7551,11 @@ end
-- @return #CTLD self
function CTLD:onafterCratesBuild(From, Event, To, Group, Unit, Vehicle)
self:T({From, Event, To})
if self.movetroopstowpzone then
self:_MoveGroupToZone(Vehicle)
if self.movetroopstowpzone and Vehicle then
local cg = self:GetGenericCargoObjectFromGroupName(Vehicle:GetName())
if not (cg and (cg.NoMoveToZone or (self.nomovetozone_names and self.nomovetozone_names[cg:GetName()]))) then
self:_MoveGroupToZone(Vehicle)
end
end
return self
end

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
@@ -679,7 +682,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 +707,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

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

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

View File

@@ -2464,7 +2464,7 @@ end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Payer Menu
-- Player Menu
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create player menu.

View File

@@ -779,6 +779,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.
@@ -2002,6 +2057,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 +3134,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
@@ -4494,6 +4552,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 +4667,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 +5029,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.

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
@@ -2513,9 +2523,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

View File

@@ -4631,7 +4631,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
@@ -5589,10 +5594,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
@@ -5727,7 +5735,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!
@@ -6232,7 +6240,7 @@ function OPSGROUP:RouteToMission(mission, delay)
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 +7539,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)
@@ -7844,8 +7852,13 @@ function OPSGROUP:_Spawn(Delay, Template)
-- Debug output.
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)
@@ -8082,7 +8095,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.
@@ -13952,6 +13965,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
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

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

View File

@@ -21,7 +21,7 @@
-- ===
-- @module Ops.PlayerTask
-- @image OPS_PlayerTask.jpg
-- @date Last Update April 2025
-- @date Last Update May 2025
do
@@ -98,7 +98,7 @@ PLAYERTASK = {
--- PLAYERTASK class version.
-- @field #string version
PLAYERTASK.version="0.1.26"
PLAYERTASK.version="0.1.28"
--- Generic task condition.
-- @type PLAYERTASK.Condition
@@ -387,6 +387,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
@@ -1227,7 +1235,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 +1451,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
@@ -1951,7 +1962,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
@@ -2581,7 +2592,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
@@ -3486,7 +3497,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 +3541,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 +3561,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
@@ -4606,7 +4636,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 +4650,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 --

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

View File

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

View File

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

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

View File

@@ -1586,12 +1586,12 @@ function UTILS.HdgDiff(h1, h2)
return math.abs(delta)
end
--- Returns the heading from one vec3 to another vec3.
-- @param DCS#Vec3 a From vec3.
-- @param DCS#Vec3 b To vec3.
--- Returns the heading from one vec2/vec3 to another vec2/vec3.
-- @param DCS#Vec3 a From Vec2 or Vec3.
-- @param DCS#Vec3 b To Vec2 or Vec3.
-- @return #number Heading in degrees.
function UTILS.HdgTo(a, b)
local dz=b.z-a.z
local dz=(b.z or b.y) - (a.z or a.y)
local dx=b.x-a.x
local heading=math.deg(math.atan2(dz, dx))
if heading < 0 then
@@ -1913,6 +1913,13 @@ end
function UTILS.GetReportingName(Typename)
local typename = string.lower(Typename)
-- special cases - Shark and Manstay have "A-50" in the name
if string.find(typename,"ka-50",1,true) then
return "Shark"
elseif string.find(typename,"a-50",1,true) then
return "Mainstay"
end
for name, value in pairs(ENUMS.ReportingName.NATO) do
local svalue = string.lower(value)
@@ -2323,6 +2330,16 @@ function UTILS.IsLoadingDoorOpen( unit_name )
return true
end
if type_name == "UH-60L_DAP" and (unit:getDrawArgumentValue(401) == 1 or unit:getDrawArgumentValue(402) == 1) then
BASE:T(unit_name .. " cargo door is open")
return true
end
if type_name == "UH-60L_DAP" and (unit:getDrawArgumentValue(38) > 0 or unit:getDrawArgumentValue(400) == 1 ) then
BASE:T(unit_name .. " front door(s) are open")
return true
end
if type_name == "AH-64D_BLK_II" then
BASE:T(unit_name .. " front door(s) are open")
return true -- no doors on this one ;)
@@ -3210,6 +3227,22 @@ function UTILS.BearingToCardinal(Heading)
end
end
--- Adjust given heading so that is is in [0, 360).
-- @param #number Heading The heading in degrees.
-- @return #number Adjust heading in [0,360).
function UTILS.AdjustHeading360(Heading)
while Heading>=360 or Heading<0 do
if Heading>=360 then
Heading=Heading-360
elseif Heading<0 then
Heading=Heading+360
end
end
return Heading
end
--- Create a BRAA NATO call string BRAA between two GROUP objects
-- @param Wrapper.Group#GROUP FromGrp GROUP object
-- @param Wrapper.Group#GROUP ToGrp GROUP object
@@ -4120,6 +4153,45 @@ function UTILS.LCGRandom()
return UTILS.lcg.seed / UTILS.lcg.m
end
--- Create a table of grid-points for n points.
-- @param #number startVec2 Starting DCS#Vec2 map coordinate, e.g. `{x=63598575,y=-63598575}`
-- @param #number n Number of points to generate.
-- @param #number spacingX Horizonzal spacing (meters).
-- @param #number spacingY Vertical spacing (meters).
-- @return #table Grid Table of DCS#Vec2 entries.
function UTILS.GenerateGridPoints(startVec2, n, spacingX, spacingY)
local points = {}
local gridSize = math.ceil(math.sqrt(n))
local count = 0
local n = n or 1
local spacingX = spacingX or 100
local spacingY = spacingY or 100
local startX = startVec2.x or 100
local startY = startVec2.y or 100
for row = 0, gridSize - 1 do
for col = 0, gridSize - 1 do
if count >= n then
break
end
local point = {
x = startX + (col * spacingX),
y = startY + (row * spacingY)
}
table.insert(points, point)
count = count + 1
end
if count >= n then
break
end
end
return points
end
--- Spawns a new FARP of a defined type and coalition and functional statics (fuel depot, ammo storage, tent, windsock) around that FARP to make it operational.
-- Adds vehicles from template if given. Fills the FARP warehouse with liquids and known materiels.
-- References: [DCS Forum Topic](https://forum.dcs.world/topic/282989-farp-equipment-to-run-it)
@@ -4136,9 +4208,42 @@ end
-- @param #string VehicleTemplate, template name for additional vehicles. Can be nil for no additional vehicles.
-- @param #number Liquids Tons of fuel to be added initially to the FARP. Defaults to 10 (tons). Set to 0 for no fill.
-- @param #number Equipment Number of equipment items per known item to be added initially to the FARP. Defaults to 10 (items). Set to 0 for no fill.
-- @param #number Airframes Number of helicopter airframes per known type in Ops.CSAR#CSAR.AircraftType to be added initially to the FARP. Set to 0 for no airframes.
-- @param #string F10Text Text to display on F10 map if given. Handy to post things like the ADF beacon Frequency, Callsign and ATC Frequency.
-- @param #boolean DynamicSpawns If true, allow Dynamic Spawns from this FARP.
-- @param #boolean HotStart If true and DynamicSpawns is true, allow hot starts for Dynamic Spawns from this FARP.
-- @param #number NumberPads If given, spawn this number of pads.
-- @param #number SpacingX For NumberPads > 1, space this many meters horizontally. Defaults to 100.
-- @param #number SpacingY For NumberPads > 1, space this many meters vertically. Defaults to 100.
-- @return #list<Wrapper.Static#STATIC> Table of spawned objects and vehicle object (if given).
-- @return #string ADFBeaconName Name of the ADF beacon, to be able to remove/stop it later.
function UTILS.SpawnFARPAndFunctionalStatics(Name,Coordinate,FARPType,Coalition,Country,CallSign,Frequency,Modulation,ADF,SpawnRadius,VehicleTemplate,Liquids,Equipment)
-- @return #number MarkerID ID of the F10 Text, to be able to remove it later.
function UTILS.SpawnFARPAndFunctionalStatics(Name,Coordinate,FARPType,Coalition,Country,CallSign,Frequency,Modulation,ADF,SpawnRadius,VehicleTemplate,Liquids,Equipment,Airframes,F10Text,DynamicSpawns,HotStart,NumberPads,SpacingX,SpacingY)
local function PopulateStorage(Name,liquids,equip,airframes)
local newWH = STORAGE:New(Name)
if liquids and liquids > 0 then
-- Storage fill-up
newWH:SetLiquid(STORAGE.Liquid.DIESEL,liquids) -- kgs to tons
newWH:SetLiquid(STORAGE.Liquid.GASOLINE,liquids)
newWH:SetLiquid(STORAGE.Liquid.JETFUEL,liquids)
newWH:SetLiquid(STORAGE.Liquid.MW50,liquids)
end
if equip and equip > 0 then
for cat,nitem in pairs(ENUMS.Storage.weapons) do
for name,item in pairs(nitem) do
newWH:SetItem(item,equip)
end
end
end
if airframes and airframes > 0 then
for typename in pairs (CSAR.AircraftType) do
newWH:SetItem(typename,airframes)
end
end
end
-- Set Defaults
local farplocation = Coordinate
@@ -4152,18 +4257,43 @@ function UTILS.SpawnFARPAndFunctionalStatics(Name,Coordinate,FARPType,Coalition,
local liquids = Liquids or 10
liquids = liquids * 1000 -- tons to kg
local equip = Equipment or 10
local airframes = Airframes or 10
local statictypes = ENUMS.FARPObjectTypeNamesAndShape[farptype] or {TypeName="FARP", ShapeName="FARPS"}
local STypeName = statictypes.TypeName
local SShapeName = statictypes.ShapeName
local Country = Country or (Coalition == coalition.side.BLUE and country.id.USA or country.id.RUSSIA)
local ReturnObjects = {}
-- Spawn FARP
local newfarp = SPAWNSTATIC:NewFromType(STypeName,"Heliports",Country) -- "Invisible FARP" "FARP"
newfarp:InitShape(SShapeName) -- "invisiblefarp" "FARPS"
newfarp:InitFARP(callsign,freq,mod)
local spawnedfarp = newfarp:SpawnFromCoordinate(farplocation,0,Name)
table.insert(ReturnObjects,spawnedfarp)
-- many FARPs
local NumberPads = NumberPads or 1
local SpacingX = SpacingX or 100
local SpacingY = SpacingY or 100
local FarpVec2 = Coordinate:GetVec2()
if NumberPads > 1 then
local Grid = UTILS.GenerateGridPoints(FarpVec2, NumberPads, SpacingX, SpacingY)
for id,gridpoint in ipairs(Grid) do
-- Spawn FARP
local location = COORDINATE:NewFromVec2(gridpoint)
local newfarp = SPAWNSTATIC:NewFromType(STypeName,"Heliports",Country) -- "Invisible FARP" "FARP"
newfarp:InitShape(SShapeName) -- "invisiblefarp" "FARPS"
newfarp:InitFARP(callsign,freq,mod,DynamicSpawns,HotStart)
local spawnedfarp = newfarp:SpawnFromCoordinate(location,0,Name.."-"..id)
table.insert(ReturnObjects,spawnedfarp)
PopulateStorage(Name.."-"..id,liquids,equip,airframes)
end
else
-- Spawn FARP
local newfarp = SPAWNSTATIC:NewFromType(STypeName,"Heliports",Country) -- "Invisible FARP" "FARP"
newfarp:InitShape(SShapeName) -- "invisiblefarp" "FARPS"
newfarp:InitFARP(callsign,freq,mod,DynamicSpawns,HotStart)
local spawnedfarp = newfarp:SpawnFromCoordinate(farplocation,0,Name)
table.insert(ReturnObjects,spawnedfarp)
PopulateStorage(Name,liquids,equip,airframes)
end
-- Spawn Objects
local FARPStaticObjectsNato = {
["FUEL"] = { TypeName = "FARP Fuel Depot", ShapeName = "GSM Rus", Category = "Fortifications"},
@@ -4197,23 +4327,6 @@ function UTILS.SpawnFARPAndFunctionalStatics(Name,Coordinate,FARPType,Coalition,
table.insert(ReturnObjects,spawnedvehicle)
end
local newWH = STORAGE:New(Name)
if liquids and liquids > 0 then
-- Storage fill-up
newWH:SetLiquid(STORAGE.Liquid.DIESEL,liquids) -- kgs to tons
newWH:SetLiquid(STORAGE.Liquid.GASOLINE,liquids)
newWH:SetLiquid(STORAGE.Liquid.JETFUEL,liquids)
newWH:SetLiquid(STORAGE.Liquid.MW50,liquids)
end
if equip and equip > 0 then
for cat,nitem in pairs(ENUMS.Storage.weapons) do
for name,item in pairs(nitem) do
newWH:SetItem(item,equip)
end
end
end
local ADFName
if ADF and type(ADF) == "number" then
local ADFFreq = ADF*1000 -- KHz to Hz
@@ -4224,7 +4337,150 @@ function UTILS.SpawnFARPAndFunctionalStatics(Name,Coordinate,FARPType,Coalition,
trigger.action.radioTransmission(Sound, vec3, 0, true, ADFFreq, 250, ADFName)
end
return ReturnObjects, ADFName
local MarkerID = nil
if F10Text then
local Color = {0,0,1}
if Coalition == coalition.side.RED then
Color = {1,0,0}
elseif Coalition == coalition.side.NEUTRAL then
Color = {0,1,0}
end
local Alpha = 0.75
local coordinate = Coordinate:Translate(600,0)
MarkerID = coordinate:TextToAll(F10Text,Coalition,Color,1,{1,1,1},Alpha,14,true)
end
return ReturnObjects, ADFName, MarkerID
end
--- Spawn a MASH at a given coordinate, optionally, add an ADF Beacon.
-- @param #string Name Unique Name of the Mash.
-- @param Core.Point#COORDINATE Coordinate Coordinate where to spawn the MASH. Can be given as a Core.Zone#ZONE object, in this case we take the center coordinate.
-- @param #number Country Country ID the MASH belongs to, e.g. country.id.USA or country.id.RUSSIA.
-- @param #number ADF (Optional) ADF Frequency in kHz (Kilohertz), if given activate an ADF Beacon at the location of the MASH.
-- @param #string Livery (Optional) The livery of the static CH-47, defaults to dark green.
-- @param #boolean DeployHelo (Optional) If true, deploy the helicopter static.
-- @param #number MASHRadio MASH Radio Frequency, defaults to 127.5.
-- @param #number MASHRadioModulation MASH Radio Modulation, defaults to radio.modulation.AM.
-- @param #number MASHCallsign Defaults to CALLSIGN.FARP.Berlin.
-- @param #table Templates (Optional) You can hand in your own template table of numbered(!) entries. Each entry consist of a relative(!) x,y position and data of a
-- static, shape_name is optional. Also, livery_id is optional, but is applied to the helicopter static only.
-- @return #table Table of Wrapper.Static#STATIC objects that were spawned.
-- @return #string ADFName Name of the ADF Beacon to remove it later.
-- @usage
-- -- MASH Template example, this one is the built in one used in the function:
-- MASHTemplates = {
-- [1]={category='Infantry',type='Soldier M4',shape_name='none',heading=0,x=0.000000,y=0.000000,},
-- [2]={category='Infantry',type='Soldier M4',shape_name='none',heading=0,x=0.313533,y=8.778935,},
-- [3]={category='Infantry',type='Soldier M4',shape_name='none',heading=0,x=16.303737,y=20.379671,},
-- [4]={category='Helicopters',type='CH-47Fbl1',shape_name='none',heading=0,x=-20.047735,y=-63.166179,livery_id = "us army dark green",},
-- [5]={category='Infantry',type='Soldier M4',shape_name='none',heading=0,x=26.650339,y=20.066138,},
-- [6]={category='Heliports',type='FARP_SINGLE_01',shape_name='FARP_SINGLE_01',heading=0,x=-25.432292,y=9.077099,},
-- [7]={category='Heliports',type='FARP_SINGLE_01',shape_name='FARP_SINGLE_01',heading=0,x=-12.717421,y=-3.216114,},
-- [8]={category='Heliports',type='FARP_SINGLE_01',shape_name='FARP_SINGLE_01',heading=0,x=-25.439281,y=-3.216114,},
-- [9]={category='Heliports',type='FARP_SINGLE_01',shape_name='FARP_SINGLE_01',heading=0,x=-12.717421,y=9.155603,},
-- [10]={category='Fortifications',type='TACAN_beacon',shape_name='none',heading=0,x=-2.329847,y=-16.579903,},
-- [11]={category='Fortifications',type='FARP Fuel Depot',shape_name='GSM Rus',heading=0,x=2.222011,y=4.487030,},
-- [12]={category='Fortifications',type='APFC fuel',shape_name='M92_APFCfuel',heading=0,x=3.614927,y=0.367838,},
-- [13]={category='Fortifications',type='Camouflage03',shape_name='M92_Camouflage03',heading=0,x=21.544148,y=21.998879,},
-- [14]={category='Fortifications',type='Container_generator',shape_name='M92_Container_generator',heading=0,x=20.989192,y=37.314334,},
-- [15]={category='Fortifications',type='FireExtinguisher02',shape_name='M92_FireExtinguisher02',heading=0,x=3.988003,y=8.362333,},
-- [16]={category='Fortifications',type='FireExtinguisher02',shape_name='M92_FireExtinguisher02',heading=0,x=-3.953195,y=12.945844,},
-- [17]={category='Fortifications',type='Windsock',shape_name='H-Windsock_RW',heading=0,x=-18.944173,y=-33.042196,},
-- [18]={category='Fortifications',type='Tent04',shape_name='M92_Tent04',heading=0,x=21.220671,y=30.247529,},
-- }
--
function UTILS.SpawnMASHStatics(Name,Coordinate,Country,ADF,Livery,DeployHelo,MASHRadio,MASHRadioModulation,MASHCallsign,Templates)
-- Basic objects table
local MASHTemplates = {
[1]={category='Infantry',type='Soldier M4',shape_name='none',heading=0,x=0.000000,y=0.000000,},
[2]={category='Infantry',type='Soldier M4',shape_name='none',heading=0,x=0.313533,y=8.778935,},
[3]={category='Infantry',type='Soldier M4',shape_name='none',heading=0,x=16.303737,y=20.379671,},
[4]={category='Helicopters',type='CH-47Fbl1',shape_name='none',heading=0,x=-20.047735,y=-63.166179,livery_id = "us army dark green",},
[5]={category='Infantry',type='Soldier M4',shape_name='none',heading=0,x=26.650339,y=20.066138,},
[6]={category='Heliports',type='FARP_SINGLE_01',shape_name='FARP_SINGLE_01',heading=0,x=-25.432292,y=9.077099,},
[7]={category='Heliports',type='FARP_SINGLE_01',shape_name='FARP_SINGLE_01',heading=0,x=-12.717421,y=-3.216114,},
[8]={category='Heliports',type='FARP_SINGLE_01',shape_name='FARP_SINGLE_01',heading=0,x=-25.439281,y=-3.216114,},
[9]={category='Heliports',type='FARP_SINGLE_01',shape_name='FARP_SINGLE_01',heading=0,x=-12.717421,y=9.155603,},
[10]={category='Fortifications',type='TACAN_beacon',shape_name='none',heading=0,x=-2.329847,y=-16.579903,},
[11]={category='Fortifications',type='FARP Fuel Depot',shape_name='GSM Rus',heading=0,x=2.222011,y=4.487030,},
[12]={category='Fortifications',type='APFC fuel',shape_name='M92_APFCfuel',heading=0,x=3.614927,y=0.367838,},
[13]={category='Fortifications',type='Camouflage03',shape_name='M92_Camouflage03',heading=0,x=21.544148,y=21.998879,},
[14]={category='Fortifications',type='Container_generator',shape_name='M92_Container_generator',heading=0,x=20.989192,y=37.314334,},
[15]={category='Fortifications',type='FireExtinguisher02',shape_name='M92_FireExtinguisher02',heading=0,x=3.988003,y=8.362333,},
[16]={category='Fortifications',type='FireExtinguisher02',shape_name='M92_FireExtinguisher02',heading=0,x=-3.953195,y=12.945844,},
[17]={category='Fortifications',type='Windsock',shape_name='H-Windsock_RW',heading=0,x=-18.944173,y=-33.042196,},
[18]={category='Fortifications',type='Tent04',shape_name='M92_Tent04',heading=0,x=21.220671,y=30.247529,},
}
if Templates then MASHTemplates=Templates end
-- locals
local name = Name or "Florence Nightingale"
local positionVec2
local positionVec3
local ReturnStatics = {}
local CountryID = Country or country.id.USA
local livery = "us army dark green"
local MASHRadio = MASHRadio or 127.5
local MASHRadioModulation = MASHRadioModulation or radio.modulation.AM
local MASHCallsign = MASHCallsign or CALLSIGN.FARP.Berlin
-- check for coordinate or zone
if type(Coordinate) == "table" then
if Coordinate:IsInstanceOf("COORDINATE") or Coordinate:IsInstanceOf("ZONE_BASE") then
positionVec2 = Coordinate:GetVec2()
positionVec3 = Coordinate:GetVec3()
end
else
BASE:E("Spawn MASH - no ZONE or COORDINATE handed!")
return
end
-- position
local BaseX = positionVec2.x
local BaseY = positionVec2.y
-- Statics
for id,object in pairs(MASHTemplates) do
local NewName = string.format("%s#%3d",name,id)
local vec2 = {x=BaseX+object.x,y=BaseY+object.y}
local Coordinate=COORDINATE:NewFromVec2(vec2)
local static = SPAWNSTATIC:NewFromType(object.type,object.category,CountryID)
if object.shape_name and object.shape_name ~= "none" then
static:InitShape(object.shape_name)
end
if object.category == "Helicopters" and DeployHelo == true then
if object.livery_id ~= nil then
livery = object.livery_id
end
static:InitLivery(livery)
local newstatic = static:SpawnFromCoordinate(Coordinate,object.heading,NewName)
table.insert(ReturnStatics,newstatic)
elseif object.category == "Heliports" then
static:InitFARP(MASHCallsign,MASHRadio,MASHRadioModulation,false,false)
local newstatic = static:SpawnFromCoordinate(Coordinate,object.heading,NewName)
table.insert(ReturnStatics,newstatic)
elseif object.category ~= "Helicopters" and object.category ~= "Heliports" then
local newstatic = static:SpawnFromCoordinate(Coordinate,object.heading,NewName)
table.insert(ReturnStatics,newstatic)
end
end
-- Beacon
local ADFName
if ADF and type(ADF) == "number" then
local ADFFreq = ADF*1000 -- KHz to Hz
local Sound = "l10n/DEFAULT/beacon.ogg"
ADFName = Name .. " ADF "..tostring(ADF).."KHz"
--BASE:I(string.format("Adding MASH Beacon %d KHz Name %s",ADF,ADFName))
trigger.action.radioTransmission(Sound, positionVec3, 0, true, ADFFreq, 250, ADFName)
end
return ReturnStatics, ADFName
end
--- Converts a Vec2 to a Vec3.
@@ -4421,3 +4677,449 @@ end
function UTILS.Weather.StopFogAnimation()
return world.weather.setFogAnimation({})
end
--- Find a ME created zone by its name
function UTILS.GetEnvZone(name)
for _,v in ipairs(env.mission.triggers.zones) do
if v.name == name then
return v
end
end
end
--- net.dostring_in
function UTILS.DoStringIn(State,DoString)
return net.dostring_in(State,DoString)
end
--- Show a picture on the screen to all
-- @param #string FileName File name of the picture
-- @param #number Duration Duration in seconds, defaults to 10
-- @param #boolean ClearView If true, clears the view before showing the picture, defaults to false
-- @param #number StartDelay Delay in seconds before showing the picture, defaults to 0
-- @param #number HorizontalAlign Horizontal alignment of the picture, 0: Left, 1: Center, 2: Right
-- @param #number VerticalAlign Vertical alignment of the picture, 0: Top, 1: Center, 2: Bottom
-- @param #number Size Size of the picture in percent, defaults to 100
-- @param #number SizeUnits Size units, 0 for % of original picture size, and 1 for % of window size
function UTILS.ShowPictureToAll(FilePath, Duration, ClearView, StartDelay, HorizontalAlign, VerticalAlign, Size, SizeUnits)
ClearView = ClearView or false
StartDelay = StartDelay or 0
HorizontalAlign = HorizontalAlign or 1
VerticalAlign = VerticalAlign or 1
Size = Size or 100
SizeUnits = SizeUnits or 0
if ClearView then ClearView = "true" else ClearView = "false" end
net.dostring_in("mission", string.format("a_out_picture(\"%s\", %d, %s, %d, \"%d\", \"%d\", %d, \"%d\")", FilePath, Duration or 10, ClearView, StartDelay, HorizontalAlign, VerticalAlign, Size, SizeUnits))
end
--- Show a picture on the screen to Coalition
-- @param #number Coalition Coalition ID, can be coalition.side.BLUE, coalition.side.RED or coalition.side.NEUTRAL
-- @param #string FileName File name of the picture
-- @param #number Duration Duration in seconds, defaults to 10
-- @param #boolean ClearView If true, clears the view before showing the picture, defaults to false
-- @param #number StartDelay Delay in seconds before showing the picture, defaults to 0
-- @param #number HorizontalAlign Horizontal alignment of the picture, 0: Left, 1: Center, 2: Right
-- @param #number VerticalAlign Vertical alignment of the picture, 0: Top, 1: Center, 2: Bottom
-- @param #number Size Size of the picture in percent, defaults to 100
-- @param #number SizeUnits Size units, 0 for % of original picture size, and 1 for % of window size
function UTILS.ShowPictureToCoalition(Coalition, FilePath, Duration, ClearView, StartDelay, HorizontalAlign, VerticalAlign, Size, SizeUnits)
ClearView = ClearView or false
StartDelay = StartDelay or 0
HorizontalAlign = HorizontalAlign or 1
VerticalAlign = VerticalAlign or 1
Size = Size or 100
SizeUnits = SizeUnits or 0
if ClearView then ClearView = "true" else ClearView = "false" end
local coalName = string.lower(UTILS.GetCoalitionName(Coalition))
net.dostring_in("mission", string.format("a_out_picture_s(\"%s\", \"%s\", %d, %s, %d, \"%d\", \"%d\", %d, \"%d\")", coalName, FilePath, Duration or 10, ClearView, StartDelay, HorizontalAlign, VerticalAlign, Size, SizeUnits))
end
--- Show a picture on the screen to Country
-- @param #number Country Country ID, can be country.id.USA, country.id.RUSSIA, etc.
-- @param #string FileName File name of the picture
-- @param #number Duration Duration in seconds, defaults to 10
-- @param #boolean ClearView If true, clears the view before showing the picture, defaults to false
-- @param #number StartDelay Delay in seconds before showing the picture, defaults to 0
-- @param #number HorizontalAlign Horizontal alignment of the picture, 0: Left, 1: Center, 2: Right
-- @param #number VerticalAlign Vertical alignment of the picture, 0: Top, 1: Center, 2: Bottom
-- @param #number Size Size of the picture in percent, defaults to 100
-- @param #number SizeUnits Size units, 0 for % of original picture size, and 1 for % of window size
function UTILS.ShowPictureToCountry(Country, FilePath, Duration, ClearView, StartDelay, HorizontalAlign, VerticalAlign, Size, SizeUnits)
ClearView = ClearView or false
StartDelay = StartDelay or 0
HorizontalAlign = HorizontalAlign or 1
VerticalAlign = VerticalAlign or 1
Size = Size or 100
SizeUnits = SizeUnits or 0
if ClearView then ClearView = "true" else ClearView = "false" end
net.dostring_in("mission", string.format("a_out_picture_c(%d, \"%s\", %d, %s, %d, \"%d\", \"%d\", %d, \"%d\")", Country, FilePath, Duration or 10, ClearView, StartDelay, HorizontalAlign, VerticalAlign, Size, SizeUnits))
end
--- Show a picture on the screen to Group
-- @param Wrapper.Group#GROUP Group Group to show the picture to
-- @param #string FileName File name of the picture
-- @param #number Duration Duration in seconds, defaults to 10
-- @param #boolean ClearView If true, clears the view before showing the picture, defaults to false
-- @param #number StartDelay Delay in seconds before showing the picture, defaults to 0
-- @param #number HorizontalAlign Horizontal alignment of the picture, 0: Left, 1: Center, 2: Right
-- @param #number VerticalAlign Vertical alignment of the picture, 0: Top, 1: Center, 2: Bottom
-- @param #number Size Size of the picture in percent, defaults to 100
-- @param #number SizeUnits Size units, 0 for % of original picture size, and 1 for % of window size
function UTILS.ShowPictureToGroup(Group, FilePath, Duration, ClearView, StartDelay, HorizontalAlign, VerticalAlign, Size, SizeUnits)
ClearView = ClearView or false
StartDelay = StartDelay or 0
HorizontalAlign = HorizontalAlign or 1
VerticalAlign = VerticalAlign or 1
Size = Size or 100
SizeUnits = SizeUnits or 0
if ClearView then ClearView = "true" else ClearView = "false" end
net.dostring_in("mission", string.format("a_out_picture_g(%d, \"%s\", %d, %s, %d, \"%d\", \"%d\", %d, \"%d\")", Group:GetID(), FilePath, Duration or 10, ClearView, StartDelay, HorizontalAlign, VerticalAlign, Size, SizeUnits))
end
--- Show a picture on the screen to Unit
-- @param Wrapper.Unit#UNIT Unit Unit to show the picture to
-- @param #string FileName File name of the picture
-- @param #number Duration Duration in seconds, defaults to 10
-- @param #boolean ClearView If true, clears the view before showing the picture, defaults to false
-- @param #number StartDelay Delay in seconds before showing the picture, defaults to 0
-- @param #number HorizontalAlign Horizontal alignment of the picture, 0: Left, 1: Center, 2: Right
-- @param #number VerticalAlign Vertical alignment of the picture, 0: Top, 1: Center, 2: Bottom
-- @param #number Size Size of the picture in percent, defaults to 100
-- @param #number SizeUnits Size units, 0 for % of original picture size, and 1 for % of window size
function UTILS.ShowPictureToUnit(Unit, FilePath, Duration, ClearView, StartDelay, HorizontalAlign, VerticalAlign, Size, SizeUnits)
ClearView = ClearView or false
StartDelay = StartDelay or 0
HorizontalAlign = HorizontalAlign or 1
VerticalAlign = VerticalAlign or 1
Size = Size or 100
SizeUnits = SizeUnits or 0
if ClearView then ClearView = "true" else ClearView = "false" end
net.dostring_in("mission", string.format("a_out_picture_u(%d, \"%s\", %d, %s, %d, \"%d\", \"%d\", %d, \"%d\")", Unit:GetID(), FilePath, Duration or 10, ClearView, StartDelay, HorizontalAlign, VerticalAlign, Size, SizeUnits))
end
--- Load a mission file. This will replace the current mission with the one given carrying along the online clients.
-- @param #string FileName Mission filename
function UTILS.LoadMission(FileName)
net.dostring_in("mission", string.format("a_load_mission(\"%s\")", FileName))
end
--- Set the mission briefing for a coalition.
-- @param #number Coalition Briefing coalition ID, can be coalition.side.BLUE, coalition.side.RED or coalition.side.NEUTRAL
-- @param #string Text Briefing text, can contain newlines, will be converted formatted properly for DCS
-- @param #string Picture Picture file path, can be a file in the DEFAULT folder inside the .miz
function UTILS.SetMissionBriefing(Coalition, Text, Picture)
Text = Text or ""
Text = Text:gsub("\n", "\\n")
Picture = Picture or ""
local coalName = string.lower(UTILS.GetCoalitionName(Coalition))
net.dostring_in("mission", string.format("a_set_briefing(\"%s\", \"%s\", \"%s\")", coalName, Picture, Text))
end
--- Show a helper gate at a DCS#Vec3 position
-- @param DCS#Vec3 pos The position
-- @param #number heading Heading in degrees, can be 0..359 degrees
function UTILS.ShowHelperGate(pos, heading)
net.dostring_in("mission",string.format("a_show_helper_gate(%s, %s, %s, %f)", pos.x, pos.y, pos.z, math.rad(heading)))
end
--- Show a helper gate for a unit.
-- @param Wrapper.Unit#UNIT Unit The unit to show the gate for
-- @param #number Flag Helper gate flag
function UTILS.ShowHelperGateForUnit(Unit, Flag)
net.dostring_in("mission",string.format("a_show_route_gates_for_unit(%d, \"%d\")", Unit:GetID(), Flag))
end
--- Set the carrier illumination mode. -2: OFF, -1: AUTO, 0: NAVIGATION, 1: AC LAUNCH, 2: AC RECOVERY
-- @param #number UnitID Carrier unit ID ( UNIT:GetID() )
-- @param #number Mode Illumination mode, can be -2: OFF, -1: AUTO, 0: NAVIGATION, 1: AC LAUNCH, 2: AC RECOVERY
function UTILS.SetCarrierIlluminationMode(UnitID, Mode)
net.dostring_in("mission",string.format("a_set_carrier_illumination_mode(%d, %d)", UnitID, Mode))
end
--- Shell a zone, zone must ME created
-- @param #string name The name of the ME created zone
-- @param #number power Equals kg of TNT, e.g. 75
-- @param #count Number of shells simulated
function UTILS.ShellZone(name, power, count)
local z = UTILS.GetEnvZone(name)
if z then
net.dostring_in("mission",string.format("a_shelling_zone(%d, %d, %d)", z.zoneId, power, count))
end
end
--- Remove objects from a zone, zone must ME created
-- @param #string name The name of the ME created zone
-- @param #number type Type of objects to remove can be 0:all, 1: trees, 2:objects
function UTILS.RemoveObjects(name, type)
local z = UTILS.GetEnvZone(name)
if z then
net.dostring_in("mission",string.format("a_remove_scene_objects(%d, %d)", z.zoneId, type))
end
end
--- Remove scenery objects from a zone, zone must ME created
-- @param #string name The name of the ME created zone
-- @param #number level Level of removal
function UTILS.DestroyScenery(name, level)
local z = UTILS.GetEnvZone(name)
if z then
net.dostring_in("mission",string.format("a_scenery_destruction_zone(%d, %d)", z.zoneId, level))
end
end
--- 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 DCS##Vec3 Center position vector for the search area.
-- @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 DCS#Vec2 positions that are clear of map objects within the given PosRadius.
function UTILS.GetSimpleZones(Vec3, SearchRadius, PosRadius, NumPositions)
return Disposition.getSimpleZones(Vec3, SearchRadius, PosRadius, NumPositions)
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 Core.Zone#ZONE Zone to search.
-- @param #number (Optional) PosRadius Required clear radius around each position. (Default is math.min(Radius/10, 200))
-- @param #number (Optional) NumPositions Number of positions to find. (Default 50)
-- @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 UTILS.GetClearZonePositions(Zone, PosRadius, NumPositions)
local radius = PosRadius or math.min(Zone:GetRadius()/10, 200)
local clearPositions = UTILS.GetSimpleZones(Zone:GetVec3(), Zone:GetRadius(), radius, NumPositions or 50)
if clearPositions and #clearPositions > 0 then
local validZones = {}
for _, vec2 in pairs(clearPositions) do
if Zone:IsVec2InZone(vec2) then
table.insert(validZones, vec2)
end
end
if #validZones > 0 then
return validZones, radius
end
end
return nil
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 Core.Zone#ZONE Zone to search.
-- @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 UTILS.GetRandomClearZoneCoordinate(Zone, PosRadius, NumPositions)
local clearPositions = UTILS.GetClearZonePositions(Zone, PosRadius, NumPositions)
if clearPositions and #clearPositions > 0 then
local randomPosition, radius = clearPositions[math.random(1, #clearPositions)]
return COORDINATE:NewFromVec2(randomPosition), radius
end
return nil
end
--- Find the point on the radius of a circle closest to a point outside of the radius.
-- @param DCS#Vec2 Vec1 Simple Vec2 marking the middle of the circle.
-- @param #number Radius The radius of the circle.
-- @param DCS#Vec2 Vec2 Simple Vec2 marking the point outside of the circle.
-- @return DCS#Vec2 Vec2 point on the radius.
function UTILS.FindNearestPointOnCircle(Vec1,Radius,Vec2)
local r = Radius
local cx = Vec1.x or 1
local cy = Vec1.y or 1
local px = Vec2.x or 1
local py = Vec2.y or 1
-- Berechne den Vektor vom Mittelpunkt zum externen Punkt
local dx = px - cx
local dy = py - cy
-- Berechne die Länge des Vektors
local dist = math.sqrt(dx * dx + dy * dy)
-- Wenn der Punkt im Mittelpunkt liegt, wähle einen Punkt auf der X-Achse
if dist == 0 then
return {x=cx + r, y=cy}
end
-- Normalisiere den Vektor (richtungsweise Vektor mit Länge 1)
local norm_dx = dx / dist
local norm_dy = dy / dist
-- Berechne den Punkt auf dem Rand des Kreises
local qx = cx + r * norm_dx
local qy = cy + r * norm_dy
local shift_factor = 1
qx = qx + shift_factor * norm_dx
qy = qy + shift_factor * norm_dy
return {x=qx, y=qy}
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.
-- @param #table Positions A table of DCS#Vec2 or DCS#Vec3, can be a units table from the group template.
-- @param DCS#Vec2 Anchor (Optional) DCS#Vec2 or DCS#Vec3 as anchor point to calculate offset of the units.
-- @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.
function UTILS.ValidateAndRepositionGroundUnits(Positions, Anchor, MaxRadius, Spacing)
local units = Positions
Anchor = Anchor or UTILS.GetCenterPoint(units)
local gPos = { x = Anchor.x, y = Anchor.z or Anchor.y }
local maxRadius = 0
local unitCount = 0
for _, unit in pairs(units) do
local pos = { x = unit.x, y = unit.z or unit.y }
local dist = UTILS.VecDist2D(pos, gPos)
if dist > maxRadius then
maxRadius = dist
end
unitCount = unitCount + 1
end
maxRadius = MaxRadius or math.max(maxRadius * 2, 10)
local spacing = Spacing or math.max(maxRadius * 0.05, 5)
if unitCount > 0 and maxRadius > 5 then
local spots = UTILS.GetSimpleZones(UTILS.Vec2toVec3(gPos), maxRadius, spacing, 1000)
if spots and #spots > 0 then
local validSpots = {}
for _, spot in pairs(spots) do -- Disposition sometimes returns points on roads, hence this filter.
if land.getSurfaceType(spot) == land.SurfaceType.LAND then
table.insert(validSpots, spot)
end
end
spots = validSpots
end
local step = spacing
for _, unit in pairs(units) do
local pos = { x = unit.x, y = unit.z or unit.y }
local isOnLand = land.getSurfaceType(pos) == land.SurfaceType.LAND
local isValid = false
if spots and #spots > 0 then
local si = 1
local sid = 0
local closestDist = 100000000
local closestSpot
for _, spot in pairs(spots) do
local dist = UTILS.VecDist2D(pos, spot)
if dist < closestDist then
closestDist = dist
closestSpot = spot
sid = si
end
si = si + 1
end
if closestSpot then
if closestDist >= spacing then
pos = closestSpot
end
isValid = true
table.remove(spots, sid)
end
end
-- Failsafe calculation
if not isValid and not isOnLand then
local h = UTILS.HdgTo(pos, gPos)
local retries = 0
while not isValid and retries < 500 do
local dist = UTILS.VecDist2D(pos, gPos)
pos = UTILS.Vec2Translate(pos, step, h)
local skip = false
for _, unit2 in pairs(units) do
if unit ~= unit2 then
local pos2 = { x = unit2.x, y = unit2.z or unit2.y }
local dist2 = UTILS.VecDist2D(pos, pos2)
if dist2 < 12 then
isValid = false
skip = true
break
end
end
end
if not skip and dist > step and land.getSurfaceType(pos) == land.SurfaceType.LAND then
isValid = true
break
elseif dist <= step then
break
end
retries = retries + 1
end
end
if isValid then
unit.x = pos.x
if unit.z then
unit.z = pos.y
else
unit.y = pos.y
end
end
end
end
end
--- This function uses Disposition and other fallback logic to find better ground positions for statics.
--- 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.
-- @param #table Positions A table of DCS#Vec2 or DCS#Vec3, can be a units table from the group template.
-- @param DCS#Vec2 Position DCS#Vec2 or DCS#Vec3 initial spawn location.
-- @param #number MaxRadius (Optional) Max radius to search for valid ground locations in meters. Default is double the max radius of the static.
-- @return DCS#Vec2 Initial Position if it's valid, else a valid spawn position. nil if no valid position found.
function UTILS.ValidateAndRepositionStatic(Country, Category, Type, Position, ShapeName, MaxRadius)
local coord = COORDINATE:NewFromVec2(Position)
local st = SPAWNSTATIC:NewFromType(Type, Category, Country)
if ShapeName then
st:InitShape(ShapeName)
end
local sName = "s-"..timer.getTime().."-"..math.random(1,10000)
local tempStatic = st:SpawnFromCoordinate(coord, 0, sName)
if tempStatic then
local sRadius = tempStatic:GetBoundingRadius(2) or 3
tempStatic:Destroy()
sRadius = sRadius * 0.5
MaxRadius = MaxRadius or math.max(sRadius * 10, 100)
local positions = UTILS.GetSimpleZones(coord:GetVec3(), MaxRadius, sRadius, 20)
if positions and #positions > 0 then
local closestSpot
local closestDist = math.huge
for _, spot in pairs(positions) do -- Disposition sometimes returns points on roads, hence this filter.
if land.getSurfaceType(spot) == land.SurfaceType.LAND then
local dist = UTILS.VecDist2D(Position, spot)
if dist < closestDist then
closestDist = dist
closestSpot = spot
end
end
end
if closestSpot then
if closestDist >= sRadius then
return closestSpot
else
return Position
end
end
end
end
return nil
end

View File

@@ -449,7 +449,6 @@ AIRBASE.TheChannel = {
-- * AIRBASE.Syria.Al_Dumayr
-- * AIRBASE.Syria.Al_Qusayr
-- * AIRBASE.Syria.Aleppo
-- * AIRBASE.Syria.Amman
-- * AIRBASE.Syria.An_Nasiriyah
-- * AIRBASE.Syria.At_Tanf
-- * AIRBASE.Syria.Bassel_Al_Assad
@@ -511,7 +510,7 @@ AIRBASE.TheChannel = {
-- * AIRBASE.Syria.Wujah_Al_Hajar
-- * AIRBASE.Syria.Ben_Gurion
-- * AIRBASE.Syria.Hatzor
-- * AIRBASE.Syria.Palmashim
-- * AIRBASE.Syria.Palmachim
-- * AIRBASE.Syria.Tel_Nof
-- * AIRBASE.Syria.Marka
--
@@ -523,7 +522,6 @@ AIRBASE.Syria={
["Al_Dumayr"] = "Al-Dumayr",
["Al_Qusayr"] = "Al Qusayr",
["Aleppo"] = "Aleppo",
["Amman"] = "Amman",
["An_Nasiriyah"] = "An Nasiriyah",
["At_Tanf"] = "At Tanf",
["Bassel_Al_Assad"] = "Bassel Al-Assad",
@@ -555,6 +553,7 @@ AIRBASE.Syria={
["Kuweires"] = "Kuweires",
["Lakatamia"] = "Lakatamia",
["Larnaca"] = "Larnaca",
["Marka"] = "Marka",
["Marj_Ruhayyil"] = "Marj Ruhayyil",
["Marj_as_Sultan_North"] = "Marj as Sultan North",
["Marj_as_Sultan_South"] = "Marj as Sultan South",
@@ -585,9 +584,8 @@ AIRBASE.Syria={
["Wujah_Al_Hajar"] = "Wujah Al Hajar",
["Ben_Gurion"] = "Ben Gurion",
["Hatzor"] = "Hatzor",
["Palmashim"] = "Palmashim",
["Palmachim"] = "Palmachim",
["Tel_Nof"] = "Tel Nof",
["Marka"] = "Marka",
}
--- Airbases of the Mariana Islands map:
@@ -613,6 +611,35 @@ AIRBASE.MarianaIslands = {
["Tinian_Intl"] = "Tinian Intl",
}
--- Airbase of the Marianas WWII map
--
-- * AIRBASE.MarianaIslandsWWII.Agana
-- * AIRBASE.MarianaIslandsWWII.Airfield_3
-- * AIRBASE.MarianaIslandsWWII.Charon_Kanoa
-- * AIRBASE.MarianaIslandsWWII.Gurguan_Point
-- * AIRBASE.MarianaIslandsWWII.Isley
-- * AIRBASE.MarianaIslandsWWII.Kagman
-- * AIRBASE.MarianaIslandsWWII.Marpi
-- * AIRBASE.MarianaIslandsWWII.Orote
-- * AIRBASE.MarianaIslandsWWII.Pagan
-- * AIRBASE.MarianaIslandsWWII.Rota
-- * AIRBASE.MarianaIslandsWWII.Ushi
-- @field AIRBASE.MarianaIslandsWWII
AIRBASE.MarianaIslandsWWII =
{
["Agana"] = "Agana",
["Airfield_3"] = "Airfield 3",
["Charon_Kanoa"] = "Charon Kanoa",
["Gurguan_Point"] = "Gurguan Point",
["Isley"] = "Isley",
["Kagman"] = "Kagman",
["Marpi"] = "Marpi",
["Orote"] = "Orote",
["Pagan"] = "Pagan",
["Rota"] = "Rota",
["Ushi"] = "Ushi",
}
--- Airbases of the South Atlantic map:
--
-- * AIRBASE.SouthAtlantic.Almirante_Schroeders
@@ -676,51 +703,56 @@ AIRBASE.SouthAtlantic={
--- Airbases of the Sinai map:
--
-- * AIRBASE.Sinai.Abu_Rudeis
-- * AIRBASE.Sinai.Abu_Suwayr
-- * AIRBASE.Sinai.Al_Bahr_al_Ahmar
-- * AIRBASE.Sinai.Al_Ismailiyah
-- * AIRBASE.Sinai.Al_Khatatbah
-- * AIRBASE.Sinai.Al_Mansurah
-- * AIRBASE.Sinai.Al_Rahmaniyah_Air_Base
-- * AIRBASE.Sinai.As_Salihiyah
-- * AIRBASE.Sinai.AzZaqaziq
-- * AIRBASE.Sinai.Baluza
-- * AIRBASE.Sinai.Ben_Gurion
-- * AIRBASE.Sinai.Beni_Suef
-- * AIRBASE.Sinai.Bilbeis_Air_Base
-- * AIRBASE.Sinai.Bir_Hasanah
-- * AIRBASE.Sinai.Birma_Air_Base
-- * AIRBASE.Sinai.Borj_El_Arab_International_Airport
-- * AIRBASE.Sinai.Cairo_International_Airport
-- * AIRBASE.Sinai.Cairo_West
-- * AIRBASE.Sinai.Difarsuwar_Airfield
-- * AIRBASE.Sinai.El_Arish
-- * AIRBASE.Sinai.El_Gora
-- * AIRBASE.Sinai.El_Minya
-- * AIRBASE.Sinai.Fayed
-- * AIRBASE.Sinai.Gebel_El_Basur_Air_Base
-- * AIRBASE.Sinai.Hatzerim
-- * AIRBASE.Sinai.Hatzor
-- * AIRBASE.Sinai.Hurghada_International_Airport
-- * AIRBASE.Sinai.Inshas_Airbase
-- * AIRBASE.Sinai.Jiyanklis_Air_Base
-- * AIRBASE.Sinai.Kedem
-- * AIRBASE.Sinai.Kibrit_Air_Base
-- * AIRBASE.Sinai.Kom_Awshim
-- * AIRBASE.Sinai.Melez
-- * AIRBASE.Sinai.Nevatim
-- * AIRBASE.Sinai.Ovda
-- * AIRBASE.Sinai.Palmachim
-- * AIRBASE.Sinai.Quwaysina
-- * AIRBASE.Sinai.Ramon_Airbase
-- * AIRBASE.Sinai.Ramon_International_Airport
-- * AIRBASE.Sinai.Sde_Dov
-- * AIRBASE.Sinai.Sharm_El_Sheikh_International_Airport
-- * AIRBASE.Sinai.St_Catherine
-- * AIRBASE.Sinai.Tel_Nof
-- * AIRBASE.Sinai.Wadi_Abu_Rish
-- * AIRBASE.Sinai.Wadi_al_Jandali
-- * AIRBASE.SinaiMap.Abu_Rudeis
-- * AIRBASE.SinaiMap.Abu_Suwayr
-- * AIRBASE.SinaiMap.Al_Bahr_al_Ahmar
-- * AIRBASE.SinaiMap.Al_Ismailiyah
-- * AIRBASE.SinaiMap.Al_Khatatbah
-- * AIRBASE.SinaiMap.Al_Mansurah
-- * AIRBASE.SinaiMap.Al_Rahmaniyah_Air_Base
-- * AIRBASE.SinaiMap.As_Salihiyah
-- * AIRBASE.SinaiMap.AzZaqaziq
-- * AIRBASE.SinaiMap.Baluza
-- * AIRBASE.SinaiMap.Ben_Gurion
-- * AIRBASE.SinaiMap.Beni_Suef
-- * AIRBASE.SinaiMap.Bilbeis_Air_Base
-- * AIRBASE.SinaiMap.Bir_Hasanah
-- * AIRBASE.SinaiMap.Birma_Air_Base
-- * AIRBASE.SinaiMap.Borg_El_Arab_International_Airport
-- * AIRBASE.SinaiMap.Cairo_International_Airport
-- * AIRBASE.SinaiMap.Cairo_West
-- * AIRBASE.SinaiMap.Damascus_Intl
-- * AIRBASE.SinaiMap.Difarsuwar_Airfield
-- * AIRBASE.SinaiMap.El_Arish
-- * AIRBASE.SinaiMap.El_Gora
-- * AIRBASE.SinaiMap.El_Minya
-- * AIRBASE.SinaiMap.Fayed
-- * AIRBASE.SinaiMap.Gebel_El_Basur_Air_Base
-- * AIRBASE.SinaiMap.Hatzerim
-- * AIRBASE.SinaiMap.Hatzor
-- * AIRBASE.SinaiMap.Hurghada_International_Airport
-- * AIRBASE.SinaiMap.Inshas_Airbase
-- * AIRBASE.SinaiMap.Jiyanklis_Air_Base
-- * AIRBASE.SinaiMap.Kedem
-- * AIRBASE.SinaiMap.Kibrit_Air_Base
-- * AIRBASE.SinaiMap.Kom_Awshim
-- * AIRBASE.SinaiMap.Melez
-- * AIRBASE.SinaiMap.Mezzeh_Air_Base
-- * AIRBASE.SinaiMap.Nevatim
-- * AIRBASE.SinaiMap.Ovda
-- * AIRBASE.SinaiMap.Palmachim
-- * AIRBASE.SinaiMap.Quwaysina
-- * AIRBASE.SinaiMap.Rafic_Hariri_Intl
-- * AIRBASE.SinaiMap.Ramat_David
-- * AIRBASE.SinaiMap.Ramon_Airbase
-- * AIRBASE.SinaiMap.Ramon_International_Airport
-- * AIRBASE.SinaiMap.Sde_Dov
-- * AIRBASE.SinaiMap.Sharm_El_Sheikh_International_Airport
-- * AIRBASE.SinaiMap.St_Catherine
-- * AIRBASE.SinaiMap.Tabuk
-- * AIRBASE.SinaiMap.Tel_Nof
-- * AIRBASE.SinaiMap.Wadi_Abu_Rish
-- * AIRBASE.SinaiMap.Wadi_al_Jandali
--
-- @field Sinai
AIRBASE.Sinai = {
@@ -739,9 +771,10 @@ AIRBASE.Sinai = {
["Bilbeis_Air_Base"] = "Bilbeis Air Base",
["Bir_Hasanah"] = "Bir Hasanah",
["Birma_Air_Base"] = "Birma Air Base",
["Borj_El_Arab_International_Airport"] = "Borj El Arab International Airport",
["Borg_El_Arab_International_Airport"] = "Borg El Arab International Airport",
["Cairo_International_Airport"] = "Cairo International Airport",
["Cairo_West"] = "Cairo West",
["Damascus_Intl"] = "Damascus Intl",
["Difarsuwar_Airfield"] = "Difarsuwar Airfield",
["El_Arish"] = "El Arish",
["El_Gora"] = "El Gora",
@@ -757,15 +790,19 @@ AIRBASE.Sinai = {
["Kibrit_Air_Base"] = "Kibrit Air Base",
["Kom_Awshim"] = "Kom Awshim",
["Melez"] = "Melez",
["Mezzeh_Air_Base"] = "Mezzeh Air Base",
["Nevatim"] = "Nevatim",
["Ovda"] = "Ovda",
["Palmachim"] = "Palmachim",
["Quwaysina"] = "Quwaysina",
["Rafic_Hariri_Intl"] = "Rafic Hariri Intl",
["Ramat_David"] = "Ramat David",
["Ramon_Airbase"] = "Ramon Airbase",
["Ramon_International_Airport"] = "Ramon International Airport",
["Sde_Dov"] = "Sde Dov",
["Sharm_El_Sheikh_International_Airport"] = "Sharm El Sheikh International Airport",
["St_Catherine"] = "St Catherine",
["Tabuk"] = "Tabuk",
["Tel_Nof"] = "Tel Nof",
["Wadi_Abu_Rish"] = "Wadi Abu Rish",
["Wadi_al_Jandali"] = "Wadi al Jandali",
@@ -830,6 +867,12 @@ AIRBASE.Kola = {
["Enontekio"] = "Enontekio",
["Evenes"] = "Evenes",
["Hosio"] = "Hosio",
["Kilpyavr"] = "Kilpyavr",
["Afrikanda"] = "Afrikanda",
["Kalevala"] = "Kalevala",
["Koshka_Yavr"] = "Koshka Yavr",
["Poduzhemye"] = "Poduzhemye",
["Luostari_Pechenga"] = "Luostari Pechenga",
}
--- Airbases of the Afghanistan map
@@ -890,37 +933,51 @@ AIRBASE.Afghanistan = {
--- Airbases of the Iraq map
--
-- * AIRBASE.Iraq.Baghdad_International_Airport
-- * AIRBASE.Iraq.Sulaimaniyah_International_Airport
-- * AIRBASE.Iraq.Al_Sahra_Airport
-- * AIRBASE.Iraq.Erbil_International_Airpor
-- * AIRBASE.Iraq.Al_Taji_Airport
-- * AIRBASE.Iraq.Al_Asad_Airbase
-- * AIRBASE.Iraq.Al_Kut_Airbase
-- * AIRBASE.Iraq.Al_Sahra_Airport
-- * AIRBASE.Iraq.Al_Salam_Airbase
-- * AIRBASE.Iraq.Balad_Airbase
-- * AIRBASE.Iraq.Kirkuk_International_Airport
-- * AIRBASE.Iraq.Bashur_Airport
-- * AIRBASE.Iraq.Al_Taji_Airport
-- * AIRBASE.Iraq.Al_Taquddum_Airport
-- * AIRBASE.Iraq.Qayyarah_Airfield_West
-- * AIRBASE.Iraq.Baghdad_International_Airport
-- * AIRBASE.Iraq.Balad_Airbase
-- * AIRBASE.Iraq.Bashur_Airport
-- * AIRBASE.Iraq.Erbil_International_Airport
-- * AIRBASE.Iraq.Sulaimaniyah_International_Airport
-- * AIRBASE.Iraq.H2_Airbase
-- * AIRBASE.Iraq.H3_Main_Airbase
-- * AIRBASE.Iraq.H3_Northwest_Airbase
-- * AIRBASE.Iraq.H3_Southwest_Airbase
-- * AIRBASE.Iraq.K1_Base
-- * AIRBASE.Iraq.Kirkuk_International_Airport
-- * AIRBASE.Iraq.Mosul_International_Airport
-- * AIRBASE.Iraq.Qayyarah_Airfield_West
-- * AIRBASE.Iraq.Sulaimaniyah_International_Airport
--
-- @field Iraq
AIRBASE.Iraq = {
["Baghdad_International_Airport"] = "Baghdad International Airport",
["Sulaimaniyah_International_Airport"] = "Sulaimaniyah International Airport",
["Al_Sahra_Airport"] = "Al-Sahra Airport",
["Erbil_International_Airport"] = "Erbil International Airport",
["Al_Taji_Airport"] = "Al-Taji Airport",
["Al_Asad_Airbase"] = "Al-Asad Airbase",
["Al_Kut_Airport"] = "Al-Kut Airport",
["Al_Sahra_Airport"] = "Al-Sahra Airport",
["Al_Salam_Airbase"] = "Al-Salam Airbase",
["Balad_Airbase"] = "Balad Airbase",
["Kirkuk_International_Airport"] = "Kirkuk International Airport",
["Bashur_Airport"] = "Bashur Airport",
["Al_Taji_Airport"] = "Al-Taji Airport",
["Al_Taquddum_Airport"] = "Al-Taquddum Airport",
["Qayyarah_Airfield_West"] = "Qayyarah Airfield West",
["Baghdad_International_Airport"] = "Baghdad International Airport",
["Balad_Airbase"] = "Balad Airbase",
["Bashur_Airport"] = "Bashur Airport",
["Erbil_International_Airport"] = "Erbil International Airport",
["H2_Airbase"] = "H-2 Airbase",
["H3_Main_Airbase"] = "H-3 Main Airbase",
["H3_Northwest_Airbase"] = "H-3 Northwest Airbase",
["H3_Southwest_Airbase"] = "H-3 Southwest Airbase",
["K1_Base"] = "K1 Base",
["Kirkuk_International_Airport"] = "Kirkuk International Airport",
["Mosul_International_Airport"] = "Mosul International Airport",
["Qayyarah_Airfield_West"] = "Qayyarah Airfield West",
["Sulaimaniyah_International_Airport"] = "Sulaimaniyah International Airport",
}
--- Airbases of the Germany Cold War map
-- * AIRBASE.GermanyCW.Airracing_Frankfurt
-- * AIRBASE.GermanyCW.Airracing_Frankfurt
@@ -1434,7 +1491,7 @@ function AIRBASE:Register(AirbaseName)
self.descriptors=self:GetDesc()
-- Debug info.
--self:I({airbase=AirbaseName, descriptors=self.descriptors})
--self:T({airbase=AirbaseName, descriptors=self.descriptors})
-- Category.
self.category=self.descriptors and self.descriptors.category or Airbase.Category.AIRDROME
@@ -1517,6 +1574,17 @@ end
return self
end
--- Get the true airbase center as seen in the ME. The position returned by the dcs object is is wrong and often at the start of the runway.
-- @return DCS#Vec2 The center of the true center of the airbase if it contains runways, otherwise the default DCS object position.
function AIRBASE:GetVec2()
local runways = self:GetRunways()
if runways and #runways > 0 then
return runways[1].center:GetVec2()
end
return self:GetCoordinate():GetVec2()
end
--- Get the category of this airbase. This is only a debug function because DCS 2.9 incorrectly returns heliports as airdromes.
-- @param #AIRBASE self
function AIRBASE:_GetCategory()
@@ -2601,6 +2669,7 @@ function AIRBASE:_InitRunways(IncludeInverse)
--runway.name=string.format("%02d", tonumber(name))
runway.magheading=tonumber(runway.name)*10
runway.idx=runway.magheading
runway.heading=heading
runway.width=width or 0
runway.length=length or 0
@@ -2913,6 +2982,7 @@ function AIRBASE:GetRunwayData(magvar, mark)
local runway={} --#AIRBASE.Runway
runway.heading=hdg
runway.idx=idx
runway.magheading=idx
runway.length=c1:Get2DDistance(c2)
runway.position=c1
runway.endpoint=c2
@@ -2928,6 +2998,57 @@ function AIRBASE:GetRunwayData(magvar, mark)
-- Add runway.
table.insert(runways, runway)
end
-- Look for identical (parallel) runways, e.g. 03L and 03R at Nellis.
local rpairs={}
for i,_ri in pairs(runways) do
local ri=_ri --#AIRBASE.Runway
for j,_rj in pairs(runways) do
local rj=_rj --#AIRBASE.Runway
if i<j then
if ri.name==rj.name then
rpairs[i]=j
end
end
end
end
local function isLeft(a, b, c)
--return ((b.x - a.x)*(c.z - a.z) - (b.z - a.z)*(c.x - a.x)) > 0
return ((b.z - a.z)*(c.x - a.x) - (b.x - a.x)*(c.z - a.z)) > 0
end
for i,j in pairs(rpairs) do
local ri=runways[i] --#AIRBASE.Runway
local rj=runways[j] --#AIRBASE.Runway
-- Draw arrow.
--ri.center:ArrowToAll(rj.center)
local c0=ri.position
-- Vector in the direction of the runway.
local a=UTILS.VecTranslate(c0, 1000, ri.heading)
-- Vector from runway i to runway j.
local b=UTILS.VecSubstract(rj.position, ri.position)
b=UTILS.VecAdd(ri.position, b)
-- Check if rj is left of ri.
local left=isLeft(c0, a, b)
--env.info(string.format("Found pair %s: i=%d, j=%d, left==%s", ri.name, i, j, tostring(left)))
if left then
ri.isLeft=false
rj.isLeft=true
else
ri.isLeft=true
rj.isLeft=false
end
--break
end
return runways

View File

@@ -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 = {
@@ -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',
@@ -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`

View File

@@ -108,6 +108,8 @@ DYNAMICCARGO.State = {
-- @type DYNAMICCARGO.AircraftTypes
DYNAMICCARGO.AircraftTypes = {
["CH-47Fbl1"] = "CH-47Fbl1",
["Mi-8MTV2"] = "CH-47Fbl1",
["Mi-8MT"] = "CH-47Fbl1",
}
--- Helo types possible.
@@ -120,17 +122,30 @@ 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,
},
}
--- DYNAMICCARGO class version.
-- @field #string version
DYNAMICCARGO.version="0.0.7"
DYNAMICCARGO.version="0.0.9"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: A lot...
-- DONE: Added Mi-8 type and dimensions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor

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",
@@ -1150,6 +1152,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 +2249,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 +2661,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 +2704,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 +3218,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

View File

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

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

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