Compare commits

..

658 Commits
v1.1 ... 2.1.2

Author SHA1 Message Date
FlightControl
ee20d91a5e Merge remote-tracking branch 'refs/remotes/origin/master-release-prep' 2017-05-18 14:17:07 +02:00
FlightControl
775a3b20ab Dynamic 2017-05-18 14:16:03 +02:00
FlightControl
97179cc0b3 Merge remote-tracking branch 'refs/remotes/origin/master-release-2.1' into master-release-prep 2017-05-18 14:13:56 +02:00
FlightControl
b26d5e09e6 Remove the moose.lua file 2017-05-18 14:13:29 +02:00
FlightControl
091373f98f Fixes #528 - DETECTION_UNITS uses a string as the index 2017-05-18 14:10:20 +02:00
FlightControl
694798947f New version 2017-05-18 10:12:26 +02:00
FlightControl
87fb057e0e Remove moose.lua 2017-05-18 10:08:24 +02:00
FlightControl
1f2e3d6514 Removed moose.lua 2017-05-18 10:07:28 +02:00
FlightControl
71566e3a53 Merge remote-tracking branch 'refs/remotes/origin/master-release-prep' 2017-05-18 10:04:34 +02:00
FlightControl
a644d3b8c8 Dynamic Moose.lua 2017-05-18 10:03:51 +02:00
Sven Van de Velde
7707293615 Merge pull request #527 from FlightControl-Master/master-release-prep
Fixes issue #525
2017-05-18 09:30:16 +02:00
FlightControl
3da82f595a Merge remote-tracking branch 'origin/master-release-prep' into master-release-prep
# Conflicts:
#	Moose Mission Setup/Moose.lua
2017-05-18 09:28:41 +02:00
FlightControl
b5f8a5aad6 Static version 2017-05-18 09:27:21 +02:00
Sven Van de Velde
596e442b8a Merge pull request #526 from FlightControl-Master/525-R21-Fix-messages-displayed-in-tasking
525 r21 fix messages displayed in tasking
2017-05-18 09:25:46 +02:00
FlightControl
c83cdfdeff Update Moose.lua 2017-05-18 09:24:19 +02:00
FlightControl
6d1ab6bd98 Updated messages being displayed wrongly 2017-05-18 09:16:39 +02:00
FlightControl
b96628eba8 Release notes 2017-05-17 10:27:38 +02:00
FlightControl
0bc7c4138a Static moose.lua 2017-05-17 06:13:18 +02:00
Sven Van de Velde
da842491d0 Merge pull request #524 from FlightControl-Master/master
Release 2.1 preparation
2017-05-17 06:11:25 +02:00
FlightControl
c036b68f40 Updated release notes 2.1 2017-05-17 06:05:20 +02:00
FlightControl
caedaddd06 Fixed with A2G tasking
Imagine, the mission had a static moose.lua. I was testing with a static
moose.lua
2017-05-16 21:24:38 +02:00
Sven Van de Velde
1662b891df Merge pull request #523 from FlightControl-Master/522-Fix-Mission-complete-when-one-task-is-finished
Mission complete disabled when a task is finished
2017-05-16 17:04:16 +02:00
FlightControl
71c59aec74 Mission complete disabled when a task is finished
The concept was completely wrong. A mission will require separate logic
or triggers to register goal completion. I will need to do a training
video on that one.
2017-05-16 16:58:40 +02:00
FlightControl
aa159b1337 Documentation tags and release prep text 2017-05-16 10:35:26 +02:00
Sven Van de Velde
666544b5ba Merge pull request #521 from FlightControl-Master/511-Fix-Make-menu-option-cancel-route
Cancel route implemented
2017-05-15 22:24:36 +02:00
FlightControl
0e0e9bb550 Cancel route implemented
-- Cancel route option added
-- Added first version of MGRS or LL coordinate system.
2017-05-15 22:24:10 +02:00
Sven Van de Velde
666a3708d7 Merge pull request #520 from FlightControl-Master/513-Fix-deploy-troops-outside-a-deploy-zone
Fixed
2017-05-15 13:05:50 +02:00
FlightControl
30d9f369f3 Fixed
When the deployzone is nil, the troops are deployed 60 meters in front
of the carrier.
2017-05-15 13:05:19 +02:00
Sven Van de Velde
4d52c0ce3e Merge pull request #519 from FlightControl-Master/517-Fix-designate-failing-on-empty-set
Fixed #517 - When detected set is empty, script fails when lasing is activated.
2017-05-15 12:40:58 +02:00
FlightControl
cc4a6a5f01 Fixed #517 - When detected set is empty, script fails when lasing is activated.
Now, when there is no target detected and lase is activated, the system
does not crash.
2017-05-15 12:40:32 +02:00
Sven Van de Velde
9053f99960 Merge pull request #510 from FlightControl-Master/TASKING
Updated the mission menu generation and task menu maintenance

-- Added Mission Briefing menu showing the Mission Briefing.
-- Mission menus are generated once for the groups when they belong to
the SetGroup.
2017-05-14 08:34:17 +02:00
FlightControl
7df1963de4 Updated the mission menu generation and task menu maintenance
-- Added Mission Briefing menu showing the Mission Briefing.
-- Mission menus are generated once for the groups when they belong to
the SetGroup.
2017-05-14 08:33:28 +02:00
FlightControl
bb8eaf3f03 Merge remote-tracking branch 'refs/remotes/origin/master' into TASKING 2017-05-14 07:00:43 +02:00
FlightControl
4f806d3e4b Documentation AI_FORMATION 2017-05-13 13:34:57 +02:00
FlightControl
755e71f6d9 Added
-- FormationBox
-- FormationTrail
-- Optimized formation forming by inclination...
2017-05-13 13:29:44 +02:00
FlightControl
4359831423 Updates in POSITIONABLE
-- Added :GetBoundingBox()
-- Added :GetHeight()
-- Updated documentation
2017-05-13 06:36:13 +02:00
FlightControl
bd75743800 Much improved AI_FORMATION version
not the different models supported, but the core logic to route the
followers.
fiew! Was the whole day busy with this!
2017-05-12 18:39:09 +02:00
FlightControl
ea1b204145 Update with correction of angle 2017-05-11 21:48:16 +02:00
FlightControl
f8454daf9f Removed cargo menu is ready. 2017-05-11 21:17:12 +02:00
FlightControl
546b960951 method AI_FORMATION:FormationCenterWing implemented. 2017-05-11 14:10:49 +02:00
FlightControl
98fb15dfb7 Documentation 2017-05-11 12:50:19 +02:00
FlightControl
675e8e7f31 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	docs/Documentation/Task_Cargo.html
2017-05-11 12:49:11 +02:00
FlightControl
15c52e03d9 Documentation updates 2017-05-11 12:48:10 +02:00
Sven Van de Velde
9fbd9000b9 Merge pull request #506 from FlightControl-Master/New-ai-formation
AI_FORMATION documentation
2017-05-11 12:45:41 +02:00
FlightControl
80a88058c0 Documentation 2017-05-11 12:45:01 +02:00
FlightControl
578630f2f2 Merge remote-tracking branch 'refs/remotes/origin/master' into New-ai-formation 2017-05-11 12:39:40 +02:00
Sven Van de Velde
cc7b2fd061 Merge pull request #505 from FlightControl-Master/New-ai-formation
AI_FORMATION class
2017-05-11 12:39:15 +02:00
FlightControl
2c4a33aacb Fixes 2017-05-11 12:38:24 +02:00
FlightControl
d4d5176f51 Merge remote-tracking branch 'refs/remotes/origin/master' into New-ai-formation 2017-05-11 11:47:35 +02:00
FlightControl
b7d37c42af Working Version! 2017-05-11 11:47:30 +02:00
Sven Van de Velde
e387ea5723 Merge pull request #503 from FlightControl-Master/New-ai-formation
Release of AI_FORMATION
2017-05-10 10:37:10 +02:00
FlightControl
b9c51ffc75 Release of AI_FORMATION 2017-05-10 10:33:23 +02:00
FlightControl
d4aa91300d Documentation updates 2017-05-10 09:52:11 +02:00
Sven Van de Velde
172696c3a5 Merge pull request #502 from FlightControl-Master/344-AI-Bomb-Zone
Attack map object added
2017-05-09 21:38:01 +02:00
FlightControl
7c9d6774c4 Documentation 2017-05-09 21:37:24 +02:00
FlightControl
4ed94d9051 Merge remote-tracking branch 'refs/remotes/origin/master' into 344-AI-Bomb-Zone
# Conflicts:
#	Moose Mission Setup/Moose.lua
2017-05-09 21:30:18 +02:00
FlightControl
f359c8c380 Attack map object as part of BA 2017-05-09 21:29:19 +02:00
FlightControl
299fef16c1 Fixes for tasking 2017-05-09 14:16:22 +02:00
FlightControl
03d215dc2a Home page change and BAI name changes 2017-05-09 11:25:33 +02:00
Sven Van de Velde
592689d494 Merge pull request #501 from FlightControl-Master/344-AI-Bomb-Zone
344 ai bomb zone
2017-05-09 10:49:00 +02:00
FlightControl
743fa8ced1 new AI_BAI class 2017-05-09 10:40:05 +02:00
FlightControl
f181101d8b Merge remote-tracking branch 'refs/remotes/origin/master' into 344-AI-Bomb-Zone 2017-05-09 09:29:43 +02:00
FlightControl
d8ba37af8d When cargo is destroyed, it will stop working... 2017-05-09 09:29:08 +02:00
FlightControl
ce1f85e09a Fixed menu and deployment of cargo
-- You can now deploy cargo anywhere in the battlefield.
-- The menu generation is improved.
---- No more first remove menu and then build, but "refresh"...
2017-05-08 16:28:36 +02:00
FlightControl
3329465fd3 Improved boarding process
Fixed boarding process when a carrier would ascend while boarding, the
boarding process would be cancelled by the cargo. the carrier is
notified of that event. When the carrier lands again, he can again board
the cargo using the cargo menu options.
2017-05-08 14:57:16 +02:00
FlightControl
2fadd949a6 Fixes 2017-05-07 15:37:27 +02:00
FlightControl
d2d59a7ba3 Fixes cargo 2017-05-06 23:06:21 +02:00
FlightControl
11c20d57fd Fixes
-- Cargo deployment
-- Mission text in menu
-- Event log decrease
2017-05-06 10:11:22 +02:00
FlightControl
192dbadd51 Cargo and smoke 2017-05-05 23:16:57 +02:00
FlightControl
c9121ed672 Menu fixes and Report Fixes
-- Added player count to plannes task menus
-- Detailed task report only shows the players in the task.
-- Added method GetPlayerNames for a Task
-- Added method GetPlayerCount for a Task
-- Started with a threat level implementation on the menus, but there is
a problem with the refresh...
2017-05-05 19:50:28 +02:00
132nd-Entropy
6d4eb818ba Merge pull request #499 from FlightControl-Master/Bugfix-132nd
Fix Missile Distance Message in Missile Trainer Menu
2017-05-05 16:52:53 +02:00
FlightControl
cb7ba702ff Correct handling of crashing player
when assigned to a task
2017-05-05 15:50:21 +02:00
FlightControl
9a54462164 Fixed problem upon mission success. 2017-05-05 14:37:55 +02:00
entropySG
dac4c48850 Fix Missile Distance Message in Missile Trainer Menu 2017-05-05 14:32:56 +02:00
FlightControl
625450ba12 Event prefix can be nil, if the event occurs on a non-spawned group with no # tag. 2017-05-05 13:37:04 +02:00
Sven Van de Velde
96aa49d682 Merge pull request #495 from FlightControl-Master/TASKING
Tasking
2017-05-05 12:47:50 +02:00
FlightControl
6f9bfc4211 Abort logic improvements 2017-05-05 12:40:16 +02:00
FlightControl
0b87b265c7 Fixing abort 2017-05-05 12:05:46 +02:00
FlightControl
1c4002fb37 Fixes abort 2017-05-05 11:50:24 +02:00
FlightControl
82477c93d2 Fixed abort logic and trace overhead on menus 2017-05-05 11:45:17 +02:00
FlightControl
02e2f21ec6 test good abort
when more than one player is still assigned to the task, the task should
not abort, only the player...
2017-05-05 10:57:29 +02:00
FlightControl
99cbe0c8bb Heavy rework of the menus and now it is much better 2017-05-05 10:50:12 +02:00
FlightControl
7a84b6cc35 Improvements on task assignment logic 2017-05-05 09:34:51 +02:00
FlightControl
16e6730dc1 Trace 2017-05-04 16:19:04 +02:00
FlightControl
e3a0a67fa5 Update Task Abort process 2017-05-04 15:53:25 +02:00
FlightControl
baa891c7f5 Merge remote-tracking branch 'refs/remotes/origin/master' into TASKING 2017-05-04 15:53:09 +02:00
FlightControl
4130b833fb Turn off default menu 2017-05-04 15:36:46 +02:00
Sven Van de Velde
cb33fde27b Merge pull request #493 from FlightControl-Master/TASKING
Tasking
2017-05-04 14:32:31 +02:00
FlightControl
5bb91646d7 Updates 2017-05-04 14:23:10 +02:00
FlightControl
a499c04fa4 Fix crash 2017-05-04 14:05:57 +02:00
FlightControl
b4107b14f8 Eliminate trace details 2017-05-04 13:33:13 +02:00
FlightControl
2519fcde48 No loop logic 2017-05-04 12:25:50 +02:00
FlightControl
559e668ff9 Updates 2017-05-04 12:22:05 +02:00
Sven Van de Velde
c998151a74 Merge pull request #491 from FlightControl-Master/Grey-Echo
Slate Documentation
2017-05-04 10:38:47 +02:00
FlightControl
0eb9d1917b Updates 2017-05-04 10:19:34 +02:00
FlightControl
4fcb7be9fc Merge remote-tracking branch 'refs/remotes/origin/master' into TASKING 2017-05-04 09:00:29 +02:00
Sven Van de Velde
92246b45d6 Merge pull request #492 from FlightControl-Master/489-Detection-Unit
Attempt to fix the count problem
2017-05-04 06:35:36 +02:00
FlightControl
a06c6176a2 Attempt to fix the count problem 2017-05-04 06:33:40 +02:00
Grey-Echo
0deab0c625 Add the deploy script
Dac update too
2017-05-03 18:04:41 +02:00
Grey-Echo
be983ccbd5 Doc 2017-05-03 16:58:31 +02:00
Grey-Echo
7b2f0fda9c Update to the documentation generation process
Include a doc update too.
2017-05-03 16:35:34 +02:00
Sven Van de Velde
9ca007ccf4 Merge pull request #490 from FlightControl-Master/489-Detection-Unit
Optimizations for detection. fixes issue #489
2017-05-03 12:07:40 +02:00
FlightControl
c7d6027734 Optimizations for detection
-- Detected range will now correctly update between detections.
-- Detected known or unknown type will now be correctly reported.
-- Detected distance is now correctly estimated or known.
-- Removed visual detection, because it just is not working correctly.
-- Cleaned up the code.
-- Added new fields in DetectedObject structure.
-- Added parameters for IsTargetDetected API
2017-05-03 12:05:15 +02:00
FlightControl
26033a4172 Check players 2017-04-27 12:11:09 +02:00
FlightControl
5a2594853c Remove static event trace of tires 2017-04-27 10:22:29 +02:00
FlightControl
c5587304f7 Trace every event. Some stuff is fishy on MP 2017-04-27 10:12:48 +02:00
FlightControl
90f614a5c0 Merge remote-tracking branch 'refs/remotes/origin/master' into TASKING 2017-04-27 10:01:31 +02:00
Sven Van de Velde
5c06aaf3ed Merge pull request #484 from FlightControl-Master/TASKING
Improvements... Fixes
2017-04-26 22:36:38 +02:00
FlightControl
858670ab80 Improvements 2017-04-26 22:34:17 +02:00
Sven Van de Velde
979bc8cf8b Merge pull request #483 from FlightControl-Master/480-tasks-added-fix
Fixed issue #480

-- The logic has been reviewed
-- There is now a direct link between the Detection ID and the Task in the A2G_TASK_DISPATCHER.
- The Task is searched by Detection ID. Thus, any task with a different name but already existing is either removed and re-added, or, left untouched. So, a BAI task will stay a BAI task if the task has been assigned to a group.
- If a Task is Planned, it will be evaluated to be deleted, but only then. Any other status will keep the task.
- Improved the message appearing of the available tasks of a Mission after a detection run.
2017-04-26 21:35:35 +02:00
FlightControl
8e9129b3b6 Fixed issue #480 2017-04-26 21:31:23 +02:00
Sven Van de Velde
3451ee837d Merge pull request #482 from FlightControl-Master/479-Task-Status-Reporting-updating
Fixeds for tasking
2017-04-26 11:34:15 +02:00
FlightControl
755343d02e Fixeds for tasking
- Fixed the hanging report (I think)
- Fixed the routing messages appearing every time again
- Fixed the Task menu
2017-04-26 11:33:48 +02:00
FlightControl
303b3a0efe First version 2017-04-26 09:59:10 +02:00
Sven Van de Velde
6e353cf893 Merge pull request #475 from FlightControl-Master/456-cargo-landing-event
Fixed #456
2017-04-26 06:29:46 +02:00
FlightControl
d369a1be9f Fixed #456
-- After landing the tasking menu is displayed again.
-- The cargo is boarded properly now. Every 15 seconds is checked if the
cargo is still moving.
2017-04-26 06:29:16 +02:00
Sven Van de Velde
a87ab0fd82 Merge pull request #474 from FlightControl-Master/386-ai-designate
386 ai designate
2017-04-25 21:45:01 +02:00
FlightControl
1977296a12 Merge remote-tracking branch 'refs/remotes/origin/master' into 386-ai-designate 2017-04-25 21:44:37 +02:00
FlightControl
664ce52ff0 R.2.1 update 2017-04-25 21:44:34 +02:00
Sven Van de Velde
f595fd4736 Merge pull request #473 from FlightControl-Master/386-ai-designate
386 ai designate
2017-04-25 21:30:14 +02:00
FlightControl
1219ee9445 Fixes Task Status Report 2017-04-25 21:28:32 +02:00
FlightControl
b2b1377962 Merge remote-tracking branch 'refs/remotes/origin/master' into 386-ai-designate 2017-04-25 21:28:09 +02:00
Sven Van de Velde
302a69ba48 Merge pull request #470 from FlightControl-Master/Grey-Echo
Remove MDES from repository
2017-04-25 21:14:20 +02:00
Sven Van de Velde
292642c1f5 Merge pull request #471 from FlightControl-Master/386-ai-designate
386 ai designate
2017-04-25 21:14:01 +02:00
FlightControl
ba53713f8d Merge remote-tracking branch 'refs/remotes/origin/master' into 386-ai-designate 2017-04-25 21:13:32 +02:00
FlightControl
2066becb17 Fix for craigowen and fixing issue #424 2017-04-25 21:13:29 +02:00
dwpenney
13bb4345e2 Merge pull request #459 from FlightControl-Master/ForEachStatic
DATABASE:ForEachStatic
2017-04-25 13:13:33 -03:00
Grey-Echo
6cbf6ab158 Remove MDES from repository 2017-04-25 13:50:18 +02:00
FlightControl
d9091392cd Merge remote-tracking branch 'refs/remotes/origin/master' into 386-ai-designate 2017-04-25 13:11:47 +02:00
Sven Van de Velde
417a6b2a06 Merge pull request #469 from FlightControl-Master/386-ai-designate
Publishing new Menu driven reporting structure for TASKING
2017-04-25 12:56:42 +02:00
FlightControl
0456deddd7 Publishing new Menu driven reporting structure for TASKING
-- Each group can now select from a menu to report a task overview or
only the task with a specific state...
-- Dactivate the flashing of the available tasks. A small message is
displayed now.
2017-04-25 12:56:02 +02:00
Sven Van de Velde
b966168be9 Merge pull request #468 from FlightControl-Master/386-ai-designate
386 ai designate
2017-04-25 10:19:42 +02:00
FlightControl
8d6b1940bb Play time
-- Improved menu system. Much shorter Detection menus now.
-- Improved Detection IDs. Each detection item has now an ID.
-- Added coordinate system.
-- Added menu system to manage coordinates. A system settings menu has
been added.
-- Coordinates can now be switched between LL Degrees, LL Decimal and
MGRS
-- COORDINATE class added.
2017-04-25 10:17:10 +02:00
FlightControl
9d68376abb Merge remote-tracking branch 'refs/remotes/origin/master' into 386-ai-designate 2017-04-24 21:57:09 +02:00
FlightControl
f7c08e11f9 Coordinate stuff 2017-04-24 21:56:59 +02:00
Sven Van de Velde
94e092fb2e Merge pull request #466 from FlightControl-Master/386-ai-designate
fix in illumination
2017-04-24 20:29:06 +02:00
FlightControl
b4f4490805 fix in illumination 2017-04-24 20:28:25 +02:00
Sven Van de Velde
b6a48d0201 Merge pull request #465 from FlightControl-Master/386-ai-designate
386 ai designate
2017-04-24 14:01:46 +02:00
FlightControl
6459ee4329 Documentation 2017-04-24 14:01:17 +02:00
FlightControl
2244a2de24 Merge remote-tracking branch 'refs/remotes/origin/master' into 386-ai-designate 2017-04-24 14:01:00 +02:00
Sven Van de Velde
34cca8a4ba Merge pull request #464 from FlightControl-Master/386-ai-designate
386 ai designate
2017-04-24 13:25:23 +02:00
FlightControl
405033ecd0 Merge remote-tracking branch 'refs/remotes/origin/master' into 386-ai-designate 2017-04-24 13:24:50 +02:00
FlightControl
4d0740ca6f Fix MenuAttackGroup bug 2017-04-24 13:24:47 +02:00
Sven Van de Velde
884c8a233f Merge pull request #463 from FlightControl-Master/386-ai-designate
386 ai designate
2017-04-24 13:05:42 +02:00
FlightControl
456636601f Merge remote-tracking branch 'refs/remotes/origin/master' into 386-ai-designate 2017-04-24 13:04:28 +02:00
FlightControl
6b3fe625e5 Important changes
-- Renamed AI_DESIGNATE to DESIGNATE
-- Added a parameter to the New() method: CC.
The declaration prototype is now:
`function DESIGNATE:New( CC, Detection, AttackSet )`

I've added a lot of good stuff in the clas now. Almost ready.
2017-04-24 13:04:25 +02:00
Sven Van de Velde
5cbc737727 Merge pull request #462 from FlightControl-Master/386-ai-designate
386 ai designate
2017-04-24 12:39:04 +02:00
FlightControl
f6b64a8170 Merge remote-tracking branch 'refs/remotes/origin/master' into 386-ai-designate 2017-04-24 12:38:26 +02:00
FlightControl
f410d2ae0b A lot of fixes
See #386
2017-04-24 12:38:24 +02:00
Sven Van de Velde
e1839f4b28 Merge pull request #460 from FlightControl-Master/386-ai-designate
386 ai designate
2017-04-23 21:34:05 +02:00
FlightControl
a8da12c09d Documentation 2017-04-23 21:33:15 +02:00
Fridge
431f810d21 Merge branch 'master' into ForEachStatic 2017-04-23 13:54:57 -03:00
Fridge
9e882104f0 Addition of DATABASE:ForEachStatic 2017-04-23 13:54:23 -03:00
FlightControl
3fb1d75151 Merge remote-tracking branch 'refs/remotes/origin/master' into 386-ai-designate 2017-04-23 17:49:05 +02:00
FlightControl
8761cc754c Changed documentaiton 2017-04-23 17:49:02 +02:00
Sven Van de Velde
b619ef6a3b Merge pull request #458 from FlightControl-Master/386-ai-designate
386 ai designate
2017-04-23 17:10:01 +02:00
FlightControl
e5b83aafe3 Documentation 2017-04-23 17:09:29 +02:00
FlightControl
6c018acc79 Documentation 2017-04-23 17:09:03 +02:00
Sven Van de Velde
9586ab9fc6 Merge pull request #457 from FlightControl-Master/386-ai-designate
386 ai designate
2017-04-23 13:51:05 +02:00
FlightControl
20b4ebfb2b Improvements 2017-04-23 13:49:32 +02:00
FlightControl
8a5a33d191 Progress 2017-04-23 10:04:11 +02:00
FlightControl
1cc89942d1 Progress 2017-04-23 08:49:12 +02:00
FlightControl
cfeec372d7 Merge remote-tracking branch 'refs/remotes/origin/master' into 386-ai-designate 2017-04-23 08:48:40 +02:00
FlightControl
7961ae90f4 Progress 2017-04-23 08:48:30 +02:00
Grey-Echo
45640b5316 Merge pull request #453 from FlightControl-Master/issue437
Add cool features related to Zones in SET_GROUP
2017-04-22 15:22:40 +02:00
Grey-Echo
7bb60f51bb Merge branch 'master' into issue437 2017-04-22 15:21:43 +02:00
Sven Van de Velde
cb50facd9a Merge pull request #454 from FlightControl-Master/386-ai-designate
386 ai designate

-- Working alpha version. It is FANTASTIC!!!!
PLEASE TRY gents, you don't know what you're missing if you don't.

Sven
2017-04-22 15:12:53 +02:00
FlightControl
e80b90fcf6 Progress 2017-04-22 15:11:02 +02:00
FlightControl
456b3f483f Progress 2017-04-22 15:10:31 +02:00
Grey-Echo
f1a9029bc6 Documentation update 2017-04-22 14:56:30 +02:00
Grey-Echo
09325a8615 Correct inprecise documentation 2017-04-22 14:54:13 +02:00
Grey-Echo
de3f8f529f SET_GROUP:CountUnitInZone() 2017-04-22 14:38:00 +02:00
Grey-Echo
d0e138b4c7 Implement GROUP:CountInZone() and SET_GROUP:CountInZone() 2017-04-22 14:35:51 +02:00
Grey-Echo
18756eb61e Implement the new SET_GROUP:AnyPartlyInZone() 2017-04-22 14:18:39 +02:00
FlightControl
036768d400 Progress! 2017-04-22 13:53:54 +02:00
Grey-Echo
fb6bb635c6 Documentation Update 2017-04-22 11:35:14 +02:00
Grey-Echo
28380a5c37 Merge branch 'master' into issue437 2017-04-22 11:33:45 +02:00
Grey-Echo
980053916b Solves a bug in GROUP:IsPartlyInZone()
If only the first UNITs of the GROUP where outside the ZONE, the function would still return false
This behaviour is fixed by this commit.
2017-04-22 11:15:16 +02:00
FlightControl
4138a54e6b Merge remote-tracking branch 'refs/remotes/origin/master' into 386-ai-designate 2017-04-22 07:10:25 +02:00
Grey-Echo
497a2c17d0 Add @usage tags to all newly added methods 2017-04-22 01:22:48 +02:00
Grey-Echo
02bb76792a Improve documentation in newly added methods in SET_GROUP, correct faulty logic in SET_GROUP:AnyPartlyInZone() 2017-04-22 01:16:19 +02:00
Grey-Echo
a058556583 Implement SET_GROUP:NoneInZone() 2017-04-22 01:12:33 +02:00
Grey-Echo
bfda34e94d Implement SET_GROUP:AnyCompletelyInZone
Also renames the 2 previously implemented funcitons
2017-04-22 01:06:35 +02:00
Grey-Echo
56eaa16792 Implement SET_GROUP:IsCompletelyInZone() 2017-04-21 14:16:32 +02:00
Grey-Echo
0b39e1a911 Implement SET_GROUP:HasGroupCompletelyInZone() 2017-04-21 14:07:06 +02:00
Sven Van de Velde
1068393dde Merge pull request #452 from FlightControl-Master/386-ai-designate
386 ai designate
2017-04-21 14:02:16 +02:00
FlightControl
51022be4d4 Working version 2017-04-21 14:01:01 +02:00
FlightControl
a79bc11834 Progress 2017-04-21 06:58:28 +02:00
FlightControl
f4fd7d43f3 Merge remote-tracking branch 'refs/remotes/origin/master' into 386-ai-designate 2017-04-21 05:59:24 +02:00
Sven Van de Velde
e90de251b0 Merge pull request #451 from FlightControl-Master/Grey-Echo
Remove submodule
2017-04-21 05:19:22 +02:00
Grey-Echo
de2f60ad1f Rmove submodule 2017-04-21 00:43:14 +02:00
Sven Van de Velde
a304d53cf0 Merge pull request #450 from FlightControl-Master/386-ai-designate
386 ai designate
2017-04-20 22:00:31 +02:00
FlightControl
dbf95924eb Updated logic... 2017-04-20 21:59:09 +02:00
Sven Van de Velde
c5f64aeafc Merge pull request #449 from FlightControl-Master/386-ai-designate
386 ai designate
2017-04-20 20:55:45 +02:00
FlightControl
7e55dd8a7c Laser fix! 2017-04-20 20:55:11 +02:00
FlightControl
5b901babfa Merge remote-tracking branch 'refs/remotes/origin/master' into 386-ai-designate 2017-04-20 20:54:51 +02:00
FlightControl
8dfd05a453 Merge remote-tracking branch 'refs/remotes/origin/master' into 386-ai-designate 2017-04-20 20:02:37 +02:00
Andrew Finegan
ece2d8a976 Merge pull request #448 from FlightControl-Master/bugfix-447-crash-caused-by-client-destroy
Bugfix 447 crash caused by client destroy
2017-04-20 13:24:02 -04:00
Andrew Finegan
a5e455a00f Revert "Commenting out client:destroy() to prevent server crash"
This reverts commit 8e02e784bc.
2017-04-20 13:08:21 -04:00
Andrew Finegan
96d97a6e51 Prevent Server Crash from Unit Destroy
Destroying a client unit while that client it on a multiplayer server
WILL crash the server. The proper way of handling this is to set a flag
and let MOOSE-SERVER handle the kicking part. This part hasnt been
written yet, but is being worked on
2017-04-20 13:03:42 -04:00
Andrew Finegan
8e02e784bc Commenting out client:destroy() to prevent server crash
Commenting out destroy client unit until MOOSE-SERVER can handle kicking
of people
2017-04-20 12:57:30 -04:00
FlightControl
e53303b71a regenerate documentation 2017-04-20 18:06:27 +02:00
Sven Van de Velde
872c0219ba Merge pull request #446 from FlightControl-Master/Grey-Echo
Solves problem after pull request #444
2017-04-20 17:17:26 +02:00
Grey-Echo
c172a03006 Solves problem after pull request #444 2017-04-20 17:03:46 +02:00
FlightControl
2e573a99e0 Merge remote-tracking branch 'refs/remotes/origin/master' into 386-ai-designate 2017-04-20 14:41:09 +02:00
Grey-Echo
635f9d1bee Merge pull request #444 from FlightControl-Master/beacons
Beacons
2017-04-20 14:39:27 +02:00
Sven Van de Velde
80857244a4 Merge pull request #445 from FlightControl-Master/386-ai-designate
386 ai designate
2017-04-20 14:15:27 +02:00
FlightControl
ac5686b9cc Final 2017-04-20 14:14:54 +02:00
FlightControl
ca3ee12c41 Finalized AI_DESIGNATE 2017-04-20 14:14:39 +02:00
Grey-Echo
ae93ea8b63 Documentation update 2017-04-20 12:53:00 +02:00
Grey-Echo
0d9f78e8bf Merge branch 'master' into beacons 2017-04-20 12:52:09 +02:00
Grey-Echo
23df0aaa68 Added documentation for BEACON 2017-04-20 12:49:18 +02:00
Grey-Echo
ed4b89dcbc Revamp of RADIO's doc 2017-04-20 12:29:58 +02:00
Grey-Echo
65cf152b4f Add loop functionnality to standard radio transmissions 2017-04-20 12:22:26 +02:00
Grey-Echo
e4145dbefd Small tweaks to module doc 2017-04-20 12:15:37 +02:00
Grey-Echo
94ad317e61 Small bufixes to BEACON 2017-04-20 12:10:36 +02:00
FlightControl
e946c6a863 Merge remote-tracking branch 'refs/remotes/origin/master' into 386-ai-designate 2017-04-20 09:52:02 +02:00
Grey-Echo
510ff2f32f Implement BEACON:StopRadioBeacon 2017-04-20 00:19:52 +02:00
Grey-Echo
8c57d86bca Implement BEACON:RadioBeacon() 2017-04-20 00:07:50 +02:00
Grey-Echo
26cf190920 Laying the groundwork for BEACON:RadioBeacon() 2017-04-19 23:49:41 +02:00
Sven Van de Velde
ee4e9621c4 Merge pull request #443 from FlightControl-Master/386-ai-designate
386 ai designate
-- Built a target designate capability based on dynamically detected targets by Recces.
-- Added a AI_DESIGNATE class
-- Added a SPOT class
-- Added Lase functions for POSITIONABLES
-- Added demo mission DES-100

Please check it out!!!!
Sven
2017-04-19 19:43:29 +02:00
FlightControl
cb5510d047 Publish to master 2017-04-19 19:41:26 +02:00
FlightControl
b1c7e04422 Progress 2017-04-19 19:13:23 +02:00
Grey-Echo
adfe811291 Major Changes in BEACON
After tests, the beacon functionnality is even more limited in DCS than what I thought.
Those changes accomode the new data from these tests.
2017-04-19 16:59:05 +02:00
FlightControl
3a3869e095 Lasing is working 2017-04-19 16:53:35 +02:00
Grey-Echo
bbcf6c4717 Implements POSITIONNABLE:GetBeacon() 2017-04-19 15:46:53 +02:00
Grey-Echo
3c80fdafcd Implement BEACON:StopAATACAN() 2017-04-19 15:40:54 +02:00
Grey-Echo
42ec0d6332 Merge branch 'master' into beacons 2017-04-19 15:33:24 +02:00
FlightControl
a7a327285f Merge remote-tracking branch 'refs/remotes/origin/master' into 386-ai-designate 2017-04-19 14:41:50 +02:00
Sven Van de Velde
86715794d7 Merge pull request #441 from FlightControl-Master/master-353-task-cargo-transport
Master 353 task cargo transport
2017-04-19 14:34:14 +02:00
FlightControl
48ea8d33d5 Merge remote-tracking branch 'refs/remotes/origin/master' into master-353-task-cargo-transport 2017-04-18 19:08:47 +02:00
FlightControl
f4a15aa316 Fixed glitch in cargo transfer as a result of the rework 2017-04-18 19:08:44 +02:00
Sven Van de Velde
841e611e3c Merge pull request #439 from FlightControl-Master/master-353-task-cargo-transport
Master 353 task cargo transport
2017-04-18 10:24:43 +02:00
FlightControl
c8b4a9839d Merge remote-tracking branch 'refs/remotes/origin/master' into master-353-task-cargo-transport 2017-04-18 10:24:14 +02:00
FlightControl
f6f91a0f23 Fixed the boarding and unboarding process for CARGO_GROUP 2017-04-18 10:24:12 +02:00
Sven Van de Velde
83dd39d318 Merge pull request #436 from FlightControl-Master/master-353-task-cargo-transport
Master 353 task cargo transport
2017-04-17 18:54:39 +02:00
FlightControl
469e1a3c9b This fixes issue #433 2017-04-17 18:53:32 +02:00
FlightControl
59c89cab39 Merge remote-tracking branch 'refs/remotes/origin/master' into master-353-task-cargo-transport 2017-04-17 18:47:22 +02:00
FlightControl
e57fdc2135 Added google tracking ID 2017-04-17 11:51:03 +02:00
FlightControl
e0829ce986 Fixes Interceptor to Battleplane
SU-25 is  battleplane ... A2G plane with A2A capatilies.
Fixes issue #421
2017-04-17 11:42:10 +02:00
FlightControl
ecde6ad694 Made A2G_TASK_DISPATCHER only detect ground units.
This solves issue #425 ...
2017-04-17 11:34:12 +02:00
FlightControl
040a342e25 Updated documentation in AI_CAP, AI_CAS, AI_BALANCER, AI_PATROL 2017-04-17 11:18:01 +02:00
FlightControl
a89c469130 Updated documentation of SCHEDULER and ZONE and FSM 2017-04-17 07:49:11 +02:00
FlightControl
0e7ebff9a2 MENU documentation update 2017-04-16 17:07:26 +02:00
FlightControl
d8e8a31cf7 Updated MESSAGE documentation 2017-04-16 16:38:47 +02:00
FlightControl
9ccd0cb6dd POINT_VEC3 and POINT_VEC2 documentation update 2017-04-16 16:36:27 +02:00
Sven Van de Velde
465a1fa7e0 Merge pull request #432 from FlightControl-Master/master-353-task-cargo-transport
Master 353 task cargo transport
2017-04-16 08:10:06 +02:00
FlightControl
898976d437 Fixed for cargo hitting client helicopter
When unboarding, the cargo would it the client helicopter on the path,
this is fixed by positioning the cargo at the direction of the target
unboarding point. No helicopter anymore in between.
2017-04-16 08:09:20 +02:00
FlightControl
f1ba010611 Merge remote-tracking branch 'refs/remotes/origin/master' into master-353-task-cargo-transport 2017-04-15 13:43:37 +02:00
Sven Van de Velde
1b15732b69 Merge pull request #430 from FlightControl-Master/master-353-task-cargo-transport
Master 353 task cargo transport
2017-04-15 13:20:24 +02:00
FlightControl
1b7623baa6 Progress 2017-04-15 13:19:15 +02:00
FlightControl
59d9eb127d Progress 2017-04-15 12:13:34 +02:00
FlightControl
db58a1b922 Merge remote-tracking branch 'refs/remotes/origin/master' into master-353-task-cargo-transport 2017-04-14 17:05:08 +02:00
FlightControl
4761e7150a Update web site 2017-04-14 09:49:29 +02:00
FlightControl
b245ed0e1d Update web site 2017-04-14 09:48:00 +02:00
FlightControl
2ab00d22a6 Rename Task_Cargo 2017-04-14 09:43:29 +02:00
FlightControl
329c89f12f Rename Task_Cargo 2017-04-14 09:42:35 +02:00
Sven Van de Velde
978de23c28 Merge pull request #428 from FlightControl-Master/master-353-task-cargo-transport
Master 353 task cargo transport
2017-04-14 09:34:33 +02:00
FlightControl
798996efd2 Documentation etc 2017-04-14 09:31:44 +02:00
FlightControl
9a2b56fb9f Optimized landing, messaging, flow 2017-04-14 08:26:53 +02:00
FlightControl
ff64255ea7 CARGO_GROUP working!!! 2017-04-14 05:43:09 +02:00
FlightControl
0b59fb87f2 Progress 2017-04-13 09:53:27 +02:00
FlightControl
1a5a74120b Progress 2017-04-13 09:42:22 +02:00
FlightControl
c20f13f0f8 Progress 2017-04-13 08:45:28 +02:00
FlightControl
92c5e0c592 Documentation update 2017-04-12 18:02:17 +02:00
Sven Van de Velde
ccdbed3159 Merge pull request #420 from FlightControl-Master/enhancement-353-task-cargo-transport
Enhancement 353 task cargo transport
2017-04-12 17:35:22 +02:00
Sven Van de Velde
d62cd7562b Merge branch 'master-353-task-cargo-transport' into enhancement-353-task-cargo-transport 2017-04-12 17:35:10 +02:00
FlightControl
c9602ab292 Documentation Update 2017-04-12 16:09:59 +02:00
FlightControl
853f780015 Fixes issue #398
-- I think this should fix it!
2017-04-12 15:15:37 +02:00
FlightControl
3d6e24b806 SPAWN fix issue #410 documentation update 2017-04-12 14:57:55 +02:00
FlightControl
989df023fb Fixes issue #410
-- SpawnScheduleStart and SpawnScheduleStop now return self
-- Proper documentation
-- SPA-023 test mission added.
2017-04-12 14:57:12 +02:00
FlightControl
58935ec1e2 Updated SPAWNSTATIC documentation 2017-04-12 14:45:54 +02:00
Sven Van de Velde
ed0a86647b Merge pull request #416 from FlightControl-Master/master-405-event-handling
Master 405 event handling

I have reworked event handling:
-- Avoid events not being handled whey they should.
-- Clean up the subscriptions when Groups or Units are dead.
-- Reinitiate the subscriptions when Groups or Units are respawned.
-- EVENT_HIT is only for Targets when the subscription is on UNIT or GROUP level.
-- MISSION_END should work now too ...
-- When a subscribed object is nillified, and the collectgarbage() is executed, it should clean the subscription.
-- Reworked and cleaned the event handling...
-- Cleaned up the code
2017-04-12 09:28:48 +02:00
FlightControl
573f444254 New dynamic loader 2017-04-12 09:27:52 +02:00
FlightControl
3bf9eab704 Merge remote-tracking branch 'refs/remotes/origin/master' into master-405-event-handling
# Conflicts:
#	Moose Mission Setup/Moose.lua
2017-04-12 09:26:27 +02:00
FlightControl
545034034e Fixes issue #412
-- wrong usage of IsCompletelyInZone() on UNIT level fixed -> Changed to
IsInZone()
2017-04-12 09:16:56 +02:00
Sven Van de Velde
e6b9da2fcb Merge pull request #418 from FlightControl-Master/master-415
Implemented :FilterCategories() method for DETECTION_ classes fixing issue #415
2017-04-12 09:01:16 +02:00
FlightControl
d7f1a74caf Implemented :FilterCategories() method for DETECTION_ classes fixing issue #415
--  Added:FilterCategories() method to DETECTION_BASE.
-- Reviewed documentation
-- Added documentation for FilterCategories method
-- Default detection methods are all ON. (They were only set to visual).
-- Created test missions. DET-30x
2017-04-12 09:00:00 +02:00
FlightControl
86fd99f356 Fixes #414 2017-04-12 07:18:26 +02:00
FlightControl
306ac64bd3 Updated SPAWN
-- Revised documentation
-- Initial delay OFF by default for SpawnScheduled() spawning. Use the
InitDelayOn() to activate the delays.
2017-04-11 20:35:46 +02:00
Sven Van de Velde
b1ff20f218 Merge pull request #417 from FlightControl-Master/master-enh-327-spawnstatic
Master enh 327 spawnstatic

SPAWNSTATIC is ADDED....

Try the new mission SPS-100 in the MOOSE_MISSIONS repository. Open the SPS-100 - Simple Spawning.lua and check the code (I listed it here):

local ZonePosition = ZONE:New( "Position" )

local SpawnBuilding = SPAWNSTATIC:NewFromStatic( "Building", country.id.GERMANY )
local SpawnBarrack = SPAWNSTATIC:NewFromStatic( "Barrack", country.id.GERMANY )

local ZonePointVec2 = ZonePosition:GetPointVec2()

local Building = SpawnBuilding:SpawnFromZone( ZonePosition, 0 )

for Heading = 0, 360,60 do
  local Radial = Heading * ( math.pi*2 ) / 360
  local x = ZonePointVec2:GetLat() + math.cos( Radial ) * 150
  local y = ZonePointVec2:GetLon() + math.sin( Radial ) * 150
  SpawnBarrack:SpawnFromPointVec2( POINT_VEC2:New( x, y ), Heading + 90 )
end

What this code does is:

    It identifies the Zone "Position" where to do the spawning.
    It creates two SPAWNSTATIC objects, one for a building and one for barracks.
    A building is created in the center of the Zone.
    Barracks are created around the center of the Zone.

A couple of points I would like to ask you to consider:

    For simplicity, I think it is the best to have one SPAWNSTATIC object per Static you want to spawn. => Is this ok?
    The only SPAWNSTATIC constructor working at this moment is NewFromStatic. The other planned constructor is NewFromType, but this will need more work to make.
    Two Spawn methods are created: SpawnFromPointVec2() and SpawnFromZone(). => Is this sufficient???
    There won't be any Init methods... => Is this ok???
    There won't be any Respawn methods... => Is this ok???
    What other Spawn APIs would you like to see... => Please think of the process or things you want to do with Statics....
    The naming of the created statics is the TemplateName#nnnnn, where nnnnn is the index of the static spawned. In the Spawn methods, you can give optionally a new name of the Static, overriding the template name.
    There is a question on Country. Each Static has a country. I can set it for default at the constructor, and optionally, if you really want that, I can allow to give an override CountryID parameter when Spawning (not my preference though, because then you'll get two optional parameters in the Spawn methods, one for the new name, and one for the country => confusing ).
    I see Pikey is looking for an additional check of the ground. To be honest, not so easy to implement and also, what is the value if the MD defines a wrong position???

pls test and consider the questions...
2017-04-11 09:05:54 +02:00
FlightControl
e65bfd28f3 Merge remote-tracking branch 'refs/remotes/origin/master' into master-enh-327-spawnstatic 2017-04-11 08:59:53 +02:00
FlightControl
b21bbb7cbd Reducing trace with one line 2017-04-11 08:52:24 +02:00
FlightControl
96de81fef3 Fixed Dead event and Crash event glitch
When a Dead or Crash event happens on UNIT or GROUP level, the
OnEventFunction is also called now!!!
2017-04-11 08:43:03 +02:00
FlightControl
8629b1c36f Merge remote-tracking branch 'refs/remotes/origin/master' into master-405-event-handling 2017-04-11 08:31:12 +02:00
FlightControl
af3558e2c0 Fixed Update All Test Missions .launch file 2017-04-11 08:29:24 +02:00
FlightControl
12948f583d Added Reset of event subscriptions after GROUP:Respawn() 2017-04-11 08:20:54 +02:00
FlightControl
51c1da3557 Added Event Reset for SPAWN, GROUP and UNIT on SPAWN:ReSpawn() 2017-04-11 08:19:06 +02:00
FlightControl
321a33f0f6 Fixed error with EventMeta 2017-04-11 07:02:01 +02:00
FlightControl
c8cf0e2cc5 Updated improved version ...
-- Fixed error in HIT processing. HIT events are only for the Target,
when set on UNIT or GROUP level!
2017-04-11 07:01:44 +02:00
FlightControl
785a297a69 Progress version 2 -- better version ... 2017-04-10 16:45:37 +02:00
FlightControl
553435bbcb Event fix, version 1 pre-alpha test version... 2017-04-10 16:14:27 +02:00
FlightControl
2b622c0a02 Progress 2017-04-09 07:53:34 +02:00
FlightControl
8440cb01ab First Version 2017-04-08 14:05:28 +02:00
Sven Van de Velde
73d1b3b439 Merge pull request #411 from FlightControl-Master/master-enh-125-spawn-schedule-delay
SPAWN Delay implementation

-- New methods SPAWN:InitDelayOnOff(), SPAWN:InitDelayOn(), SPAWN:InitDelayOff()
-- Default is delay is on, but only for :SpawnScheduled()
-- Did a large review of the SPAWN documentation (for dummies).
-- Moved the SPAWN class documentation to the SPAWN object, so that it becomes visible in intellisense.
-- Added Demo Mission links
-- Added Youtube channel links
2017-04-08 08:07:41 +02:00
FlightControl
9e2c66dae4 SPAWN Delay implementation
-- New methods SPAWN:InitDelayOnOff(), SPAWN:InitDelayOn(),
SPAWN:InitDelayOff()
-- Default is delay is on, but only for :SpawnScheduled()
-- Did a large review of the SPAWN documentation (for dummies).
-- Moved the SPAWN class documentation to the SPAWN object, so that it
becomes visible in intellisense.
-- Added Demo Mission links
-- Added Youtube channel links
2017-04-08 08:06:29 +02:00
Grey-Echo
fb4fc4add1 Merge pull request #401 from FlightControl-Master/Grey-Echo
Changes to the documentation generation
2017-04-05 14:27:21 +02:00
Grey-Echo
d5adf0a282 Resolve problems with Zone.lua
also generate Dynamic Loader (forgot to generate it after generating static for tests)
2017-04-05 09:35:06 +02:00
Grey-Echo
3ca84adb53 Merge branch 'master' into Grey-Echo 2017-04-05 01:31:01 +02:00
Grey-Echo
3b69cf992e This is an important refactor of the way documentation generation works
* Installs luarocks WITH it's executable (easy to install other rocks if necessary)
* Use Lua supplied with luarocks
* Create Utils/luadocumentor.bat, which works with RELATIVE PATH ! -> Everybody can generate the doc
* Updated launch files accordingly
2017-04-05 01:26:39 +02:00
Sven Van de Velde
0de157ebe1 Merge pull request #391 from FlightControl-Master/readme
Changed wrong link to Moose_Missions
2017-04-04 21:09:28 +02:00
Grey-Echo
35e88c27c5 Changed wrong link to Moose_Missions 2017-04-03 15:32:42 +02:00
Sven Van de Velde
c2517d399c Merge pull request #389 from FlightControl-Master/Usage-guide
Correct the link for test missions
2017-04-02 22:35:43 +02:00
Grey-Echo
0ef9a26222 Start of BEACON:StopAATACAN() 2017-04-02 22:12:34 +02:00
Grey-Echo
7263bdeaf0 Correct the link for test missions 2017-04-02 22:08:15 +02:00
Grey-Echo
411982d557 Implement AATACAN() 2017-04-02 19:21:15 +02:00
Grey-Echo
a7e3f1a961 Merge branch 'master' into beacons 2017-04-02 18:50:59 +02:00
Sven Van de Velde
43624e4067 Merge pull request #388 from FlightControl-Master/FlightControl-task-cargo-transport
Flightcontrol task cargo transport
2017-04-02 15:33:12 +02:00
FlightControl
118afde739 Merge remote-tracking branch 'refs/remotes/origin/enhancement-353-task-cargo-transport' into FlightControl-task-cargo-transport
# Conflicts:
#	Moose Mission Setup/Moose.lua
2017-04-02 15:32:09 +02:00
Grey-Echo
513a103947 Merge pull request #387 from FlightControl-Master/Grey-Echo
* Add a Utils folder with 7-Zip and lua
*  Changes Moose \Mission Setup\Moose Mission Update\Moose_Update_Missions.bat to use 7-Zip in Utils
* Removes 7-Zip in \Mission Setup\Moose Mission Update\
* Changes every .lauch file to use \Utils\lua\5.1\bin\lua.exe instead of PATH

Known Issue : Moose Development\LDT External Tools\Moose DOCUMENTATION Generate.launch does NOT work for contrbutors other than @FlightControl-Master , need to update \Utils\lua\5.1\bin\luadocumentor.bat with new paths
2017-04-02 14:47:16 +02:00
Grey-Echo
6d90661738 Merge branch 'master' into Grey-Echo 2017-04-02 12:46:41 +02:00
Grey-Echo
1a863261a8 Updates to lauch files, add Utils with 7-Zip and lua
Known bug : .lauch file Generate doc doesn't work, it needs an update to luadocumentor.bat
2017-04-02 12:37:25 +02:00
FlightControl
17a332cf16 Update .launch files 2017-04-02 11:48:17 +02:00
FlightControl
7e9b97dda0 Documentation 2017-04-02 09:39:00 +02:00
FlightControl
f8f68ea695 Progress 2017-04-02 09:16:34 +02:00
FlightControl
2e2aabdcc9 Progress 2017-04-02 08:04:43 +02:00
FlightControl
bad2e81c56 Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl-task-cargo-transport 2017-04-02 05:53:52 +02:00
Grey-Echo
b846757f4d Changes to BEACON 2017-04-01 22:45:52 +02:00
FlightControl
6c4c149349 Fix 368 2017-04-01 20:58:06 +02:00
Grey-Echo
f9afe8c937 Add a start for BEACON:AATACAN() 2017-04-01 19:33:42 +02:00
Grey-Echo
c752264325 Implement constructor an TACANToFrequecy to BEACON 2017-04-01 15:18:41 +02:00
FlightControl
293267f760 Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl-task-cargo-transport
# Conflicts:
#	Moose Development/Moose/Moose.lua
#	Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua
#	Moose Mission Setup/Moose.lua
#	Moose Test Missions/TAD - Task Dispatching/TAD-100 - A2G Task
Dispatching DETECTION_AREAS/TAD-100 - A2G Task Dispatching
DETECTION_AREAS.lua
2017-04-01 15:02:51 +02:00
Sven Van de Velde
72603601b1 Merge pull request #385 from FlightControl-Master/master-release-prep
Release 2.0!!!! Note that this is the DYNAMIC loading Moose.lua that is published here!!!!
2017-04-01 11:08:02 +02:00
FlightControl
6c112193ed Ensure dynamic loading is enabled... 2017-04-01 11:03:44 +02:00
FlightControl
c5107f1b8a Merge remote-tracking branch 'refs/remotes/origin/master' into master-release-prep 2017-04-01 09:01:00 +02:00
Sven Van de Velde
bd0c78492a Set theme jekyll-theme-architect 2017-04-01 08:52:49 +02:00
Sven Van de Velde
c910a7a29b Set theme jekyll-theme-merlot 2017-04-01 08:51:11 +02:00
Sven Van de Velde
9085838189 Set theme jekyll-theme-architect 2017-04-01 08:41:27 +02:00
Sven Van de Velde
1b85fbfab3 Merge pull request #384 from FlightControl-Master/Tweaks-to-Readme
Tweaks to readme
2017-04-01 08:17:03 +02:00
Grey-Echo
a49c5b2dea Hope this works 2017-03-31 22:52:14 +02:00
Grey-Echo
756e5170a8 Merge pull request #383 from FlightControl-Master/usage_guide
Small tweaks to Usage_Guide.md
2017-03-31 21:20:23 +02:00
Grey-Echo
6de01e3303 Small tweaks to Usage_Guide.md 2017-03-31 17:45:43 +02:00
Grey-Echo
666e0b115b Small tweaks to README.md 2017-03-31 17:21:54 +02:00
FlightControl
64d2a49cc6 Merge remote-tracking branch 'refs/remotes/origin/master' into master-release-prep 2017-03-31 14:31:22 +02:00
FlightControl
0c7622969d Fixes in documentation 2017-03-31 14:31:00 +02:00
Sven Van de Velde
3405b3f203 Merge pull request #380 from FlightControl-Master/Contribution_Guide.md-Tweaks
Small tweaks to Contribution_Guide.md
2017-03-31 13:06:10 +02:00
FlightControl
2c5cc66826 Fixed a bug testing for partially in zone ... 2017-03-31 12:59:37 +02:00
Grey-Echo
94546fc2a5 Small tweaks to Contribution_Guide.md
Should be pretty final.
2017-03-31 12:46:34 +02:00
FlightControl
f68fec303a Fixing #281
I think this one liner fixes the problem...  The menu got removed. And
it shouldn't. It can stay ...
2017-03-31 11:48:47 +02:00
FlightControl
4dbab26b5f Merge remote-tracking branch 'refs/remotes/origin/master' into master-release-prep 2017-03-31 11:48:42 +02:00
Sven Van de Velde
5c51484e81 Merge pull request #379 from FlightControl-Master/Grey-Echo
Finished Contribution_Guide.md
2017-03-31 11:29:47 +02:00
FlightControl
24746644d3 Updated launch files 2017-03-31 11:15:18 +02:00
Grey-Echo
441a771c8a Finish Contribution_Guide.md 2017-03-31 11:08:14 +02:00
FlightControl
d3d113e2ba Merge remote-tracking branch 'refs/remotes/origin/master' into master-release-prep 2017-03-31 10:34:32 +02:00
FlightControl
a988a8da24 docs 2017-03-31 10:17:15 +02:00
FlightControl
29c6773da6 Readme 2017-03-31 10:14:39 +02:00
FlightControl
86cf86ef41 Updated documentation 2017-03-31 10:12:12 +02:00
Grey-Echo
5f87d9eeb6 Contribution_Guide is almost finished
Parts left to do are 1 and 6
2017-03-30 23:33:11 +02:00
FlightControl
51b28f990c Documentation update 2017-03-30 18:11:37 +02:00
Sven Van de Velde
614581222d Merge pull request #375 from FlightControl-Master/DocWork1
Small tweaks to Beta_Test_Guide.md
2017-03-30 13:09:08 +02:00
Grey-Echo
7e4ef10489 Small tweaks to Beta_Test_Guide.md
Turns out i need to mull request this !
2017-03-30 13:00:42 +02:00
Sven Van de Velde
4b56272b07 Merge pull request #374 from FlightControl-Master/Grey-Echo
Doc work on Beta_Test_Guide.md
2017-03-30 12:06:05 +02:00
FlightControl
47efa9b459 Demonstration missions added 2017-03-30 12:02:19 +02:00
FlightControl
edd4b66fa9 demonstration missions added. 2017-03-30 12:00:49 +02:00
Grey-Echo
263dc9b946 add a picture for MDES in Beta_Test_Guide.md 2017-03-30 11:58:32 +02:00
Sven Van de Velde
63f9cd9956 Merge pull request #373 from FlightControl-Master/FlightControl-Release-Prep
Flightcontrol release prep
2017-03-30 11:55:54 +02:00
FlightControl
042cbb4f24 Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl-Release-Prep 2017-03-30 11:54:30 +02:00
Grey-Echo
193ee81be6 Update Beta_Test_Guide.md. Almost finished ! 2017-03-30 11:48:38 +02:00
FlightControl
c1d15e2c55 Main site, deleted intro part, because it is repeated below. 2017-03-30 11:39:44 +02:00
FlightControl
6364ed55ed Readme for github. 2017-03-30 11:36:55 +02:00
FlightControl
5b6421e998 Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl-Release-Prep 2017-03-30 11:34:35 +02:00
Grey-Echo
cc99518f14 Merge branch 'master' into Grey-Echo 2017-03-30 09:41:26 +02:00
Grey-Echo
7f1ef89c98 Merge pull request #370 from FlightControl-Master/MDES
Moose Development Environment Setup (MDES)
2017-03-30 09:31:31 +02:00
FlightControl
6234588a1b Removed training mission 2017-03-30 07:53:18 +02:00
FlightControl
2d5970e75e Removed Test Missions from main repository 2017-03-30 07:51:16 +02:00
FlightControl
f692b2bc90 Removed training files 2017-03-30 06:40:01 +02:00
Grey-Echo
c31501bb55 Corrects poor wording and obvious mistakes in Beta_Test_Guide.md 2017-03-30 01:02:36 +02:00
Grey-Echo
f1553cc896 Work on the Beta_Test_Guide.md doc file. Around 2/3 done 2017-03-30 00:54:14 +02:00
Grey-Echo
00a60a6e56 Add the precompiled Lua 5.1, in order to be used by the install script.
Note tha LF had to be converted to CRLF. Doesn't seem to ba an issue though.
2017-03-29 23:03:03 +02:00
Grey-Echo
95ddd14ad6 Add script and executable fot the setup program 2017-03-29 22:56:02 +02:00
FlightControl
d5e1643a6c Removed logos and presentations 2017-03-29 19:30:45 +02:00
FlightControl
f21d498d99 Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl-Release-Prep 2017-03-29 18:04:20 +02:00
FlightControl
f1fc6511ca Updates 2017-03-29 18:01:37 +02:00
FlightControl
cdd66aa494 Manuals 2017-03-29 17:36:14 +02:00
FlightControl
2637b992b8 Cleanup of some files
-- Deleted Copy of Moose_Create.bat. Not needed anymore.
-- Deleted Moose.lua. Not needed as it will be generated upon release.
2017-03-29 09:52:36 +02:00
FlightControl
581414b259 Release automation preparation
-- Updated .launch files to work from workspace. So everybody can use
them from LDT.
-- Deleted obscolete .launch files.
-- Created new Moose_Create.lua script. Now lua makes the Dynamic and
Static Moose.lua stub.
-- One single file is the reference for the Moose sources: Moose.files
located in Moose Setup
-- Removed the l10n directory from Moose Setup\Moose Mission Update.
-- Removed the 7z and other outputs to update missions.
2017-03-29 09:50:39 +02:00
FlightControl
9cb661cf29 Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl-Release-Prep 2017-03-29 06:17:04 +02:00
Sven Van de Velde
d937d6668e Merge pull request #366 from FlightControl-Master/master-bugfix-#349
Fixed #349
2017-03-28 13:17:32 +02:00
FlightControl
f1d206d4ea Fixed #349
-- problem was with ipairs, should have been pairs... changed it.
2017-03-28 13:17:09 +02:00
FlightControl
a00311ed13 Progress on Transport
-- Created SET_CARGO
-- DATABASE handles CARGO
-- Event handles CARGO
-- Event triggers CARGO events
-- Menu system to pickup and deploy cargo
-- Menu system to board and unboard cargo
2017-03-28 12:20:15 +02:00
FlightControl
f74c660bf8 Merge remote-tracking branch 'refs/remotes/origin/FlightControl' into FlightControl-task-cargo-transport
# Conflicts:
#	Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua
#	Moose Mission Setup/Moose.lua
#	Moose Test Missions/TSK - Task Modelling/TSK-020 - Task Modelling -
Pickup/TSK-020 - Task Modelling - Pickup.miz
2017-03-28 06:34:03 +02:00
FlightControl
30734bf64f Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl 2017-03-28 06:16:55 +02:00
Grey-Echo
0753e48bee Merge pull request #364 from FlightControl-Master/Grey-Echo
Grey echo
2017-03-27 12:26:56 +02:00
Grey-Echo
6101965956 Generate Moose Static 2017-03-27 12:19:26 +02:00
Grey-Echo
0dffc350a1 Merge branch 'master' into Grey-Echo 2017-03-27 12:18:49 +02:00
Grey-Echo
cb8d75f044 Static Moose.lua Generation 2017-03-27 12:11:02 +02:00
Grey-Echo
080cadb8e0 Add a check for the Radius parameter in POSITIONABLE:GetRandomVec3(Radius) 2017-03-27 12:03:51 +02:00
Grey-Echo
1e60111ce5 Add polymorphic methods GROUP:GetPointVec2(), GROUP:GetRandomVec3(Radius), GROUP:GetHeading()
Also fix a Luadoc documentation issues in Wrapper.Group and Wrapper.Positionable
2017-03-27 11:48:54 +02:00
Sven Van de Velde
80a5b54980 Merge pull request #363 from FlightControl-Master/master-bugfix--#97
Fix of bug #97

**1) Fixed GROUP:IsAlive()**
Returns if the Group is alive.
The Group must:

    * Exist at run-time.
    * Has at least one unit.

When the first @{Unit} of the Group is active, it will return true.
If the first @{Unit} of the Group is inactive, it will return false.
@param #GROUP self
@return #boolean true if the Group is alive and active.
@return #boolean false if the Group is alive but inactive.
@return #nil if the group does not exist anymore.

**2) Fixed IDENTIFIABLEIsAlive()**
Returns if the Identifiable is alive.
If the Identifiable is not alive, nil is returned.
If the Identifiable is alive, true is returned.
@param #IDENTIFIABLE self
@return #boolean true if Identifiable is alive.
@return #nil if the Identifiable is not existing or is not alive.

**3) Fixed UNIT:IsAlive()**
Returns if the Unit is alive.
If the Unit is not alive, nil is returned.
If the Unit is alive and active, true is returned.
If the Unit is alive but not active, false is returned.
@param #UNIT self
@return #boolean true if Unit is alive and active.
@return #boolean false if Unit is alive but not active.
@return #nil if the Unit is not existing or is not alive.

**4) Updated all test missions, as this is a core change.**
2017-03-27 10:39:17 +02:00
FlightControl
0ebcbb4879 Fix of bug #97
-- Fixed GROUP:IsAlive()
--- Returns if the Group is alive.
-- The Group must:
--
--   * Exist at run-time.
--   * Has at least one unit.
--
-- When the first @{Unit} of the Group is active, it will return true.
-- If the first @{Unit} of the Group is inactive, it will return false.
--
-- @param #GROUP self
-- @return #boolean true if the Group is alive and active.
-- @return #boolean false if the Group is alive but inactive.
-- @return #nil if the group does not exist anymore.

-- Fixed Identifiable:IsAlive()
--- Returns if the Identifiable is alive.
-- If the Identifiable is not alive, nil is returned.
-- If the Identifiable is alive, true is returned.
-- @param #IDENTIFIABLE self
-- @return #boolean true if Identifiable is alive.
-- @return #nil if the Identifiable is not existing or is not alive.

-- Fixed UNIT:IsAlive()
--- Returns if the Unit is alive.
-- If the Unit is not alive, nil is returned.
-- If the Unit is alive and active, true is returned.
-- If the Unit is alive but not active, false is returned.
-- @param #UNIT self
-- @return #boolean true if Unit is alive and active.
-- @return #boolean false if Unit is alive but not active.
-- @return #nil if the Unit is not existing or is not alive.

-- Updated all test missions, as this is a core change.
2017-03-27 10:34:51 +02:00
FlightControl
d9c4716127 Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl-task-cargo-transport
# Conflicts:
#	Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua
#	Moose Mission Setup/Moose.lua
2017-03-27 09:47:51 +02:00
Sven Van de Velde
3a453ca7d9 Merge pull request #352 from FlightControl-Master/master-bugfix-337
Master bugfix #337
2017-03-26 08:54:33 +02:00
FlightControl
6ce32af1ce Update 2017-03-26 08:53:56 +02:00
FlightControl
ff69012c8d Working 2017-03-26 08:47:50 +02:00
FlightControl
96546e21f5 Progress 2017-03-25 22:22:06 +01:00
Sven Van de Velde
d4ab9e3e8a Merge pull request #351 from FlightControl-Master/master-bugfix-330
Master bugfix #330 .
Please test.
-- Created test mission GRP-310 that simulates a ground group stopping to move and continuing to move.
2017-03-25 20:58:02 +01:00
FlightControl
82ae6011f0 Bug #330 fix 2017-03-25 20:57:01 +01:00
FlightControl
d1a7e5864d Progress 2017-03-25 11:59:20 +01:00
FlightControl
09c05057ae Progress 2017-03-25 07:56:37 +01:00
FlightControl
14ca38bc8b Progress 2017-03-25 07:31:47 +01:00
FlightControl
0539ae3b2f Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl 2017-03-25 06:42:18 +01:00
Sven Van de Velde
2be25298b8 Merge pull request #346 from FlightControl-Master/FlightControl
Bugfix in DETECTION_AREAS
2017-03-25 06:30:34 +01:00
FlightControl
1bb40824a2 Fix in detection of crash. 2017-03-25 06:23:15 +01:00
FlightControl
3e8824b89b Updated documentation 2017-03-24 14:47:51 +01:00
FlightControl
de87e1f557 Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl 2017-03-24 06:34:56 +01:00
Sven Van de Velde
8f645617e4 Merge pull request #343 from FlightControl-Master/FlightControl
retry for easy
2017-03-23 21:04:41 +01:00
FlightControl
12b2974b19 retry 2017-03-23 21:04:10 +01:00
Sven Van de Velde
86669c9ed8 Merge pull request #342 from FlightControl-Master/FlightControl
WeaponType for CONTROLLABLE:TaskAttackUnit
2017-03-23 20:45:45 +01:00
FlightControl
7f43e958df WeaponType added in AttackUnit
--- WeaponType is added
2017-03-23 20:44:56 +01:00
FlightControl
6f1070cb72 Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl 2017-03-23 20:14:06 +01:00
Sven Van de Velde
fad7291450 Merge pull request #341 from FlightControl-Master/FlightControl
SPAWN fixes
2017-03-23 19:56:31 +01:00
FlightControl
31cae70100 mission script fix 2017-03-23 19:55:01 +01:00
FlightControl
5216a31b47 fix in script 2017-03-23 19:34:44 +01:00
FlightControl
b7166a1295 Testing 2017-03-23 19:15:37 +01:00
FlightControl
f75f9512a2 Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl
# Conflicts:
#	Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua
#	Moose Mission Setup/Moose.lua
#	Moose Test Missions/RAD - Radio/RAD-002 - Transmission Tips and
Tricks/RAD-002 - Transmission Tips and Tricks.miz
2017-03-23 18:23:08 +01:00
FlightControl
69c88bc2a6 Working missions 2017-03-23 13:36:03 +01:00
FlightControl
aa806e19e1 Fixed spawn problems, i think ... 2017-03-23 12:57:46 +01:00
Grey-Echo
0c7358c718 Merge pull request #340 from FlightControl-Master/Grey-Echo
Small Tweaks to RADIO
2017-03-23 09:06:38 +01:00
FlightControl
f91d8fd07d Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl
# Conflicts:
#	Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua
#	Moose Mission Setup/Moose.lua
2017-03-23 05:35:12 +01:00
FlightControl
c4ba4760fc static moose.lua 2017-03-23 05:29:44 +01:00
FlightControl
63973e4e2d Merge remote-tracking branch 'refs/remotes/origin/master' into Grey-Echo
# Conflicts:
#	Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua
#	Moose Mission Setup/Moose.lua
2017-03-23 05:28:09 +01:00
Grey-Echo
24d7ed16b3 Generated Static Moose.lua (for real this time !) 2017-03-22 21:50:02 +01:00
Grey-Echo
f42a02e1f4 Generate Static Moose.lua 2017-03-22 21:41:08 +01:00
Grey-Echo
cd267b1ed5 Cleaned conditions with nil in RADIO 2017-03-22 21:28:57 +01:00
FlightControl
22562802cb Moose static 2017-03-22 18:12:25 +01:00
FlightControl
ef67ac7e8f Progress 2017-03-22 18:11:54 +01:00
Grey-Echo
ea3ca25c23 Update RAD-002 to use SCHEDULER properly 2017-03-22 18:05:38 +01:00
Grey-Echo
b1b0789113 Doc update for RADIO:StopBroadcast Fix 2017-03-22 10:40:22 +01:00
Grey-Echo
4563e7e300 in RADIO, use the MOOSE way to setCommand()
self.Positionable:GetDCSObject():getController():setCommand()  =>  self.Positionable:SetCommand()
2017-03-22 10:22:04 +01:00
Grey-Echo
24a166826a Add RADIO:StopBroadcast() 2017-03-22 10:17:53 +01:00
Grey-Echo
79518ed926 Merge pull request #332 from FlightControl-Master/RadioComs
Radio coms
2017-03-21 20:51:25 +01:00
Sven Van de Velde
871945a06b Merge pull request #333 from FlightControl-Master/master-scoring-fixes
-- Fixed ThreatLevel call resulting in a crash during S_EVENT_CRASH or S_EVENT_DEAD. Threatlevels are determined for Player and Targets during hit events and cached.
-- Fixed issue were a player1 hiting player2 would keep granting score for that player1. Now, when the player2 is dead, the hit timestamp is reset for player1. So when player 3 causes a dead event for player2, then only player3 will receive a score and not player1 also.
-- Scenery objects and static objects are now also taken into account for the scoring.
-- When a scenery object is hit, a message is displayed.
-- Only when scenery objects are set as a goal, the S_EVENT_DEAD will result in a message to the player and scores.
-- The player will now receive scores when eliminating a static target in multi-player mode!

That's it.
FC
2017-03-21 12:19:39 +01:00
FlightControl
b1a049f193 Final fixes 2017-03-21 12:13:56 +01:00
FlightControl
f1cdc23cf5 MP test 2017-03-21 11:58:00 +01:00
FlightControl
f2e28cb62d MP test 2017-03-21 11:48:29 +01:00
FlightControl
d1735689ca MP test 2017-03-21 11:41:44 +01:00
FlightControl
6183a9acab MP test 2017-03-21 11:36:13 +01:00
FlightControl
ffe04ff657 MP test 2017-03-21 11:25:33 +01:00
FlightControl
cb886229da MP test 2017-03-21 11:08:04 +01:00
FlightControl
5f70c62c80 MP Test 2017-03-21 11:06:24 +01:00
FlightControl
266a27d127 MP test 2017-03-21 10:31:06 +01:00
FlightControl
91cc18b467 MP test 2017-03-21 10:27:43 +01:00
FlightControl
ffeea0ee8b MP test 2017-03-21 10:12:58 +01:00
FlightControl
576394f18c MP test 2017-03-21 09:56:20 +01:00
FlightControl
f0c7c2026f test 2017-03-21 09:37:25 +01:00
FlightControl
384238fd36 Master Scoring Fixes 2017-03-21 09:34:49 +01:00
Grey-Echo
af6f942218 Merge branch 'master' into RadioComs 2017-03-20 22:23:03 +01:00
Grey-Echo
340c6d0820 Remove variable parameters in shortcuts functions in RADIO
Plus some other small tweaks
2017-03-20 21:38:33 +01:00
FlightControl
baa17a4cb7 @Grey-Echo I've done a review and created this branch.
I've done some minor changes, but there is one item that i think
requires your attention, and that is on line 82. It writes
*Positionable,*... Shouldn't that be some kind of a value there?

I am really impressed with the work you did. Excellent job!
2017-03-20 20:33:26 +01:00
FlightControl
c0dcc86405 Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl 2017-03-20 20:13:26 +01:00
Sven Van de Velde
8ab75fb66d Merge pull request #331 from FlightControl-Master/FlightControl
documentation
2017-03-20 20:11:20 +01:00
FlightControl
716ffa8b0f documentation 2017-03-20 20:10:00 +01:00
FlightControl
13159f3ed3 Update detection documentation. 2017-03-20 20:07:51 +01:00
FlightControl
6d41c30831 Detection presentation upates. 2017-03-20 15:42:30 +01:00
FlightControl
1ae0d10009 Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl 2017-03-20 15:42:11 +01:00
Grey-Echo
55cbd24588 LDoc final pass and HTML doc generation 2017-03-20 13:51:00 +01:00
Sven Van de Velde
761f1ded53 Merge pull request #328 from FlightControl-Master/FlightControl
Task dispatcher fine tunings.
2017-03-20 13:34:01 +01:00
FlightControl
1bd37b8eed Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl 2017-03-20 13:33:06 +01:00
FlightControl
664580e3f1 Updates after the presentation. 2017-03-20 13:33:02 +01:00
Sven Van de Velde
e50e08dc38 Merge pull request #325 from FlightControl-Master/FlightControl
Fixes for tasking
2017-03-20 12:54:30 +01:00
FlightControl
32c5227d71 Updated stuff in tasking
-- SET improved
-- Resolved bug with destroy scoring not granted.
-- Implemented LL for all detection reports
-- Testing
-- Added test mission TAD-220 for DETECTION_TYPES testing.
2017-03-20 12:53:36 +01:00
FlightControl
1dd8f523a1 Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl 2017-03-20 09:36:53 +01:00
Grey-Echo
817274aa01 Add details in RAD-002 and small corrections to LDoc 2017-03-19 21:56:19 +01:00
Grey-Echo
4c7e839ef8 Add Test missions for RADIO
RAD-000 / RAD-001 / RAD-002
2017-03-19 18:06:14 +01:00
Sven Van de Velde
0f03dc2162 Merge pull request #324 from FlightControl-Master/FlightControl
Fixed accounting of targets in the new tasking system
2017-03-19 15:05:27 +01:00
FlightControl
ae4052ba2d Account for destroy events. 2017-03-19 15:03:07 +01:00
FlightControl
4dde14eba6 Fixes accounting of destroy and updated documentation
Destroy events were not correctly accounted for in the new tasking
system. this is now fixed.
2017-03-19 15:02:49 +01:00
FlightControl
bf489f6679 Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl 2017-03-19 08:24:49 +01:00
Sven Van de Velde
f4f87f0a3d Merge pull request #321 from FlightControl-Master/FlightControl
Added scoring for A2G dispatcher
2017-03-19 08:03:59 +01:00
FlightControl
2e894df4c2 Updated the DETECTION_MANAGER
-- Added a scoring tailoring possibility for TASK_DISPATCHER class
tasks.
-- DETECTION_MANAGER has become an FSM
-- TASK_A2G_DISPATCHER has become an FSM. It implements an Assign Event,
that can be handled. In the Assign event handler, you can specify
scoring schemes etc.
2017-03-19 07:59:47 +01:00
FlightControl
90cb0dc012 Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl 2017-03-19 05:52:21 +01:00
Sven Van de Velde
afc0a15080 Merge pull request #320 from FlightControl-Master/master-ai-cas-optimize-engage
AI_CAS optimization attempt
2017-03-19 05:51:13 +01:00
FlightControl
369ea1b6e6 AI_CAS optimization attempt 2017-03-19 05:50:46 +01:00
Sven Van de Velde
59f7b56ceb Merge pull request #319 from FlightControl-Master/FlightControl
Added test mission to test respawning
2017-03-19 05:40:12 +01:00
FlightControl
9a971d61cf Added test mission to test respawning 2017-03-19 05:34:36 +01:00
Grey-Echo
8561bced0a Corrects small typos in comments 2017-03-18 20:49:26 +01:00
FlightControl
441b3df3fb Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl 2017-03-18 20:26:34 +01:00
Sven Van de Velde
fe3ffae01c Merge pull request #318 from FlightControl-Master/master-missions-rework
Master missions rework
2017-03-18 14:43:41 +01:00
FlightControl
cd69b8a879 Publish reworked missions with global variables 2017-03-18 14:42:03 +01:00
FlightControl
553ceb462e first batch of missions 2017-03-18 14:18:14 +01:00
Sven Van de Velde
71374f0ce1 Merge pull request #316 from FlightControl-Master/FlightControl
Fixed weaponexpend problem for gunterlund in AI_CAS
-- The attack sequence was commented out. Put it back in.
-- Fixed documentation order glitch.
-- Documented the usage of AI.Task.WeaponExpend
-- Deleted weaponType from Controller Task
2017-03-18 07:44:17 +01:00
FlightControl
d216fc96da Fixed weaponexpend problem for gunterlund in AI_CAS.
-- The attack sequence was commented out. Put it back in.
-- Fixed documentation order glitch.
-- Documented the usage of AI.Task.WeaponExpend
-- Deleted weaponType from Controller Task
2017-03-18 07:42:41 +01:00
FlightControl
80ba6bf40d Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl 2017-03-18 05:45:44 +01:00
Sven Van de Velde
46e8f30c4a Merge pull request #315 from FlightControl-Master/FlightControl
New tasking system
2017-03-17 21:53:41 +01:00
FlightControl
4e59f1a674 Final commit ... 2017-03-17 21:49:06 +01:00
FlightControl
ce00cbf83e Better 2017-03-17 18:46:00 +01:00
FlightControl
9f2179a428 Trying to get the dependency on EVENTs solved. 2017-03-17 18:33:08 +01:00
FlightControl
3f0c983194 Updates 2017-03-17 13:59:19 +01:00
FlightControl
7231492eaa Release prep 2017-03-17 11:50:24 +01:00
Sven Van de Velde
8c4ae420e2 Merge pull request #314 from FlightControl-Master/FlightControl-menu-optimization
Flightcontrol menu optimization
2017-03-17 11:30:09 +01:00
FlightControl
83ed29a90a OK. Menu is working again ... 2017-03-17 11:29:23 +01:00
FlightControl
af55214c52 Menu optimization 2017-03-17 06:57:32 +01:00
FlightControl
4c64548bea Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl
# Conflicts:
#	Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua
#	Moose Mission Setup/Moose.lua
2017-03-16 05:03:43 +01:00
Sven Van de Velde
9b54e32b50 Merge pull request #313 from FlightControl-Master/master-bugfix-cas-engage
Fix of Engage trigger loosing given parameters when rerouting
2017-03-15 19:17:10 +01:00
FlightControl
0555ced241 Fix of Engage trigger loosing given parameters when rerouting
This is fixed now.
2017-03-15 19:16:29 +01:00
FlightControl
056bebe4c3 Updated all test missions 2017-03-15 19:09:33 +01:00
FlightControl
520ee6e459 Updated Mission, cleaned it up and removed stuff. 2017-03-15 11:49:47 +01:00
FlightControl
9fc3f7a601 Made user exits for Mission Completion evaluation...
Man, this is going to rock!
2017-03-15 10:08:03 +01:00
Sven Van de Velde
450892bfd9 Merge pull request #312 from FlightControl-Master/flightcontrol-nillification
Flightcontrol nillification
2017-03-15 09:40:59 +01:00
FlightControl
d77405bf9b nillification of the unit processes for tasks is fixed. 2017-03-15 09:35:41 +01:00
FlightControl
65c15281fd Merge remote-tracking branch 'refs/remotes/origin/master' into flightcontrol-nillification
# Conflicts:
#	Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua
#	Moose Mission Setup/Moose.lua
2017-03-15 05:06:23 +01:00
FlightControl
d7f75eaab6 Generate moose.lua 2017-03-14 15:30:37 +01:00
Sven Van de Velde
03d921831f Merge pull request #311 from FlightControl-Master/Whisper_TaskRouteToZone
TaskRouteToZone formation fix
2017-03-14 15:28:36 +01:00
kalbuth
dbb710a980 TaskRouteToZone formation fix
setting initial point of TaskRouteToZone mission waypoints to have
expected Formation set up.
2017-03-14 15:03:12 +01:00
FlightControl
d160b24b0d Commit 2017-03-14 09:31:17 +01:00
FlightControl
b7e5af772a Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl
# Conflicts:
#	Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua
#	Moose Mission Setup/Moose.lua
#	Moose Test Missions/CAP - Combat Air Patrol/CAP-001 - Combat Air
Patrol/CAP-001 - Combat Air Patrol.miz
#	Moose Test Missions/CAP - Combat Air Patrol/CAP-010 - CAP and Engage
within Range/CAP-010 - CAP and Engage within Range.miz
#	Moose Test Missions/CAP - Combat Air Patrol/CAP-011 - CAP and Engage
within Zone/CAP-011 - CAP and Engage within Zone.miz
#	Moose Test Missions/CAP - Combat Air Patrol/CAP-020 - Combat Air
Patrol RTB Test/CAP-020 - Combat Air Patrol RTB Test.miz
#	Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by
Airplane Group/CAS-001 - CAS in a ZONE-ME Test.miz
#	Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by
Airplane Group/CAS-001 - CAS in a Zone by Airplane Group.miz
#	Moose Test Missions/CAS - Close Air Support/CAS-002 - CAS in a Zone by
Airplane Group - Engage with Speed/CAS-002 - CAS in a Zone by Airplane
Group - Engage with Speed.miz
#	Moose Test Missions/CAS - Close Air Support/CAS-003 - CAS in a Zone by
Airplane Group - Engage with Speed and Altitude/CAS-003 - CAS in a Zone
by Airplane Group - Engage with Speed and Altitude.miz
#	Moose Test Missions/CAS - Close Air Support/CAS-010 - CAS in a Zone by
Helicopter/CAS-010 - CAS in a Zone by Helicopter.miz
#	Moose Test Missions/CAS - Close Air Support/CAS-011 - CAS in a Zone by
Helicopter Group/CAS-011 - CAS in a Zone by Helicopter Group.miz
#	Moose Test Missions/CAS - Close Air Support/CAS-111 - Multiple CAS in
1 Radius Zone by Helicopter and AirPlane Groups/CAS-111 - Multiple CAS
in 1 Radius Zone by Helicopter and AirPlane Groups.miz
#	Moose Test Missions/SPA - Spawning/SPA-010 - Spawn Demo/SPA-010 -
Spawn Demo.miz
#	Moose Test Missions/SPA - Spawning/SPA-011 - Ground Ops - Simple
Spawning/SPA-011 - Ground Ops - Simple Spawning.miz
#	Moose Test Missions/SPA - Spawning/SPA-012 - Ground Ops - Multiple
Spawns/SPA-012 - Ground Ops - Multiple Spawns.miz
#	Moose Test Missions/SPA - Spawning/SPA-013 - Ground Ops - Scheduled
Spawns/SPA-013 - Ground Ops - Scheduled Spawns.miz
#	Moose Test Missions/SPA - Spawning/SPA-014 - Ground Ops - Scheduled
Spawns Limited/SPA-014 - Ground Ops - Scheduled Spawns Limited.miz
#	Moose Test Missions/SPA - Spawning/SPA-015 - Ground Ops - Randomize
Route/SPA-015 - Ground Ops - Randomize Route.miz
#	Moose Test Missions/SPA - Spawning/SPA-016 - Ground Ops - Randomize
Zones/SPA-016 - Ground Ops - Randomize Zones.miz
#	Moose Test Missions/SPA - Spawning/SPA-017 - Ground Ops - Set AI
inactive while spawning/SPA-017 - Ground Ops - Set AI inactive while
spawning.miz
#	Moose Test Missions/SPA - Spawning/SPA-018 - Ground Ops - Randomize
Templates/SPA-018 - Ground Ops - Randomize Templates.miz
#	Moose Test Missions/SPA - Spawning/SPA-019 - Ground Ops - Randomize
Templates without Waypoints/SPA-019 - Ground Ops - Randomize Templates
without Waypoints.miz
#	Moose Test Missions/SPA - Spawning/SPA-020 - Ground Ops - Randomize
Templates in Random Zones without Waypoints/SPA-020 - Ground Ops -
Randomize Templates in Random Zones without Waypoints.miz
#	Moose Test Missions/SPA - Spawning/SPA-100 - CleanUp Inactive
Units/SPA-100 - CleanUp Inactive Units.miz
#	Moose Test Missions/SPA - Spawning/SPA-110 - Limit Spawning/SPA-110 -
Limit Spawning.miz
#	Moose Test Missions/SPA - Spawning/SPA-120 - Air Ops - Scheduled Spawn
with Repeat on Landing with Limit/SPA-120 - Air Ops - Scheduled Spawn
with Repeat on Landing with Limit.miz
#	Moose Test Missions/SPA - Spawning/SPA-121 - Air Ops - Scheduled
Spawns with Repeat on Landing with Limit/SPA-121 - Air Ops - Scheduled
Spawns with Repeat on Landing with Limit.miz
#	Moose Test Missions/SPA - Spawning/SPA-130 - Uncontrolled
Spawning/SPA-130 - Uncontrolled Spawning.miz
#	Moose Test Missions/SPA - Spawning/SPA-200 - Randomize Unit
Types/SPA-200 - Randomize Unit Types.miz
#	Moose Test Missions/SPA - Spawning/SPA-220 - Randomize Zones/SPA-220 -
Randomize Zones.miz
#	Moose Test Missions/SPA - Spawning/SPA-310 - Spawn at Static
position/SPA-310 - Spawn at Static position.miz
#	Moose Test Missions/SPA - Spawning/SPA-320 - Spawn at Unit
position/SPA-320 - Spawn at Unit position.miz
#	Moose Test Missions/SPA - Spawning/SPA-330 - Spawn at Vec2
position/SPA-330 - Spawn at Vec2 position.miz
#	Moose Test Missions/SPA - Spawning/SPA-340 - Spawn at Vec3
position/SPA-340 - Spawn at Vec3 position.miz
#	docs/Documentation/AI_Cas.html
2017-03-14 09:30:54 +01:00
FlightControl
ed81858f0c Move of header template 2017-03-14 09:19:26 +01:00
Sven Van de Velde
78625b0f7f Merge pull request #309 from FlightControl-Master/master-addon-spawn-InitSpawnRandomizeGroups
SPAWN: Added InitRandomizePosition API
2017-03-14 09:10:19 +01:00
FlightControl
a0100d0980 SPAWN: Added InitRandomizePosition API
-- SPAWN:InitRandomizePosition( RandomizePosition, OuterRadius,
InnerRadius ) added.
-- SPA-350 test missions
-- Documentation Update
-- testing.
2017-03-14 09:09:48 +01:00
Sven Van de Velde
8c8e95d8fb Merge pull request #308 from FlightControl-Master/master-bugfix-ai-cas-cap-abort
Fixed abort in AI_CAP_ZONE and CAI_CAS_ZONE
2017-03-14 06:45:39 +01:00
FlightControl
782af122dd Fixed abort in AI_CAP_ZONE and CAI_CAS_ZONE
-- When the event Abort was triggered, nothing happened.
Is fixed now. AI will fly back to the patrol zone and will continue
patrolling.
2017-03-14 06:45:14 +01:00
Sven Van de Velde
985a922d5f Merge pull request #307 from FlightControl-Master/master-addon-SPAWN
SPAWN: Keep unit names
2017-03-14 05:38:13 +01:00
FlightControl
8d535fa3dd SPAWN: Keep unit names
-- new method SPAWN:InitKeepUnitNames() added.
-- Added test mission SPA-021
-- Documentation
-- Testing
2017-03-14 05:37:36 +01:00
FlightControl
fe79821474 Fixing SCHEDULER - TO BE FURTHER STUDIES AND FIXED
TASK object should dissapear after nillified.
They are hooked to the SCHEDULER... SCHEDULEs should dissapear, but they
don't....

To be further studied.
2017-03-14 03:36:12 +01:00
FlightControl
32d23927b6 Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl
# Conflicts:
#	Moose Development/Moose/AI/AI_CAS.lua
#	Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua
#	Moose Mission Setup/Moose.lua
#	Moose Test Missions/CAS - Close Air Support/CAS-003 - CAS in a Zone by
Airplane Group - Engage with Speed and Altitude/CAS-003 - CAS in a Zone
by Airplane Group - Engage with Speed and Altitude.miz
#	docs/Documentation/AI_Cas.html
2017-03-13 10:13:17 +01:00
Sven Van de Velde
9d67015649 Merge pull request #305 from FlightControl-Master/master-fix-ai-cap
Master fix ai cap
2017-03-13 10:00:52 +01:00
FlightControl
6b5fbc530b Updated documentation and test mission to show the mechanism. 2017-03-13 10:00:19 +01:00
FlightControl
e997e0769b Merge remote-tracking branch 'refs/remotes/origin/master' into master-fix-ai-cap 2017-03-13 08:39:20 +01:00
FlightControl
36123f02c7 Test of unit types detection 2017-03-12 19:35:28 +01:00
FlightControl
c07ac8e1ba DETECTION_TYPES working! 2017-03-12 19:32:36 +01:00
FlightControl
13ac7e0c41 Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl
# Conflicts:
#	Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua
#	Moose Mission Setup/Moose.lua
#	Moose Test Missions/CAP - Combat Air Patrol/CAP-001 - Combat Air
Patrol/CAP-001 - Combat Air Patrol.miz
#	Moose Test Missions/CAP - Combat Air Patrol/CAP-010 - CAP and Engage
within Range/CAP-010 - CAP and Engage within Range.miz
#	Moose Test Missions/CAP - Combat Air Patrol/CAP-011 - CAP and Engage
within Zone/CAP-011 - CAP and Engage within Zone.miz
#	Moose Test Missions/CAP - Combat Air Patrol/CAP-020 - Combat Air
Patrol RTB Test/CAP-020 - Combat Air Patrol RTB Test.miz
#	Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by
Airplane Group/CAS-001 - CAS in a ZONE-ME Test.miz
#	Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by
Airplane Group/CAS-001 - CAS in a Zone by Airplane Group.miz
#	Moose Test Missions/CAS - Close Air Support/CAS-002 - CAS in a Zone by
Airplane Group - Engage with Speed/CAS-002 - CAS in a Zone by Airplane
Group - Engage with Speed.miz
#	Moose Test Missions/CAS - Close Air Support/CAS-003 - CAS in a Zone by
Airplane Group - Engage with Speed and Altitude/CAS-003 - CAS in a Zone
by Airplane Group - Engage with Speed and Altitude.miz
#	Moose Test Missions/CAS - Close Air Support/CAS-010 - CAS in a Zone by
Helicopter/CAS-010 - CAS in a Zone by Helicopter.miz
#	Moose Test Missions/CAS - Close Air Support/CAS-011 - CAS in a Zone by
Helicopter Group/CAS-011 - CAS in a Zone by Helicopter Group.miz
#	Moose Test Missions/CAS - Close Air Support/CAS-111 - Multiple CAS in
1 Radius Zone by Helicopter and AirPlane Groups/CAS-111 - Multiple CAS
in 1 Radius Zone by Helicopter and AirPlane Groups.miz
#	Moose Test Missions/PAT - Patrolling/PAT-001 - Switching Patrol
Zones/PAT-001 - Switching Patrol Zones.miz
2017-03-12 17:51:18 +01:00
FlightControl
052ca384a5 Progress 2017-03-12 17:48:19 +01:00
FlightControl
82f505ddb0 Progress
-- Got DETECTION_TYPES running with A2G_TASK_DISPATCHER
2017-03-12 17:14:00 +01:00
Sven Van de Velde
c53b3cd1e0 Merge pull request #303 from FlightControl-Master/master-fix-ai-cap
Fixes bug sending messages upon a Destroy event
2017-03-12 07:21:46 +01:00
FlightControl
e4fec6432d Fixes bug sending messages upon a Destroy event
-- Removed the Messages from the Destroy event handler.
-- AI_CAP_ZONE never got a Destroy event trigger because the Dead event
was never catched.
-- The Dead event handlers are now the default name to keep up with the
standard.
-- Check that ONLY the Destroy event is triggered for the targets in
scope.
2017-03-12 07:20:01 +01:00
Grey-Echo
e6e7d8b166 LDT pass on RADIO class 2017-03-11 14:52:09 +01:00
Grey-Echo
4df4223373 Implement RADIO:NewGenericTransmission() and RADIO:NewUnitTransmission() 2017-03-11 14:45:22 +01:00
FlightControl
e9a7898410 Updates on presentation 2017-03-11 09:22:03 +01:00
Grey-Echo
4b45476105 Every RADIO Setter is tested 2017-03-10 23:34:42 +01:00
Grey-Echo
894ed41374 RADIO:SetFileName() and RADIO:SetFrequency() both tested and fixed 2017-03-10 23:34:32 +01:00
Grey-Echo
4655a6413d Support for UNIT and GROUP in RADIO:SetTransmission() + indentation 2017-03-10 23:34:24 +01:00
Grey-Echo
396aa38c18 Move verifying logic in set methods in RADIO
Both RADIO:NewGenericTransmission() and RADIO:NewUnitTransmission() need to be redone to accomodate the change
2017-03-10 23:34:16 +01:00
Grey-Echo
e4343179b3 Add checks and trace for RADIO:NewGenericTransmission 2017-03-10 23:34:09 +01:00
Grey-Echo
a9c7cd4e18 Add checks and trace for RADIO:New 2017-03-10 23:33:56 +01:00
Grey-Echo
f35269936f Fixes in RADIO Class.
Every method implemented is now functionnal and tested
2017-03-10 23:33:38 +01:00
Grey-Echo
3754ea6c27 Change RADIO:NewGenericTransmission() and RADIO:NewUnitTransmission() to support optionnal parameters 2017-03-10 23:33:31 +01:00
Grey-Echo
65c61a15b4 Fix various bugs in RADIO
This is the first implementation that PLAY A SOUND !
The whole RADIO class isn't tested thoroughly though
2017-03-10 23:33:21 +01:00
Grey-Echo
b75d90092d Add Test Mission + Small Fixes to RADIO 2017-03-10 23:33:16 +01:00
Grey-Echo
c8a4c87226 Merge branch 'developer-configuration' into RadioComs 2017-03-10 23:33:11 +01:00
Grey-Echo
d7291d44fd Updates 2017-03-10 23:33:05 +01:00
Grey-Echo
5da73b220e Merge branch 'developer-configuration' into RadioComs 2017-03-10 23:32:58 +01:00
Grey-Echo
3478f8e749 Eclipse share configuration files 2017-03-10 23:32:51 +01:00
Grey-Echo
b73a48c4e0 Merge pull request #291 from FlightControl-Master/master-bugfix
Documentation refinement of Core Classes
2017-03-10 23:32:46 +01:00
Grey-Echo
ac0effe141 Documentation refinement of Core Classes 2017-03-10 23:32:39 +01:00
Grey-Echo
fc3ad53ebe Implement POSITIONABLE:GetRadio() and small fixes in Radio.lua 2017-03-10 23:32:33 +01:00
Grey-Echo
59bba1e17e Update Moose.lua to include Core/Radio.lua
With this, the first slice of Radio is done and testing can start
2017-03-10 23:32:27 +01:00
Grey-Echo
2365681438 Implements a first pass for RADIO:Broadcast() 2017-03-10 23:32:22 +01:00
Grey-Echo
ff5048a43f Implementation of RADIO:NewTransmissionUnit() 2017-03-10 23:32:17 +01:00
Grey-Echo
1a45b2bd44 Modified implementation of RADIO:NewTransmission() to make parameters optional 2017-03-10 23:32:11 +01:00
Grey-Echo
2437e45eec Remove RADIO.ConvertFrequency()
The implementataion didn't allow for WW2 style frequencies.
I might try to redo it later, but for now, the user is expected to input frequency in kHz
2017-03-10 23:32:05 +01:00
Grey-Echo
921743bf20 Implements RADIO:NewTransmission()
the implementation made me realize the need for RADIO.VerifyFileName() and RADIO.ConvertFrequency(),
so they are both implemented in this commit to.
2017-03-10 23:31:59 +01:00
Grey-Echo
c37d6e6f95 Implements RADIO's constructor 2017-03-10 23:31:53 +01:00
Grey-Echo
7866de2370 Declaration of RADIO Class 2017-03-10 23:31:47 +01:00
Grey-Echo
ad0cac8356 Laying the groundwork for the RADIO class
With a special emphasis on LDT Documentation
2017-03-10 23:31:32 +01:00
FlightControl
8add761982 Updated documentation 2017-03-10 23:05:02 +01:00
FlightControl
d70d13449d Regression Testing 2017-03-10 22:40:16 +01:00
FlightControl
b606041c11 Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl 2017-03-10 20:45:06 +01:00
FlightControl
7ebf0cdb0f Updates 2017-03-10 14:50:14 +01:00
Sven Van de Velde
5e25851f36 Merge pull request #301 from FlightControl-Master/Logo
Logo
2017-03-10 14:13:04 +01:00
FlightControl
4100000edf New avatars etc 2017-03-10 14:12:17 +01:00
FlightControl
801d6a6f30 Merge remote-tracking branch 'refs/remotes/origin/master' into Logo 2017-03-10 14:11:11 +01:00
FlightControl
cfc4dd8846 Progress 2017-03-10 14:09:15 +01:00
FlightControl
907b9c9abb Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl
# Conflicts:
#	Moose Mission Setup/Moose.lua
2017-03-10 06:23:51 +01:00
EasyEB
5b18773f6c Merge pull request #300 from FlightControl-Master/Logo
MOOSE Brand
2017-03-09 21:16:51 +01:00
EasyEB
fb3f6149c7 Add files via upload 2017-03-09 21:11:06 +01:00
FlightControl
df96c55c5a Logo placeholder 2017-03-09 20:53:28 +01:00
FlightControl
01e2aa5b5d Progress 2017-03-09 20:47:34 +01:00
dwpenney
958d97ad9d Merge pull request #299 from FlightControl-Master/Issue-292
Issue 292
2017-03-09 13:23:23 -04:00
FlightControl
3bd0cdbe32 Progress 2017-03-09 17:44:56 +01:00
David Penney
20239a1248 Update the static Moose w/ the component changes 2017-03-09 12:02:09 -04:00
David Penney
57099ad136 A random fix for code I notices
This relates to the Vec2/Vec3 redefinition of y. Vec3 is (x=Latitude, y=Altitude and z=Longitude). Vec2 is (x=Latitude, y=Longitude).
2017-03-09 11:49:20 -04:00
David Penney
29148cf8fd But fix for Issue-292
By accessing the values directly, we skip past the problem where GetY returns different values for POINT_VEC2 and POINT_VEC3 objects.
2017-03-09 11:47:40 -04:00
David Penney
670768df42 Addition of GetLat, GetLon, SetLat, SetLon for POINT_VEC2
This should help clarify the difference between Vec2 and Vec3. The original GetX, Gety, SetX and SetY functions remain so that it does not break other peoples stuff.
2017-03-09 11:46:31 -04:00
FlightControl
fbae55b9f9 Progress on tasking 2017-03-09 13:37:13 +01:00
FlightControl
40884cd411 removed trash 2017-03-09 10:39:20 +01:00
FlightControl
5d6ac9419f Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl
# Conflicts:
#	Moose Development/Moose/Core/Event.lua
#	Moose Development/Moose/Functional/Scoring.lua
#	Moose Development/Moose/Wrapper/Controllable.lua
#	Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua
#	Moose Mission Setup/Moose.lua
#	Moose Test Missions/EVT - Event Handling/EVT-100 - OnEventShot
Example/EVT-100 - OnEventShot Example.miz
#	Moose Test Missions/EVT - Event Handling/EVT-101 - OnEventHit
Example/EVT-101 - OnEventHit Example.miz
#	Moose Test Missions/EVT - Event Handling/EVT-102 - OnEventTakeoff
Example/EVT-102 - OnEventTakeoff Example.miz
#	Moose Test Missions/EVT - Event Handling/EVT-103 - OnEventLand
Example/EVT-103 - OnEventLand Example.miz
#	Moose Test Missions/EVT - Event Handling/EVT-104 - OnEventCrash
Example/EVT-104 - OnEventCrash Example.miz
#	Moose Test Missions/SCO - Scoring/SCO-100 - Scoring of Statics/SCO-100
- Scoring of Statics.miz
#	Moose Test Missions/SCO - Scoring/SCO-101 - Scoring Client to
Client/SCO-101 - Scoring Client to Client.miz
#	docs/Documentation/Controllable.html
#	docs/Documentation/Event.html
#	docs/Documentation/Point.html
#	docs/Documentation/Zone.html
2017-03-09 10:38:55 +01:00
Sven Van de Velde
d16b68e03a Merge pull request #298 from FlightControl-Master/master-bugfix
fixes of base and scoring
2017-03-08 21:41:04 +01:00
FlightControl
95b091c5d0 static moose 2017-03-08 21:40:02 +01:00
FlightControl
1c3e737d8e Bugfixes of BASE and SCORING 2017-03-08 21:39:41 +01:00
Sven Van de Velde
891ecf996c Merge pull request #297 from FlightControl-Master/master-bugfix
Events with initiator and target will now properly be handled for UNIT and GROUP level event handlers.
2017-03-08 20:50:48 +01:00
FlightControl
c6ad706c1e Fixed a potential glitch.
Now target will ALWAYS be tested too.
2017-03-08 20:49:19 +01:00
FlightControl
894854440b Test mission to test HIT event handling of target units 2017-03-08 20:37:18 +01:00
FlightControl
9354e23630 Fix to also handle events for UNIT and GROUP when the target is in the event data.
The target was never checked, and thus in a HIT event, a HIT event was
never called. This is fixed now.
2017-03-08 20:02:03 +01:00
FlightControl
bcb68e2d64 Merge remote-tracking branch 'refs/remotes/origin/master' into master-bugfix 2017-03-08 19:40:35 +01:00
Sven Van de Velde
7c38bbce2d Merge pull request #296 from FlightControl-Master/developer-configuration
Developer configuration
2017-03-08 19:38:43 +01:00
FlightControl
43848e8dc7 New launch files 2017-03-08 19:25:30 +01:00
FlightControl
9fee299e0f Updates 2017-03-08 14:00:43 +01:00
FlightControl
28f81990f5 Eclipse share configuration files 2017-03-08 13:44:07 +01:00
Sven Van de Velde
3077631bb4 Merge pull request #295 from FlightControl-Master/Grey-Echo
Test of Permissions
2017-03-08 13:42:06 +01:00
Grey_Echo
fc03ba4661 Test of Permissions 2017-03-08 13:33:13 +01:00
Sven Van de Velde
1bdacbdad9 Merge pull request #291 from FlightControl-Master/master-bugfix
Documentation refinement of Core Classes
2017-03-07 21:00:46 +01:00
FlightControl
0bfbf042b5 Documentation refinement of Core Classes 2017-03-07 20:58:53 +01:00
Sven Van de Velde
343de7fe69 Merge pull request #290 from FlightControl-Master/master-bugfix
Fix problem with AttackControllable as a result of a stupid rename problem.
2017-03-07 12:03:29 +01:00
FlightControl
f708d3d33d Fix problem with AttackControllable as a result of a stupid rename problem. 2017-03-07 12:02:58 +01:00
Sven Van de Velde
f4c2637d66 Merge pull request #289 from FlightControl-Master/master-bugfix
Fixed Client to Client SCORING problem.
The TargetPlayerName was not logged in the CSV file. This is now also added. So, now, both the PlayerName and the Target PlayerName will be logged in the CSV. So when Client to Client kills happen, both player names will be listed.
Nice for statistics.
2017-03-07 10:29:53 +01:00
FlightControl
e84c9cc83b Fixed a problem that the TargetPlayerName was not logged in the CSV.
I adapted the CSV file, and added as the second parameter the
TargetPlayerName.
Where relevant, the TargetPlayerName will be mentioned in the CSV
logging.
IMPORTANT, the method SCORING:ScoreCSV has been adapted!!! The second
parameter is now the TargetPlayerName. Provide a "" or nil, if there is
no TargetPlayerName, but ensure the order of the parameters is correct!

function SCORING:ScoreCSV( PlayerName, TargetPlayerName, ScoreType,
ScoreTimes, ScoreAmount, PlayerUnitName, PlayerUnitCoalition,
PlayerUnitCategory, PlayerUnitType, TargetUnitName, TargetUnitCoalition,
TargetUnitCategory, TargetUnitType )
2017-03-07 10:27:59 +01:00
FlightControl
2f7bcbe7c6 Merge remote-tracking branch 'refs/remotes/origin/master' into master-bugfix 2017-03-07 09:57:41 +01:00
Sven Van de Velde
ad90a08ec0 Merge pull request #288 from FlightControl-Master/master-adding
Master Bugfix
2017-03-07 09:44:00 +01:00
FlightControl
ef6885b63b Updated mission naming, documentation, briefing and tested all 2017-03-07 09:38:20 +01:00
FlightControl
02c44c158f Updated mission briefing 2017-03-07 09:26:25 +01:00
FlightControl
8dc13f7a0c Finalization of patch. 2017-03-07 09:24:10 +01:00
FlightControl
af85399975 Implemented event dispatching for GROUP
-- Created EVT-200 test mission
-- Documentation
2017-03-07 09:15:44 +01:00
FlightControl
ef67c82c0f AI_PATROL_ZONE
Added a Stop event and Stopped state to trigger a state transition from
* to Stopped when the AI process needs to be stopped.
2017-03-07 08:31:18 +01:00
FlightControl
e6db83047f Merge remote-tracking branch 'refs/remotes/origin/master' into master-adding 2017-03-07 08:27:35 +01:00
Sven Van de Velde
3a847fed9b Merge pull request #285 from FlightControl-Master/master-bugfix
New test mission to test OnEventHit for a SET_UNIT...
2017-03-06 16:38:36 +01:00
FlightControl
ef536022cc Removed smoke from AI_CAS 2017-03-06 16:32:14 +01:00
FlightControl
4b008f8879 New test mission demonstrating OnEventHit for a SET_UNIT object 2017-03-06 16:31:06 +01:00
FlightControl
454d96d4af Merge remote-tracking branch 'refs/remotes/origin/master' into master-bugfix 2017-03-06 15:44:37 +01:00
FlightControl
a2f4ce1db5 Updates on missions 2017-03-06 14:34:34 +01:00
FlightControl
25bd0f8049 Fixed DETECTION_AREAS, working again and complete with test missions. 2017-03-06 13:56:34 +01:00
FlightControl
8d73df48ce Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl
# Conflicts:
#	Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua
#	Moose Mission Setup/Moose.lua
#	Moose Test Missions/SCO - Scoring/SCO-100 - Scoring of Statics/SCO-100
- Scoring of Statics.miz
#	Moose Test Missions/SCO - Scoring/SCO-101 - Scoring Client to
Client/SCO-101 - Scoring Client to Client.miz
#	docs/Documentation/Point.html
2017-03-06 12:28:27 +01:00
Sven Van de Velde
e6fcc15965 Merge pull request #284 from FlightControl-Master/master-adding
Renamed TaskRouteToVec2 and TaskRouteToVec3
2017-03-06 11:44:18 +01:00
FlightControl
9d0f264d5c Point optmized. 2017-03-06 11:43:13 +01:00
FlightControl
6f3e076f13 Merge remote-tracking branch 'refs/remotes/origin/master' into master-adding 2017-03-06 10:38:47 +01:00
FlightControl
0315bf5aa0 Fixed test mission 2017-03-05 14:52:12 +01:00
FlightControl
2cf27b3b0f SCO-500 Demo Mission for SCORING system demonstration on a MP server 2017-03-05 14:37:18 +01:00
FlightControl
a6c596f724 Check scoring test mission in master. 2017-03-05 14:23:13 +01:00
FlightControl
32d6233cf1 Progress Dispatcher 2017-03-05 12:37:56 +01:00
FlightControl
6710bfba26 Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl
# Conflicts:
#	Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua
#	Moose Mission Setup/Moose.lua
2017-03-05 10:28:54 +01:00
FlightControl
fc3b66c06c Removed trace from Zone.lua 2017-03-05 10:27:54 +01:00
FlightControl
b004f0f968 Removed trace 2017-03-05 10:27:29 +01:00
FlightControl
86e684dc63 Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl
# Conflicts:
#	Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua
#	Moose Mission Setup/Moose.lua
2017-03-05 10:26:36 +01:00
FlightControl
56c9401ce3 Merge remote-tracking branch 'refs/remotes/origin/master' into master-bugfix 2017-03-05 10:15:27 +01:00
FlightControl
3244f46e88 Moose.lua with the changes 2017-03-05 10:14:53 +01:00
FlightControl
b4527635d6 Progress 2017-03-05 10:14:29 +01:00
FlightControl
a1ae1cc519 Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl 2017-03-05 10:05:36 +01:00
FlightControl
538e040fdf Updated Database 2017-03-05 10:05:12 +01:00
FlightControl
61cd592967 Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl 2017-03-05 10:02:28 +01:00
Sven Van de Velde
158e41e04e Merge pull request #279 from FlightControl-Master/master-bugfix
Master bugfix
2017-03-05 10:01:58 +01:00
FlightControl
0d474b2286 Updated Database 2017-03-05 10:01:28 +01:00
FlightControl
79657efaf8 Merge remote-tracking branch 'refs/remotes/origin/master' into master-bugfix 2017-03-05 09:54:43 +01:00
FlightControl
8905a49d0c Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl
# Conflicts:
#	Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua
#	Moose Mission Setup/Moose.lua
2017-03-05 09:52:13 +01:00
Sven Van de Velde
48841f2412 Merge pull request #278 from FlightControl-Master/master-bugfix
Master bugfix
2017-03-05 09:51:01 +01:00
FlightControl
0ebff60e8d Updated Set 2017-03-05 09:50:31 +01:00
FlightControl
5c5bedd794 Merge remote-tracking branch 'refs/remotes/origin/master' into master-bugfix 2017-03-05 09:46:00 +01:00
FlightControl
21c2dd8c80 Progress 2017-03-05 09:45:49 +01:00
FlightControl
de84accffa Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl
# Conflicts:
#	Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua
#	Moose Mission Setup/Moose.lua
2017-03-05 09:37:06 +01:00
Sven Van de Velde
91bc509984 Merge pull request #277 from FlightControl-Master/master-adding
Zone boundaries by tires with a white flag.
2017-03-05 09:33:21 +01:00
FlightControl
f7bf997511 Implemented a new method on ZONE:BoundZone
Creates a visual boundary around a ZONE_* of tires with a white flag.
These tires will also move when the ZONE is "rebound".
2017-03-05 09:32:17 +01:00
FlightControl
5793e04aeb Merge remote-tracking branch 'refs/remotes/origin/master' into master-adding 2017-03-05 07:46:09 +01:00
FlightControl
cd3648f03d Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl
# Conflicts:
#	Moose Development/Moose/Functional/Scoring.lua
#	Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua
#	Moose Mission Setup/Moose.lua
#	Moose Test Missions/ABP - Airbase Police/APL-001 - Caucasus/APL-001 -
Caucasus.miz
#	Moose Test Missions/ABP - Airbase Police/APL-002 - Nevada/APL-002 -
Nevada.miz
#	Moose Test Missions/ACL - Airbase Cleaner/ACL-001 - Airbase
CleanUp/ACL-001 - Airbase CleanUp.miz
#	Moose Test Missions/AIB - AI Balancing/AIB-001 - Spawned AI/AIB-001 -
Spawned AI.miz
#	Moose Test Missions/AIB - AI Balancing/AIB-002 - Patrol AI/AIB-002 -
Patrol AI.miz
#	Moose Test Missions/AIB - AI Balancing/AIB-003 - Two coalitions
InitCleanUp test/AIB-003 - Two coalitions InitCleanUp test.miz
#	Moose Test Missions/AIB - AI Balancing/AIB-004 - Respawn Test when
Destroyed/AIB-004 - Respawn Test when Destroyed.miz
#	Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and
Randomize Zones/AIB-005 - Patrol AI and Randomize Zones.miz
#	Moose Test Missions/AIB - AI Balancing/AIB-006 - Declutter AI at
Airbases/AIB-006 - Declutter AI at Airbases.miz
#	Moose Test Missions/CAP - Combat Air Patrol/CAP-001 - Combat Air
Patrol/CAP-001 - Combat Air Patrol.miz
#	Moose Test Missions/CAP - Combat Air Patrol/CAP-010 - CAP and Engage
within Range/CAP-010 - CAP and Engage within Range.miz
#	Moose Test Missions/CAP - Combat Air Patrol/CAP-011 - CAP and Engage
within Zone/CAP-011 - CAP and Engage within Zone.miz
#	Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by
Airplane Group/CAS-001 - CAS in a ZONE-ME Test.miz
#	Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by
Airplane Group/CAS-001 - CAS in a Zone by Airplane Group.miz
#	Moose Test Missions/CAS - Close Air Support/CAS-002 - CAS in a Zone by
Airplane Group - Engage with Speed/CAS-002 - CAS in a Zone by Airplane
Group - Engage with Speed.miz
#	Moose Test Missions/CAS - Close Air Support/CAS-003 - CAS in a Zone by
Airplane Group - Engage with Speed and Altitude/CAS-003 - CAS in a Zone
by Airplane Group - Engage with Speed and Altitude.miz
#	Moose Test Missions/CAS - Close Air Support/CAS-010 - CAS in a Zone by
Helicopter/CAS-010 - CAS in a Zone by Helicopter.miz
#	Moose Test Missions/CAS - Close Air Support/CAS-011 - CAS in a Zone by
Helicopter Group/CAS-011 - CAS in a Zone by Helicopter Group.miz
#	Moose Test Missions/CAS - Close Air Support/CAS-111 - Multiple CAS in
1 Radius Zone by Helicopter and AirPlane Groups/CAS-111 - Multiple CAS
in 1 Radius Zone by Helicopter and AirPlane Groups.miz
#	Moose Test Missions/CGO - Cargo/CGO-001 - Unit Boarding/CGO-001 - Unit
Boarding.miz
#	Moose Test Missions/CGO - Cargo/CGO-002 - Unit Unboarding/CGO-002 -
Unit Unboarding.miz
#	Moose Test Missions/CGO - Cargo/CGO-003 - Unit Transferring/CGO-003 -
Unit Transferring.miz
#	Moose Test Missions/CGO - Cargo/CGO-101 - Group Boarding/CGO-101 -
Group Boarding.miz
#	Moose Test Missions/CGO - Cargo/CGO-102 - Group Unboarding/CGO-102 -
Group Unboarding.miz
#	Moose Test Missions/CGO - Cargo/CGO-103 - Group Transferring/CGO-103 -
Group Transferring.miz
#	Moose Test Missions/CGO - Cargo/CGO-201 - Package Boarding/CGO-201 -
Package Boarding.miz
#	Moose Test Missions/CGO - Cargo/CGO-202 - Package Unboarding/CGO-202 -
Package Unboarding.miz
#	Moose Test Missions/DET - Detection/DET-001 - Detection Areas/DET-001
- Detection Areas.miz
#	Moose Test Missions/DET - Detection/DET-101 - Detection
Reporting/DET-101 - Detection Reporting.miz
#	Moose Test Missions/ESC - Escorting/ESC-001 - Escorting
Helicopters/ESC-001 - Escorting Helicopters.miz
#	Moose Test Missions/EVT - Event Handling/EVT-001 - API Demo 1/EVT-001
- API Demo 1.miz
#	Moose Test Missions/EVT - Event Handling/EVT-100 - OnEventShot
Example/EVT-100 - OnEventShot Example.miz
#	Moose Test Missions/EVT - Event Handling/EVT-101 - OnEventHit
Example/EVT-101 - OnEventHit Example.miz
#	Moose Test Missions/EVT - Event Handling/EVT-102 - OnEventTakeoff
Example/EVT-102 - OnEventTakeoff Example.miz
#	Moose Test Missions/EVT - Event Handling/EVT-103 - OnEventLand
Example/EVT-103 - OnEventLand Example.miz
#	Moose Test Missions/EVT - Event Handling/EVT-104 - OnEventCrash
Example/EVT-104 - OnEventCrash Example.miz
#	Moose Test Missions/FSM - Finite State Machine/FSM-100 - Transition
Explanation/FSM-100 - Transition Explanation.miz
#	Moose Test Missions/GRP - Group Commands/GRP-200 - Follow
Group/GRP-200 - Follow Group.miz
#	Moose Test Missions/GRP - Group Commands/GRP-300 - Switch
WayPoints/GRP-300 - Switch WayPoints.miz
#	Moose Test Missions/GRP - Group Commands/Moose_Test_WRAPPER.miz
#	Moose Test Missions/MEN - Menu Options/MEN-001 - Menu Client/MEN-001 -
Menu Client.miz
#	Moose Test Missions/MEN - Menu Options/MEN-002 - Menu
Coalition/MEN-002 - Menu Coalition.miz
#	Moose Test Missions/MEN - Menu Options/MEN-003 - Menu Group/MEN-003 -
Menu Group.miz
#	Moose Test Missions/MIT - Missile Trainer/MIT-001 - Missile
Trainer/MIT-001 - Missile Trainer.miz
#	Moose Test Missions/MOOSE_Test_Template.miz
#	Moose Test Missions/PAT - Patrolling/PAT-001 - Switching Patrol
Zones/PAT-001 - Switching Patrol Zones.miz
#	Moose Test Missions/SCH - Scheduler/SCH-000 - Simple
Scheduling/SCH-000 - Simple Scheduling.miz
#	Moose Test Missions/SCH - Scheduler/SCH-001 - Simple Object
Scheduling/SCH-001 - Simple Object Scheduling.miz
#	Moose Test Missions/SCH - Scheduler/SCH-100 - Simple Repeat
Scheduling/SCH-100 - Simple Repeat Scheduling.miz
#	Moose Test Missions/SCH - Scheduler/SCH-110 - Object Repeat
Scheduling/SCH-110 - Object Repeat Scheduling.miz
#	Moose Test Missions/SCH - Scheduler/SCH-200 - Simple Repeat Scheduling
Stop and Start/SCH-200 - Simple Repeat Scheduling Stop and Start.miz
#	Moose Test Missions/SCH - Scheduler/SCH-300 - GC Simple Object
Scheduling/SCH-300 - GC Simple Object Scheduling.miz
#	Moose Test Missions/SCH - Scheduler/SCH-310 - GC Object Repeat
Scheduling/SCH-310 - GC Object Repeat Scheduling.miz
#	Moose Test Missions/SCO - Scoring/SCO-100 - Scoring of Statics/SCO-100
- Scoring of Statics.miz
#	Moose Test Missions/SCO - Scoring/SCO-101 - Scoring Client to
Client/SCO-101 - Scoring Client to Client.miz
#	Moose Test Missions/SET - Data Sets/SET-001 - Airbase Sets/SET-001 -
Airbase Sets.miz
#	Moose Test Missions/SET - Data Sets/SET-101 - Group Sets/SET-101 -
Group Sets.miz
#	Moose Test Missions/SET - Data Sets/SET-201 - Client Sets/SET-201 -
Client Sets.miz
#	Moose Test Missions/SEV - SEAD Evasion/SEV-001 - SEAD Evasion/SEV-001
- SEAD Evasion.miz
#	Moose Test Missions/SPA - Spawning/SPA-010 - Spawn Demo/SPA-010 -
Spawn Demo.miz
#	Moose Test Missions/SPA - Spawning/SPA-011 - Ground Ops - Simple
Spawning/SPA-011 - Ground Ops - Simple Spawning.miz
#	Moose Test Missions/SPA - Spawning/SPA-012 - Ground Ops - Multiple
Spawns/SPA-012 - Ground Ops - Multiple Spawns.miz
#	Moose Test Missions/SPA - Spawning/SPA-013 - Ground Ops - Scheduled
Spawns/SPA-013 - Ground Ops - Scheduled Spawns.miz
#	Moose Test Missions/SPA - Spawning/SPA-014 - Ground Ops - Scheduled
Spawns Limited/SPA-014 - Ground Ops - Scheduled Spawns Limited.miz
#	Moose Test Missions/SPA - Spawning/SPA-015 - Ground Ops - Randomize
Route/SPA-015 - Ground Ops - Randomize Route.miz
#	Moose Test Missions/SPA - Spawning/SPA-016 - Ground Ops - Randomize
Zones/SPA-016 - Ground Ops - Randomize Zones.miz
#	Moose Test Missions/SPA - Spawning/SPA-017 - Ground Ops - Set AI
inactive while spawning/SPA-017 - Ground Ops - Set AI inactive while
spawning.miz
#	Moose Test Missions/SPA - Spawning/SPA-018 - Ground Ops - Randomize
Templates/SPA-018 - Ground Ops - Randomize Templates.miz
#	Moose Test Missions/SPA - Spawning/SPA-100 - CleanUp Inactive
Units/SPA-100 - CleanUp Inactive Units.miz
#	Moose Test Missions/SPA - Spawning/SPA-110 - Limit Spawning/SPA-110 -
Limit Spawning.miz
#	Moose Test Missions/SPA - Spawning/SPA-121 - Air Ops - Scheduled
Spawns with Repeat on Landing with Limit/SPA-121 - Air Ops - Scheduled
Spawns with Repeat on Landing with Limit.miz
#	Moose Test Missions/SPA - Spawning/SPA-130 - Uncontrolled
Spawning/SPA-130 - Uncontrolled Spawning.miz
#	Moose Test Missions/SPA - Spawning/SPA-200 - Randomize Unit
Types/SPA-200 - Randomize Unit Types.miz
#	Moose Test Missions/SPA - Spawning/SPA-220 - Randomize Zones/SPA-220 -
Randomize Zones.miz
#	Moose Test Missions/SPA - Spawning/SPA-310 - Spawn at Static
position/SPA-310 - Spawn at Static position.miz
#	Moose Test Missions/SPA - Spawning/SPA-320 - Spawn at Unit
position/SPA-320 - Spawn at Unit position.miz
#	Moose Test Missions/SPA - Spawning/SPA-330 - Spawn at Vec2
position/SPA-330 - Spawn at Vec2 position.miz
#	Moose Test Missions/SPA - Spawning/SPA-340 - Spawn at Vec3
position/SPA-340 - Spawn at Vec3 position.miz
#	Moose Test Missions/TAD - Task Dispatching/TAD-010 - Task Dispatching
Demo/TAD-010 - Task Dispatching Demo.miz
#	Moose Test Missions/TSK - Task Modelling/TSK-010 - Task Modelling -
SEAD/TSK-010 - Task Modelling - SEAD.miz
#	Moose Test Missions/TSK - Task Modelling/TSK-020 - Task Modelling -
Pickup/TSK-020 - Task Modelling - Pickup.miz
#	Moose Test Missions/ZON - Zones/ZON-100 - Normal Zone/ZON-100 - Normal
Zone.miz
#	Moose Test Missions/ZON - Zones/ZON-200 - Group Zone/ZON-200 - Group
Zone.miz
#	Moose Test Missions/ZON - Zones/ZON-300 - Unit Zone/ZON-300 - Unit
Zone.miz
#	Moose Test Missions/ZON - Zones/ZON-400 - Radius Zone/ZON-400 - Radius
Zone.miz
#	Moose Test Missions/ZON - Zones/ZON-500 - Polygon Zone/ZON-500 -
Polygon Zone.miz
#	Moose Test Missions/ZON - Zones/ZON-510 - Send message if Clients fly
the first time in the Polygon Zones/ZON-510 - Send message if Clients
fly the first time in the Polygon Zones.miz
2017-03-05 07:45:08 +01:00
FlightControl
32e52731ae Detection Progress 2017-03-05 07:35:03 +01:00
FlightControl
b46d61d865 Got Task_SEAD template working with all the variations and complexity! 2017-03-04 08:51:31 +01:00
FlightControl
5ddeb8d396 Progress on SEAD 2017-03-03 12:34:52 +01:00
FlightControl
22fdf034ee Progress on SEAD 2017-03-03 10:49:38 +01:00
FlightControl
a99924d9ed Progress on SEAD model 2017-03-03 10:27:09 +01:00
FlightControl
846edca815 Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl
# Conflicts:
#	Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua
#	Moose Mission Setup/Moose.lua
#	Moose Test Missions/SPA - Spawning/SPA-010 - Spawn Demo/SPA-010 -
Spawn Demo.miz
#	Moose Test Missions/SPA - Spawning/SPA-011 - Ground Ops - Simple
Spawning/SPA-011 - Ground Ops - Simple Spawning.miz
#	Moose Test Missions/SPA - Spawning/SPA-012 - Ground Ops - Multiple
Spawns/SPA-012 - Ground Ops - Multiple Spawns.miz
#	Moose Test Missions/SPA - Spawning/SPA-013 - Ground Ops - Scheduled
Spawns/SPA-013 - Ground Ops - Scheduled Spawns.miz
#	Moose Test Missions/SPA - Spawning/SPA-014 - Ground Ops - Scheduled
Spawns Limited/SPA-014 - Ground Ops - Scheduled Spawns Limited.miz
#	Moose Test Missions/SPA - Spawning/SPA-015 - Ground Ops - Randomize
Route/SPA-015 - Ground Ops - Randomize Route.miz
#	Moose Test Missions/SPA - Spawning/SPA-016 - Ground Ops - Randomize
Zones/SPA-016 - Ground Ops - Randomize Zones.miz
#	Moose Test Missions/SPA - Spawning/SPA-017 - Ground Ops - Set AI
inactive while spawning/SPA-017 - Ground Ops - Set AI inactive while
spawning.miz
#	Moose Test Missions/SPA - Spawning/SPA-018 - Ground Ops - Randomize
Templates/SPA-018 - Ground Ops - Randomize Templates.miz
#	Moose Test Missions/SPA - Spawning/SPA-100 - CleanUp Inactive
Units/SPA-100 - CleanUp Inactive Units.miz
#	Moose Test Missions/SPA - Spawning/SPA-110 - Limit Spawning/SPA-110 -
Limit Spawning.miz
#	Moose Test Missions/SPA - Spawning/SPA-121 - Air Ops - Scheduled
Spawns with Repeat on Landing with Limit/SPA-121 - Air Ops - Scheduled
Spawns with Repeat on Landing with Limit.miz
#	Moose Test Missions/SPA - Spawning/SPA-130 - Uncontrolled
Spawning/SPA-130 - Uncontrolled Spawning.miz
#	Moose Test Missions/SPA - Spawning/SPA-200 - Randomize Unit
Types/SPA-200 - Randomize Unit Types.miz
#	Moose Test Missions/SPA - Spawning/SPA-220 - Randomize Zones/SPA-220 -
Randomize Zones.miz
#	Moose Test Missions/SPA - Spawning/SPA-310 - Spawn at Static
position/SPA-310 - Spawn at Static position.miz
#	Moose Test Missions/SPA - Spawning/SPA-320 - Spawn at Unit
position/SPA-320 - Spawn at Unit position.miz
#	Moose Test Missions/SPA - Spawning/SPA-330 - Spawn at Vec2
position/SPA-330 - Spawn at Vec2 position.miz
#	Moose Test Missions/SPA - Spawning/SPA-340 - Spawn at Vec3
position/SPA-340 - Spawn at Vec3 position.miz
#	Moose Test Missions/ZON - Zones/ZON-100 - Normal Zone/ZON-100 - Normal
Zone.miz
#	docs/Documentation/Zone.html
2017-03-03 09:51:17 +01:00
FlightControl
41eb39c2a5 Task Templates 2017-02-26 13:30:29 +01:00
Sven Van de Velde
a3aee1e154 Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl
# Conflicts:
#	Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua
#	Moose Mission Setup/Moose.lua
#	Moose Test Missions/ZON - Zones/ZON-100 - Normal Zone/ZON-100 - Normal
Zone.miz
#	Moose Test Missions/ZON - Zones/ZON-200 - Group Zone/ZON-200 - Group
Zone.miz
#	Moose Test Missions/ZON - Zones/ZON-300 - Unit Zone/ZON-300 - Unit
Zone.miz
#	Moose Test Missions/ZON - Zones/ZON-400 - Radius Zone/ZON-400 - Radius
Zone.miz
#	Moose Test Missions/ZON - Zones/ZON-500 - Polygon Zone/ZON-500 -
Polygon Zone.miz
#	Moose Test Missions/ZON - Zones/ZON-510 - Send message if Clients fly
the first time in the Polygon Zones/ZON-510 - Send message if Clients
fly the first time in the Polygon Zones.miz
2017-02-24 10:37:48 +01:00
Sven Van de Velde
8c4a6e2301 Progress 2017-02-21 11:09:40 +01:00
Sven Van de Velde
6d3a2b0f52 Progress 2017-02-20 14:43:55 +01:00
Sven Van de Velde
e56e81ee57 Progress SEAD Tasking 2017-02-20 14:38:50 +01:00
FlightControl
e243ff32fa Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl
# Conflicts:
#	Moose Development/Moose/Core/Zone.lua
#	Moose Development/ReleaseNotes.txt
#	Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua
#	Moose Mission Setup/Moose.lua
#	Moose Test Missions/ZON - Zones/ZON-100 - Normal Zone/ZON-100 - Normal
Zone.miz
#	Moose Test Missions/ZON - Zones/ZON-200 - Group Zone/ZON-200 - Group
Zone.miz
#	Moose Test Missions/ZON - Zones/ZON-300 - Unit Zone/ZON-300 - Unit
Zone.miz
#	Moose Test Missions/ZON - Zones/ZON-400 - Radius Zone/ZON-400 - Radius
Zone.miz
#	Moose Test Missions/ZON - Zones/ZON-500 - Polygon Zone/ZON-500 -
Polygon Zone.miz
#	Moose Test Missions/ZON - Zones/ZON-510 - Send message if Clients fly
the first time in the Polygon Zones/ZON-510 - Send message if Clients
fly the first time in the Polygon Zones.miz
#	docs/Documentation/Zone.html
2017-02-19 13:43:44 +01:00
FlightControl
4ff9766690 Analysis of TASK_SEAD 2017-02-19 13:32:43 +01:00
FlightControl
f9708de598 Progress 2017-02-18 19:51:20 +01:00
FlightControl
5c090b108c Merge remote-tracking branch 'refs/remotes/origin/master' into FlightControl
# Conflicts:
#	Moose Mission Setup/Moose Mission Update/l10n/DEFAULT/Moose.lua
#	Moose Mission Setup/Moose.lua
#	Moose Test Missions/ABP - Airbase Police/APL-001 - Caucasus/APL-001 -
Caucasus.miz
#	Moose Test Missions/ABP - Airbase Police/APL-002 - Nevada/APL-002 -
Nevada.miz
#	Moose Test Missions/ACL - Airbase Cleaner/ACL-001 - Airbase
CleanUp/ACL-001 - Airbase CleanUp.miz
#	Moose Test Missions/AIB - AI Balancing/AIB-001 - Spawned AI/AIB-001 -
Spawned AI.miz
#	Moose Test Missions/AIB - AI Balancing/AIB-002 - Patrol AI/AIB-002 -
Patrol AI.miz
#	Moose Test Missions/AIB - AI Balancing/AIB-003 - Two coalitions
InitCleanUp test/AIB-003 - Two coalitions InitCleanUp test.miz
#	Moose Test Missions/AIB - AI Balancing/AIB-004 - Respawn Test when
Destroyed/AIB-004 - Respawn Test when Destroyed.miz
#	Moose Test Missions/AIB - AI Balancing/AIB-005 - Patrol AI and
Randomize Zones/AIB-005 - Patrol AI and Randomize Zones.miz
#	Moose Test Missions/AIB - AI Balancing/AIB-006 - Declutter AI at
Airbases/AIB-006 - Declutter AI at Airbases.miz
#	Moose Test Missions/CAP - Combat Air Patrol/CAP-001 - Combat Air
Patrol/CAP-001 - Combat Air Patrol.miz
#	Moose Test Missions/CAP - Combat Air Patrol/CAP-010 - CAP and Engage
within Range/CAP-010 - CAP and Engage within Range.miz
#	Moose Test Missions/CAP - Combat Air Patrol/CAP-011 - CAP and Engage
within Zone/CAP-011 - CAP and Engage within Zone.miz
#	Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by
Airplane Group/CAS-001 - CAS in a ZONE-ME Test.miz
#	Moose Test Missions/CAS - Close Air Support/CAS-001 - CAS in a Zone by
Airplane Group/CAS-001 - CAS in a Zone by Airplane Group.miz
#	Moose Test Missions/CAS - Close Air Support/CAS-002 - CAS in a Zone by
Airplane Group - Engage with Speed/CAS-002 - CAS in a Zone by Airplane
Group - Engage with Speed.miz
#	Moose Test Missions/CAS - Close Air Support/CAS-003 - CAS in a Zone by
Airplane Group - Engage with Speed and Altitude/CAS-003 - CAS in a Zone
by Airplane Group - Engage with Speed and Altitude.miz
#	Moose Test Missions/CAS - Close Air Support/CAS-010 - CAS in a Zone by
Helicopter/CAS-010 - CAS in a Zone by Helicopter.miz
#	Moose Test Missions/CAS - Close Air Support/CAS-011 - CAS in a Zone by
Helicopter Group/CAS-011 - CAS in a Zone by Helicopter Group.miz
#	Moose Test Missions/CAS - Close Air Support/CAS-111 - Multiple CAS in
1 Radius Zone by Helicopter and AirPlane Groups/CAS-111 - Multiple CAS
in 1 Radius Zone by Helicopter and AirPlane Groups.miz
#	Moose Test Missions/CGO - Cargo/CGO-001 - Unit Boarding/CGO-001 - Unit
Boarding.miz
#	Moose Test Missions/CGO - Cargo/CGO-002 - Unit Unboarding/CGO-002 -
Unit Unboarding.miz
#	Moose Test Missions/CGO - Cargo/CGO-003 - Unit Transferring/CGO-003 -
Unit Transferring.miz
#	Moose Test Missions/CGO - Cargo/CGO-101 - Group Boarding/CGO-101 -
Group Boarding.miz
#	Moose Test Missions/CGO - Cargo/CGO-102 - Group Unboarding/CGO-102 -
Group Unboarding.miz
#	Moose Test Missions/CGO - Cargo/CGO-103 - Group Transferring/CGO-103 -
Group Transferring.miz
#	Moose Test Missions/CGO - Cargo/CGO-201 - Package Boarding/CGO-201 -
Package Boarding.miz
#	Moose Test Missions/CGO - Cargo/CGO-202 - Package Unboarding/CGO-202 -
Package Unboarding.miz
#	Moose Test Missions/DET - Detection/DET-001 - Detection Areas/DET-001
- Detection Areas.miz
#	Moose Test Missions/DET - Detection/DET-101 - Detection
Reporting/DET-101 - Detection Reporting.miz
#	Moose Test Missions/ESC - Escorting/ESC-001 - Escorting
Helicopters/ESC-001 - Escorting Helicopters.miz
#	Moose Test Missions/EVT - Event Handling/EVT-001 - API Demo 1/EVT-001
- API Demo 1.miz
#	Moose Test Missions/EVT - Event Handling/EVT-100 - OnEventShot
Example/EVT-100 - OnEventShot Example.miz
#	Moose Test Missions/EVT - Event Handling/EVT-101 - OnEventHit
Example/EVT-101 - OnEventHit Example.miz
#	Moose Test Missions/EVT - Event Handling/EVT-102 - OnEventTakeoff
Example/EVT-102 - OnEventTakeoff Example.miz
#	Moose Test Missions/EVT - Event Handling/EVT-103 - OnEventLand
Example/EVT-103 - OnEventLand Example.miz
#	Moose Test Missions/EVT - Event Handling/EVT-104 - OnEventCrash
Example/EVT-104 - OnEventCrash Example.miz
#	Moose Test Missions/FSM - Finite State Machine/FSM-100 - Transition
Explanation/FSM-100 - Transition Explanation.miz
#	Moose Test Missions/GRP - Group Commands/GRP-200 - Follow
Group/GRP-200 - Follow Group.miz
#	Moose Test Missions/GRP - Group Commands/GRP-300 - Switch
WayPoints/GRP-300 - Switch WayPoints.miz
#	Moose Test Missions/GRP - Group Commands/Moose_Test_WRAPPER.miz
#	Moose Test Missions/MEN - Menu Options/MEN-001 - Menu Client/MEN-001 -
Menu Client.miz
#	Moose Test Missions/MEN - Menu Options/MEN-002 - Menu
Coalition/MEN-002 - Menu Coalition.miz
#	Moose Test Missions/MEN - Menu Options/MEN-003 - Menu Group/MEN-003 -
Menu Group.miz
#	Moose Test Missions/MIT - Missile Trainer/MIT-001 - Missile
Trainer/MIT-001 - Missile Trainer.miz
#	Moose Test Missions/MOOSE_Test_Template.miz
#	Moose Test Missions/PAT - Patrolling/PAT-001 - Switching Patrol
Zones/PAT-001 - Switching Patrol Zones.miz
#	Moose Test Missions/SCH - Scheduler/SCH-000 - Simple
Scheduling/SCH-000 - Simple Scheduling.miz
#	Moose Test Missions/SCH - Scheduler/SCH-001 - Simple Object
Scheduling/SCH-001 - Simple Object Scheduling.miz
#	Moose Test Missions/SCH - Scheduler/SCH-100 - Simple Repeat
Scheduling/SCH-100 - Simple Repeat Scheduling.miz
#	Moose Test Missions/SCH - Scheduler/SCH-110 - Object Repeat
Scheduling/SCH-110 - Object Repeat Scheduling.miz
#	Moose Test Missions/SCH - Scheduler/SCH-200 - Simple Repeat Scheduling
Stop and Start/SCH-200 - Simple Repeat Scheduling Stop and Start.miz
#	Moose Test Missions/SCH - Scheduler/SCH-300 - GC Simple Object
Scheduling/SCH-300 - GC Simple Object Scheduling.miz
#	Moose Test Missions/SCH - Scheduler/SCH-310 - GC Object Repeat
Scheduling/SCH-310 - GC Object Repeat Scheduling.miz
#	Moose Test Missions/SET - Data Sets/SET-001 - Airbase Sets/SET-001 -
Airbase Sets.miz
#	Moose Test Missions/SET - Data Sets/SET-101 - Group Sets/SET-101 -
Group Sets.miz
#	Moose Test Missions/SET - Data Sets/SET-201 - Client Sets/SET-201 -
Client Sets.miz
#	Moose Test Missions/SEV - SEAD Evasion/SEV-001 - SEAD Evasion/SEV-001
- SEAD Evasion.miz
#	Moose Test Missions/SPA - Spawning/SPA-010 - Spawn Demo/SPA-010 -
Spawn Demo.miz
#	Moose Test Missions/SPA - Spawning/SPA-011 - Ground Ops - Simple
Spawning/SPA-011 - Ground Ops - Simple Spawning.miz
#	Moose Test Missions/SPA - Spawning/SPA-012 - Ground Ops - Multiple
Spawns/SPA-012 - Ground Ops - Multiple Spawns.miz
#	Moose Test Missions/SPA - Spawning/SPA-013 - Ground Ops - Scheduled
Spawns/SPA-013 - Ground Ops - Scheduled Spawns.miz
#	Moose Test Missions/SPA - Spawning/SPA-014 - Ground Ops - Scheduled
Spawns Limited/SPA-014 - Ground Ops - Scheduled Spawns Limited.miz
#	Moose Test Missions/SPA - Spawning/SPA-015 - Ground Ops - Randomize
Route/SPA-015 - Ground Ops - Randomize Route.miz
#	Moose Test Missions/SPA - Spawning/SPA-016 - Ground Ops - Randomize
Zones/SPA-016 - Ground Ops - Randomize Zones.miz
#	Moose Test Missions/SPA - Spawning/SPA-017 - Ground Ops - Set AI
inactive while spawning/SPA-017 - Ground Ops - Set AI inactive while
spawning.miz
#	Moose Test Missions/SPA - Spawning/SPA-018 - Ground Ops - Randomize
Templates/SPA-018 - Ground Ops - Randomize Templates.miz
#	Moose Test Missions/SPA - Spawning/SPA-100 - CleanUp Inactive
Units/SPA-100 - CleanUp Inactive Units.miz
#	Moose Test Missions/SPA - Spawning/SPA-110 - Limit Spawning/SPA-110 -
Limit Spawning.miz
#	Moose Test Missions/SPA - Spawning/SPA-120 - Repeat Spawning/SPA-120 -
Repeat Spawning.miz
#	Moose Test Missions/SPA - Spawning/SPA-121 - Air Ops - Scheduled
Spawns with Repeat on Landing with Limit/SPA-121 - Air Ops - Scheduled
Spawns with Repeat on Landing with Limit.miz
#	Moose Test Missions/SPA - Spawning/SPA-130 - Uncontrolled
Spawning/SPA-130 - Uncontrolled Spawning.miz
#	Moose Test Missions/SPA - Spawning/SPA-200 - Randomize Unit
Types/SPA-200 - Randomize Unit Types.miz
#	Moose Test Missions/SPA - Spawning/SPA-220 - Randomize Zones/SPA-220 -
Randomize Zones.miz
#	Moose Test Missions/SPA - Spawning/SPA-310 - Spawn at Static
position/SPA-310 - Spawn at Static position.miz
#	Moose Test Missions/SPA - Spawning/SPA-320 - Spawn at Unit
position/SPA-320 - Spawn at Unit position.miz
#	Moose Test Missions/SPA - Spawning/SPA-330 - Spawn at Vec2
position/SPA-330 - Spawn at Vec2 position.miz
#	Moose Test Missions/SPA - Spawning/SPA-340 - Spawn at Vec3
position/SPA-340 - Spawn at Vec3 position.miz
#	Moose Test Missions/TAD - Task Dispatching/TAD-010 - Task Dispatching
Demo/TAD-010 - Task Dispatching Demo.miz
#	Moose Test Missions/TSK - Task Modelling/TSK-010 - Task Modelling -
SEAD/TSK-010 - Task Modelling - SEAD.miz
#	Moose Test Missions/TSK - Task Modelling/TSK-020 - Task Modelling -
Pickup/TSK-020 - Task Modelling - Pickup.miz
#	Moose Test Missions/ZON - Zones/ZON-100 - Normal Zone/ZON-100 - Normal
Zone.miz
#	Moose Test Missions/ZON - Zones/ZON-200 - Group Zone/ZON-200 - Group
Zone.miz
#	Moose Test Missions/ZON - Zones/ZON-300 - Unit Zone/ZON-300 - Unit
Zone.miz
#	Moose Test Missions/ZON - Zones/ZON-400 - Radius Zone/ZON-400 - Radius
Zone.miz
#	Moose Test Missions/ZON - Zones/ZON-500 - Polygon Zone/ZON-500 -
Polygon Zone.miz
#	Moose Test Missions/ZON - Zones/ZON-510 - Send message if Clients fly
the first time in the Polygon Zones/ZON-510 - Send message if Clients
fly the first time in the Polygon Zones.miz
2017-02-15 17:44:34 +01:00
FlightControl
62b668029b Updates 2017-02-14 08:49:59 +01:00
FlightControl
a47195198e Fixes and framework stabilization 2017-02-08 22:40:26 +01:00
886 changed files with 136954 additions and 102098 deletions

6
.gitignore vendored
View File

@@ -5,7 +5,6 @@
*.pydevproject
.project
.metadata
bin/
tmp/
*.tmp
*.bak
@@ -19,8 +18,6 @@ local.properties
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# CDT-specific
.cproject
@@ -47,7 +44,6 @@ local.properties
[Rr]elease/
x64/
build/
[Bb]in/
[Oo]bj/
# MSTest test Results
@@ -224,3 +220,5 @@ _gsdata_/
#GITHUB
.gitattributes
.gitignore
Moose Test Missions/MOOSE_Test_Template.miz
Moose.lua

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.ui.externaltools.ProgramLaunchConfigurationType">
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.ui.externaltools.launchGroup"/>
</listAttribute>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/Moose_Framework/Utils/GenerateDocumentations.bat}"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/Moose_Framework/Utils}"/>
</launchConfiguration>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.ui.externaltools.ProgramLaunchConfigurationType">
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.ui.externaltools.launchGroup"/>
</listAttribute>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/Moose_Framework/Utils/luarocks/lua5.1.exe}"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value="&quot;Moose_Create.lua&quot; &#13;&#10;&quot;D&quot;&#13;&#10;&quot;${current_date}&quot; &#13;&#10;&quot;${workspace_loc:/Moose_Framework//Moose Development/Moose}&quot; &#13;&#10;&quot;${workspace_loc:/Moose_Framework/Moose Mission Setup}&quot;"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/Moose_Framework/Moose Mission Setup}"/>
</launchConfiguration>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.ui.externaltools.ProgramLaunchConfigurationType">
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.ui.externaltools.launchGroup"/>
</listAttribute>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/Moose_Framework/Utils/luarocks/lua5.1.exe}"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value="&quot;Moose_Create.lua&quot; &#13;&#10;&quot;S&quot;&#13;&#10;&quot;${current_date}&quot; &#13;&#10;&quot;${workspace_loc:/Moose_Framework//Moose Development/Moose}&quot; &#13;&#10;&quot;${workspace_loc:/Moose_Framework/Moose Mission Setup}&quot;"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/Moose_Framework/Moose Mission Setup}"/>
</launchConfiguration>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.ui.externaltools.ProgramLaunchConfigurationType">
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.ui.externaltools.launchGroup"/>
</listAttribute>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/Moose_Framework/Moose Mission Setup/Moose Mission Update/Moose_Update_Missions.bat}"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/Moose_Missions}"/>
</launchConfiguration>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.ui.externaltools.ProgramLaunchConfigurationType">
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.ui.externaltools.launchGroup"/>
</listAttribute>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/Moose_Framework/Moose Mission Setup/Moose Mission Update/Moose_Update_Missions.bat}"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value="&quot;${selected_resource_loc}&quot;"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/Moose_Framework/Moose Mission Setup/Moose Mission Update}"/>
</launchConfiguration>

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,674 @@
--- **AI** -- **Provide Battlefield Air Interdiction (bombing).**
--
-- ![Banner Image](..\Presentations\AI_BAI\Dia1.JPG)
--
-- ===
--
-- AI_BAI classes makes AI Controllables execute bombing tasks.
--
-- There are the following types of BAI classes defined:
--
-- * @{#AI_BAI_ZONE}: Perform a BAI in a zone.
--
-- ====
--
-- # Demo Missions
--
-- ### [AI_BAI Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/BOMB%20-%20Close%20Air%20Support)
--
-- ### [AI_BAI Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/BOMB%20-%20Close%20Air%20Support)
--
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
--
-- ====
--
-- # YouTube Channel
--
-- ### [AI_BAI YouTube Channel](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl3JBO1WDqqpyYRRmIkR2ir2)
--
-- ===
--
-- # **API CHANGE HISTORY**
--
-- The underlying change log documents the API changes. Please read this carefully. The following notation is used:
--
-- * **Added** parts are expressed in bold type face.
-- * _Removed_ parts are expressed in italic type face.
--
-- Hereby the change log:
--
-- 2017-01-15: Initial class and API.
--
-- ===
--
-- # **AUTHORS and CONTRIBUTIONS**
--
-- ### Contributions:
--
-- * **[Gunterlund](http://forums.eagle.ru:8080/member.php?u=75036)**: Test case revision.
--
-- ### Authors:
--
-- * **FlightControl**: Concept, Design & Programming.
--
-- @module AI_Bai
--- AI_BAI_ZONE class
-- @type AI_BAI_ZONE
-- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling.
-- @field Core.Zone#ZONE_BASE TargetZone The @{Zone} where the patrol needs to be executed.
-- @extends AI.AI_Patrol#AI_PATROL_ZONE
--- # AI_BAI_ZONE class, extends @{AI_Patrol#AI_PATROL_ZONE}
--
-- AI_BAI_ZONE derives from the @{AI_Patrol#AI_PATROL_ZONE}, inheriting its methods and behaviour.
--
-- The AI_BAI_ZONE class implements the core functions to provide BattleGround Air Interdiction in an Engage @{Zone} by an AIR @{Controllable} or @{Group}.
-- The AI_BAI_ZONE runs a process. It holds an AI in a Patrol Zone and when the AI is commanded to engage, it will fly to an Engage Zone.
--
-- ![HoldAndEngage](..\Presentations\AI_BAI\Dia3.JPG)
--
-- The AI_BAI_ZONE is assigned a @{Group} and this must be done before the AI_BAI_ZONE process can be started through the **Start** event.
--
-- ![Start Event](..\Presentations\AI_BAI\Dia4.JPG)
--
-- Upon started, The AI will **Route** itself towards the random 3D point within a patrol zone,
-- using a random speed within the given altitude and speed limits.
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
-- This cycle will continue until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB.
--
-- ![Route Event](..\Presentations\AI_BAI\Dia5.JPG)
--
-- When the AI is commanded to provide BattleGround Air Interdiction (through the event **Engage**), the AI will fly towards the Engage Zone.
-- Any target that is detected in the Engage Zone will be reported and will be destroyed by the AI.
--
-- ![Engage Event](..\Presentations\AI_BAI\Dia6.JPG)
--
-- The AI will detect the targets and will only destroy the targets within the Engage Zone.
--
-- ![Engage Event](..\Presentations\AI_BAI\Dia7.JPG)
--
-- Every target that is destroyed, is reported< by the AI.
--
-- ![Engage Event](..\Presentations\AI_BAI\Dia8.JPG)
--
-- Note that the AI does not know when the Engage Zone is cleared, and therefore will keep circling in the zone.
--
-- ![Engage Event](..\Presentations\AI_BAI\Dia9.JPG)
--
-- Until it is notified through the event **Accomplish**, which is to be triggered by an observing party:
--
-- * a FAC
-- * a timed event
-- * a menu option selected by a human
-- * a condition
-- * others ...
--
-- ![Engage Event](..\Presentations\AI_BAI\Dia10.JPG)
--
-- When the AI has accomplished the Bombing, it will fly back to the Patrol Zone.
--
-- ![Engage Event](..\Presentations\AI_BAI\Dia11.JPG)
--
-- It will keep patrolling there, until it is notified to RTB or move to another BOMB Zone.
-- It can be notified to go RTB through the **RTB** event.
--
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
--
-- ![Engage Event](..\Presentations\AI_BAI\Dia12.JPG)
--
-- # 1. AI_BAI_ZONE constructor
--
-- * @{#AI_BAI_ZONE.New}(): Creates a new AI_BAI_ZONE object.
--
-- ## 2. AI_BAI_ZONE is a FSM
--
-- ![Process](..\Presentations\AI_BAI\Dia2.JPG)
--
-- ### 2.1. AI_BAI_ZONE States
--
-- * **None** ( Group ): The process is not started yet.
-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone.
-- * **Engaging** ( Group ): The AI is engaging the targets in the Engage Zone, executing BOMB.
-- * **Returning** ( Group ): The AI is returning to Base..
--
-- ### 2.2. AI_BAI_ZONE Events
--
-- * **@{AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process.
-- * **@{AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone.
-- * **@{#AI_BAI_ZONE.Engage}**: Engage the AI to provide BOMB in the Engage Zone, destroying any target it finds.
-- * **@{#AI_BAI_ZONE.Abort}**: Aborts the engagement and return patrolling in the patrol zone.
-- * **@{AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base.
-- * **@{AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets.
-- * **@{AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
-- * **@{#AI_BAI_ZONE.Destroy}**: The AI has destroyed a target @{Unit}.
-- * **@{#AI_BAI_ZONE.Destroyed}**: The AI has destroyed all target @{Unit}s assigned in the BOMB task.
-- * **Status**: The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB.
--
-- ## 3. Modify the Engage Zone behaviour to pinpoint a **map object** or **scenery object**
--
-- Use the method @{#AI_BAI_ZONE.SearchOff}() to specify that the EngageZone is not to be searched for potential targets (UNITs), but that the center of the zone
-- is the point where a map object is to be destroyed (like a bridge).
--
-- Example:
--
-- -- Tell the BAI not to search for potential targets in the BAIEngagementZone, but rather use the center of the BAIEngagementZone as the bombing location.
-- AIBAIZone:SearchOff()
--
-- Searching can be switched back on with the method @{#AI_BAI_ZONE.SearchOn}(). Use the method @{#AI_BAI_ZONE.SearchOnOff}() to flexibily switch searching on or off.
--
-- ===
--
-- @field #AI_BAI_ZONE
AI_BAI_ZONE = {
ClassName = "AI_BAI_ZONE",
}
--- Creates a new AI_BAI_ZONE object
-- @param #AI_BAI_ZONE self
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed.
-- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h.
-- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h.
-- @param Core.Zone#ZONE_BASE EngageZone The zone where the engage will happen.
-- @param Dcs.DCSTypes#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
-- @return #AI_BAI_ZONE self
function AI_BAI_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageZone, PatrolAltType )
-- Inherits from BASE
local self = BASE:Inherit( self, AI_PATROL_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) ) -- #AI_BAI_ZONE
self.EngageZone = EngageZone
self.Accomplished = false
self:SetDetectionZone( self.EngageZone )
self:SearchOn()
self:AddTransition( { "Patrolling", "Engaging" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_BAI_ZONE.
--- OnBefore Transition Handler for Event Engage.
-- @function [parent=#AI_BAI_ZONE] OnBeforeEngage
-- @param #AI_BAI_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Engage.
-- @function [parent=#AI_BAI_ZONE] OnAfterEngage
-- @param #AI_BAI_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Engage.
-- @function [parent=#AI_BAI_ZONE] Engage
-- @param #AI_BAI_ZONE self
-- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone.
-- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement.
-- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack.
-- If parameter is not defined the unit / controllable will choose expend on its own discretion.
-- Use the structure @{DCSTypes#AI.Task.WeaponExpend} to define the amount of weapons to be release at each attack.
-- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.
-- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.
--- Asynchronous Event Trigger for Event Engage.
-- @function [parent=#AI_BAI_ZONE] __Engage
-- @param #AI_BAI_ZONE self
-- @param #number Delay The delay in seconds.
-- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone.
-- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement.
-- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack.
-- If parameter is not defined the unit / controllable will choose expend on its own discretion.
-- Use the structure @{DCSTypes#AI.Task.WeaponExpend} to define the amount of weapons to be release at each attack.
-- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.
-- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.
--- OnLeave Transition Handler for State Engaging.
-- @function [parent=#AI_BAI_ZONE] OnLeaveEngaging
-- @param #AI_BAI_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnEnter Transition Handler for State Engaging.
-- @function [parent=#AI_BAI_ZONE] OnEnterEngaging
-- @param #AI_BAI_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
self:AddTransition( "Engaging", "Target", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_BAI_ZONE.
self:AddTransition( "Engaging", "Fired", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_BAI_ZONE.
--- OnBefore Transition Handler for Event Fired.
-- @function [parent=#AI_BAI_ZONE] OnBeforeFired
-- @param #AI_BAI_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Fired.
-- @function [parent=#AI_BAI_ZONE] OnAfterFired
-- @param #AI_BAI_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Fired.
-- @function [parent=#AI_BAI_ZONE] Fired
-- @param #AI_BAI_ZONE self
--- Asynchronous Event Trigger for Event Fired.
-- @function [parent=#AI_BAI_ZONE] __Fired
-- @param #AI_BAI_ZONE self
-- @param #number Delay The delay in seconds.
self:AddTransition( "*", "Destroy", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_BAI_ZONE.
--- OnBefore Transition Handler for Event Destroy.
-- @function [parent=#AI_BAI_ZONE] OnBeforeDestroy
-- @param #AI_BAI_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Destroy.
-- @function [parent=#AI_BAI_ZONE] OnAfterDestroy
-- @param #AI_BAI_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Destroy.
-- @function [parent=#AI_BAI_ZONE] Destroy
-- @param #AI_BAI_ZONE self
--- Asynchronous Event Trigger for Event Destroy.
-- @function [parent=#AI_BAI_ZONE] __Destroy
-- @param #AI_BAI_ZONE self
-- @param #number Delay The delay in seconds.
self:AddTransition( "Engaging", "Abort", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_BAI_ZONE.
--- OnBefore Transition Handler for Event Abort.
-- @function [parent=#AI_BAI_ZONE] OnBeforeAbort
-- @param #AI_BAI_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Abort.
-- @function [parent=#AI_BAI_ZONE] OnAfterAbort
-- @param #AI_BAI_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Abort.
-- @function [parent=#AI_BAI_ZONE] Abort
-- @param #AI_BAI_ZONE self
--- Asynchronous Event Trigger for Event Abort.
-- @function [parent=#AI_BAI_ZONE] __Abort
-- @param #AI_BAI_ZONE self
-- @param #number Delay The delay in seconds.
self:AddTransition( "Engaging", "Accomplish", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_BAI_ZONE.
--- OnBefore Transition Handler for Event Accomplish.
-- @function [parent=#AI_BAI_ZONE] OnBeforeAccomplish
-- @param #AI_BAI_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Accomplish.
-- @function [parent=#AI_BAI_ZONE] OnAfterAccomplish
-- @param #AI_BAI_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Accomplish.
-- @function [parent=#AI_BAI_ZONE] Accomplish
-- @param #AI_BAI_ZONE self
--- Asynchronous Event Trigger for Event Accomplish.
-- @function [parent=#AI_BAI_ZONE] __Accomplish
-- @param #AI_BAI_ZONE self
-- @param #number Delay The delay in seconds.
return self
end
--- Set the Engage Zone where the AI is performing BOMB. Note that if the EngageZone is changed, the AI needs to re-detect targets.
-- @param #AI_BAI_ZONE self
-- @param Core.Zone#ZONE EngageZone The zone where the AI is performing BOMB.
-- @return #AI_BAI_ZONE self
function AI_BAI_ZONE:SetEngageZone( EngageZone )
self:F2()
if EngageZone then
self.EngageZone = EngageZone
else
self.EngageZone = nil
end
end
--- Specifies whether to search for potential targets in the zone, or let the center of the zone be the bombing coordinate.
-- AI_BAI_ZONE will search for potential targets by default.
-- @param #AI_BAI_ZONE self
-- @return #AI_BAI_ZONE
function AI_BAI_ZONE:SearchOnOff( Search )
self.Search = Search
return self
end
--- If Search is Off, the current zone coordinate will be the center of the bombing.
-- @param #AI_BAI_ZONE self
-- @return #AI_BAI_ZONE
function AI_BAI_ZONE:SearchOff()
self:SearchOnOff( false )
return self
end
--- If Search is On, BAI will search for potential targets in the zone.
-- @param #AI_BAI_ZONE self
-- @return #AI_BAI_ZONE
function AI_BAI_ZONE:SearchOn()
self:SearchOnOff( true )
return self
end
--- onafter State Transition for Event Start.
-- @param #AI_BAI_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_BAI_ZONE:onafterStart( Controllable, From, Event, To )
-- Call the parent Start event handler
self:GetParent(self).onafterStart( self, Controllable, From, Event, To )
self:HandleEvent( EVENTS.Dead )
self:SetDetectionDeactivated() -- When not engaging, set the detection off.
end
--- @param Wrapper.Controllable#CONTROLLABLE AIControllable
function _NewEngageRoute( AIControllable )
AIControllable:T( "NewEngageRoute" )
local EngageZone = AIControllable:GetState( AIControllable, "EngageZone" ) -- AI.AI_BAI#AI_BAI_ZONE
EngageZone:__Engage( 1, EngageZone.EngageSpeed, EngageZone.EngageAltitude, EngageZone.EngageWeaponExpend, EngageZone.EngageAttackQty, EngageZone.EngageDirection )
end
--- @param #AI_BAI_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_BAI_ZONE:onbeforeEngage( Controllable, From, Event, To )
if self.Accomplished == true then
return false
end
end
--- @param #AI_BAI_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_BAI_ZONE:onafterTarget( Controllable, From, Event, To )
self:F({"onafterTarget",self.Search,Controllable:IsAlive()})
if Controllable:IsAlive() then
local AttackTasks = {}
if self.Search == true then
for DetectedUnit, Detected in pairs( self.DetectedUnits ) do
local DetectedUnit = DetectedUnit -- Wrapper.Unit#UNIT
if DetectedUnit:IsAlive() then
if DetectedUnit:IsInZone( self.EngageZone ) then
if Detected == true then
self:F( {"Target: ", DetectedUnit } )
self.DetectedUnits[DetectedUnit] = false
local AttackTask = Controllable:TaskAttackUnit( DetectedUnit, false, self.EngageWeaponExpend, self.EngageAttackQty, self.EngageDirection, self.EngageAltitude, nil )
self.Controllable:PushTask( AttackTask, 1 )
end
end
else
self.DetectedUnits[DetectedUnit] = nil
end
end
else
self:F("Attack zone")
local AttackTask = Controllable:TaskAttackMapObject(
self.EngageZone:GetPointVec2():GetVec2(),
true,
self.EngageWeaponExpend,
self.EngageAttackQty,
self.EngageDirection,
self.EngageAltitude
)
self.Controllable:PushTask( AttackTask, 1 )
end
self:__Target( -10 )
end
end
--- @param #AI_BAI_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_BAI_ZONE:onafterAbort( Controllable, From, Event, To )
Controllable:ClearTasks()
self:__Route( 1 )
end
--- @param #AI_BAI_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone.
-- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement.
-- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion.
-- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.
-- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.
function AI_BAI_ZONE:onafterEngage( Controllable, From, Event, To,
EngageSpeed,
EngageAltitude,
EngageWeaponExpend,
EngageAttackQty,
EngageDirection )
self:F("onafterEngage")
self.EngageSpeed = EngageSpeed or 400
self.EngageAltitude = EngageAltitude or 2000
self.EngageWeaponExpend = EngageWeaponExpend
self.EngageAttackQty = EngageAttackQty
self.EngageDirection = EngageDirection
if Controllable:IsAlive() then
local EngageRoute = {}
--- Calculate the current route point.
local CurrentVec2 = self.Controllable:GetVec2()
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1).
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
local ToEngageZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:RoutePointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
self.EngageSpeed,
true
)
EngageRoute[#EngageRoute+1] = CurrentRoutePoint
local AttackTasks = {}
if self.Search == true then
for DetectedUnitID, DetectedUnitData in pairs( self.DetectedUnits ) do
local DetectedUnit = DetectedUnitData -- Wrapper.Unit#UNIT
self:T( DetectedUnit )
if DetectedUnit:IsAlive() then
if DetectedUnit:IsInZone( self.EngageZone ) then
self:F( {"Engaging ", DetectedUnit } )
AttackTasks[#AttackTasks+1] = Controllable:TaskBombing(
DetectedUnit:GetPointVec2():GetVec2(),
true,
EngageWeaponExpend,
EngageAttackQty,
EngageDirection,
EngageAltitude
)
end
else
self.DetectedUnits[DetectedUnit] = nil
end
end
else
self:F("Attack zone")
AttackTasks[#AttackTasks+1] = Controllable:TaskAttackMapObject(
self.EngageZone:GetPointVec2():GetVec2(),
true,
EngageWeaponExpend,
EngageAttackQty,
EngageDirection,
EngageAltitude
)
end
EngageRoute[#EngageRoute].task = Controllable:TaskCombo( AttackTasks )
--- Define a random point in the @{Zone}. The AI will fly to that point within the zone.
--- Find a random 2D point in EngageZone.
local ToTargetVec2 = self.EngageZone:GetRandomVec2()
self:T2( ToTargetVec2 )
--- Obtain a 3D @{Point} from the 2D point + altitude.
local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, self.EngageAltitude, ToTargetVec2.y )
--- Create a route point of type air.
local ToTargetRoutePoint = ToTargetPointVec3:RoutePointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
self.EngageSpeed,
true
)
EngageRoute[#EngageRoute+1] = ToTargetRoutePoint
Controllable:OptionROEOpenFire()
Controllable:OptionROTVertical()
--- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable...
Controllable:WayPointInitialize( EngageRoute )
--- Do a trick, link the NewEngageRoute function of the object to the AIControllable in a temporary variable ...
Controllable:SetState( Controllable, "EngageZone", self )
Controllable:WayPointFunction( #EngageRoute, 1, "_NewEngageRoute" )
--- NOW ROUTE THE GROUP!
Controllable:WayPointExecute( 1 )
self:SetDetectionInterval( 2 )
self:SetDetectionActivated()
self:__Target( -2 ) -- Start Targetting
end
end
--- @param #AI_BAI_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_BAI_ZONE:onafterAccomplish( Controllable, From, Event, To )
self.Accomplished = true
self:SetDetectionDeactivated()
end
--- @param #AI_BAI_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param Core.Event#EVENTDATA EventData
function AI_BAI_ZONE:onafterDestroy( Controllable, From, Event, To, EventData )
if EventData.IniUnit then
self.DetectedUnits[EventData.IniUnit] = nil
end
end
--- @param #AI_BAI_ZONE self
-- @param Core.Event#EVENTDATA EventData
function AI_BAI_ZONE:OnEventDead( EventData )
self:F( { "EventDead", EventData } )
if EventData.IniDCSUnit then
if self.DetectedUnits and self.DetectedUnits[EventData.IniUnit] then
self:__Destroy( 1, EventData )
end
end
end

View File

@@ -4,62 +4,22 @@
--
-- ![Banner Image](..\Presentations\AI_Balancer\Dia1.JPG)
--
-- ===
-- ====
--
-- # 1) @{AI_Balancer#AI_BALANCER} class, extends @{Fsm#FSM_SET}
-- # Demo Missions
--
-- The @{AI_Balancer#AI_BALANCER} class monitors and manages as many replacement AI groups as there are
-- CLIENTS in a SET_CLIENT collection, which are not occupied by human players.
-- In other words, use AI_BALANCER to simulate human behaviour by spawning in replacement AI in multi player missions.
-- ### [AI_BALANCER Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/AIB%20-%20AI%20Balancing)
--
-- The parent class @{Fsm#FSM_SET} manages the functionality to control the Finite State Machine (FSM).
-- The mission designer can tailor the behaviour of the AI_BALANCER, by defining event and state transition methods.
-- An explanation about state and event transition methods can be found in the @{FSM} module documentation.
-- ### [AI_BALANCER Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AIB%20-%20AI%20Balancing)
--
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
--
-- The mission designer can tailor the AI_BALANCER behaviour, by implementing a state or event handling method for the following:
-- ====
--
-- * **@{#AI_BALANCER.OnAfterSpawned}**( AISet, From, Event, To, AIGroup ): Define to add extra logic when an AI is spawned.
-- # YouTube Channel
--
-- ## 1.1) AI_BALANCER construction
-- ### [AI_BALANCER YouTube Channel](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl2CJVIrL1TdAumuVS8n64B7)
--
-- Create a new AI_BALANCER object with the @{#AI_BALANCER.New}() method:
--
-- ## 1.2) AI_BALANCER is a FSM
--
-- ![Process](..\Presentations\AI_Balancer\Dia13.JPG)
--
-- ### 1.2.1) AI_BALANCER States
--
-- * **Monitoring** ( Set ): Monitoring the Set if all AI is spawned for the Clients.
-- * **Spawning** ( Set, ClientName ): There is a new AI group spawned with ClientName as the name of reference.
-- * **Spawned** ( Set, AIGroup ): A new AI has been spawned. You can handle this event to customize the AI behaviour with other AI FSMs or own processes.
-- * **Destroying** ( Set, AIGroup ): The AI is being destroyed.
-- * **Returning** ( Set, AIGroup ): The AI is returning to the airbase specified by the ReturnToAirbase methods. Handle this state to customize the return behaviour of the AI, if any.
--
-- ### 1.2.2) AI_BALANCER Events
--
-- * **Monitor** ( Set ): Every 10 seconds, the Monitor event is triggered to monitor the Set.
-- * **Spawn** ( Set, ClientName ): Triggers when there is a new AI group to be spawned with ClientName as the name of reference.
-- * **Spawned** ( Set, AIGroup ): Triggers when a new AI has been spawned. You can handle this event to customize the AI behaviour with other AI FSMs or own processes.
-- * **Destroy** ( Set, AIGroup ): The AI is being destroyed.
-- * **Return** ( Set, AIGroup ): The AI is returning to the airbase specified by the ReturnToAirbase methods.
--
-- ## 1.3) AI_BALANCER spawn interval for replacement AI
--
-- Use the method @{#AI_BALANCER.InitSpawnInterval}() to set the earliest and latest interval in seconds that is waited until a new replacement AI is spawned.
--
-- ## 1.4) AI_BALANCER returns AI to Airbases
--
-- By default, When a human player joins a slot that is AI_BALANCED, the AI group will be destroyed by default.
-- However, there are 2 additional options that you can use to customize the destroy behaviour.
-- When a human player joins a slot, you can configure to let the AI return to:
--
-- * @{#AI_BALANCER.ReturnToHomeAirbase}: Returns the AI to the **home** @{Airbase#AIRBASE}.
-- * @{#AI_BALANCER.ReturnToNearestAirbases}: Returns the AI to the **nearest friendly** @{Airbase#AIRBASE}.
--
-- Note that when AI returns to an airbase, the AI_BALANCER will trigger the **Return** event and the AI will return,
-- otherwise the AI_BALANCER will trigger a **Destroy** event, and the AI will be destroyed.
--
-- ===
--
-- # **API CHANGE HISTORY**
@@ -90,12 +50,68 @@
--
-- @module AI_Balancer
--- AI_BALANCER class
-- @type AI_BALANCER
--- @type AI_BALANCER
-- @field Core.Set#SET_CLIENT SetClient
-- @field Functional.Spawn#SPAWN SpawnAI
-- @field Wrapper.Group#GROUP Test
-- @extends Core.Fsm#FSM_SET
--- # AI_BALANCER class, extends @{Fsm#FSM_SET}
--
-- The AI_BALANCER class monitors and manages as many replacement AI groups as there are
-- CLIENTS in a SET_CLIENT collection, which are not occupied by human players.
-- In other words, use AI_BALANCER to simulate human behaviour by spawning in replacement AI in multi player missions.
--
-- The parent class @{Fsm#FSM_SET} manages the functionality to control the Finite State Machine (FSM).
-- The mission designer can tailor the behaviour of the AI_BALANCER, by defining event and state transition methods.
-- An explanation about state and event transition methods can be found in the @{FSM} module documentation.
--
-- The mission designer can tailor the AI_BALANCER behaviour, by implementing a state or event handling method for the following:
--
-- * @{#AI_BALANCER.OnAfterSpawned}( AISet, From, Event, To, AIGroup ): Define to add extra logic when an AI is spawned.
--
-- ## 1. AI_BALANCER construction
--
-- Create a new AI_BALANCER object with the @{#AI_BALANCER.New}() method:
--
-- ## 2. AI_BALANCER is a FSM
--
-- ![Process](..\Presentations\AI_Balancer\Dia13.JPG)
--
-- ### 2.1. AI_BALANCER States
--
-- * **Monitoring** ( Set ): Monitoring the Set if all AI is spawned for the Clients.
-- * **Spawning** ( Set, ClientName ): There is a new AI group spawned with ClientName as the name of reference.
-- * **Spawned** ( Set, AIGroup ): A new AI has been spawned. You can handle this event to customize the AI behaviour with other AI FSMs or own processes.
-- * **Destroying** ( Set, AIGroup ): The AI is being destroyed.
-- * **Returning** ( Set, AIGroup ): The AI is returning to the airbase specified by the ReturnToAirbase methods. Handle this state to customize the return behaviour of the AI, if any.
--
-- ### 2.2. AI_BALANCER Events
--
-- * **Monitor** ( Set ): Every 10 seconds, the Monitor event is triggered to monitor the Set.
-- * **Spawn** ( Set, ClientName ): Triggers when there is a new AI group to be spawned with ClientName as the name of reference.
-- * **Spawned** ( Set, AIGroup ): Triggers when a new AI has been spawned. You can handle this event to customize the AI behaviour with other AI FSMs or own processes.
-- * **Destroy** ( Set, AIGroup ): The AI is being destroyed.
-- * **Return** ( Set, AIGroup ): The AI is returning to the airbase specified by the ReturnToAirbase methods.
--
-- ## 3. AI_BALANCER spawn interval for replacement AI
--
-- Use the method @{#AI_BALANCER.InitSpawnInterval}() to set the earliest and latest interval in seconds that is waited until a new replacement AI is spawned.
--
-- ## 4. AI_BALANCER returns AI to Airbases
--
-- By default, When a human player joins a slot that is AI_BALANCED, the AI group will be destroyed by default.
-- However, there are 2 additional options that you can use to customize the destroy behaviour.
-- When a human player joins a slot, you can configure to let the AI return to:
--
-- * @{#AI_BALANCER.ReturnToHomeAirbase}: Returns the AI to the **home** @{Airbase#AIRBASE}.
-- * @{#AI_BALANCER.ReturnToNearestAirbases}: Returns the AI to the **nearest friendly** @{Airbase#AIRBASE}.
--
-- Note that when AI returns to an airbase, the AI_BALANCER will trigger the **Return** event and the AI will return,
-- otherwise the AI_BALANCER will trigger a **Destroy** event, and the AI will be destroyed.
--
-- @field #AI_BALANCER
AI_BALANCER = {
ClassName = "AI_BALANCER",
PatrolZones = {},

View File

@@ -1,85 +1,31 @@
--- Single-Player:**Yes** / Multi-Player:**Yes** / AI:**Yes** / Human:**No** / Types:**Air** -- **Execute Combat Air Patrol (CAP).**
--- **AI** - **Execute Combat Air Patrol (CAP).**
--
-- ![Banner Image](..\Presentations\AI_CAP\Dia1.JPG)
--
-- ===
--
-- # 1) @{#AI_CAP_ZONE} class, extends @{AI_CAP#AI_PATROL_ZONE}
--
-- The @{#AI_CAP_ZONE} class implements the core functions to patrol a @{Zone} by an AI @{Controllable} or @{Group}
-- and automatically engage any airborne enemies that are within a certain range or within a certain zone.
-- AI CAP classes makes AI Controllables execute a Combat Air Patrol.
--
-- ![Process](..\Presentations\AI_CAP\Dia3.JPG)
-- There are the following types of CAP classes defined:
--
-- The AI_CAP_ZONE is assigned a @{Group} and this must be done before the AI_CAP_ZONE process can be started using the **Start** event.
--
-- ![Process](..\Presentations\AI_CAP\Dia4.JPG)
--
-- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits.
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
--
-- ![Process](..\Presentations\AI_CAP\Dia5.JPG)
--
-- This cycle will continue.
--
-- ![Process](..\Presentations\AI_CAP\Dia6.JPG)
--
-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event.
--
-- ![Process](..\Presentations\AI_CAP\Dia9.JPG)
--
-- When enemies are detected, the AI will automatically engage the enemy.
--
-- ![Process](..\Presentations\AI_CAP\Dia10.JPG)
--
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB.
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
--
-- ![Process](..\Presentations\AI_CAP\Dia13.JPG)
--
-- ## 1.1) AI_CAP_ZONE constructor
-- * @{#AI_CAP_ZONE}: Perform a CAP in a zone.
--
-- * @{#AI_CAP_ZONE.New}(): Creates a new AI_CAP_ZONE object.
-- ====
--
-- ## 1.2) AI_CAP_ZONE is a FSM
-- # Demo Missions
--
-- ![Process](..\Presentations\AI_CAP\Dia2.JPG)
-- ### [AI_CAP Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/CAP%20-%20Combat%20Air%20Patrol)
--
-- ### 1.2.1) AI_CAP_ZONE States
--
-- * **None** ( Group ): The process is not started yet.
-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone.
-- * **Engaging** ( Group ): The AI is engaging the bogeys.
-- * **Returning** ( Group ): The AI is returning to Base..
--
-- ### 1.2.2) AI_CAP_ZONE Events
--
-- * **Start** ( Group ): Start the process.
-- * **Route** ( Group ): Route the AI to a new random 3D point within the Patrol Zone.
-- * **Engage** ( Group ): Let the AI engage the bogeys.
-- * **RTB** ( Group ): Route the AI to the home base.
-- * **Detect** ( Group ): The AI is detecting targets.
-- * **Detected** ( Group ): The AI has detected new targets.
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB.
-- ### [AI_CAP Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/CAP%20-%20Combat%20Air%20Patrol)
--
-- ## 1.3) Set the Range of Engagement
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
--
-- ![Range](..\Presentations\AI_CAP\Dia11.JPG)
-- ====
--
-- An optional range can be set in meters,
-- that will define when the AI will engage with the detected airborne enemy targets.
-- The range can be beyond or smaller than the range of the Patrol Zone.
-- The range is applied at the position of the AI.
-- Use the method @{AI_CAP#AI_CAP_ZONE.SetEngageRange}() to define that range.
--
-- ## 1.4) Set the Zone of Engagement
-- # YouTube Channel
--
-- ![Zone](..\Presentations\AI_CAP\Dia12.JPG)
-- ### [AI_CAP YouTube Channel](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl1YCyPxJgoZn-CfhwyeW65L)
--
-- An optional @{Zone} can be set,
-- that will define when the AI will engage with the detected airborne enemy targets.
-- Use the method @{AI_Cap#AI_CAP_ZONE.SetEngageZone}() to define that Zone.
--
-- ====
--
-- # **API CHANGE HISTORY**
@@ -112,11 +58,94 @@
-- @module AI_Cap
--- AI_CAP_ZONE class
-- @type AI_CAP_ZONE
--- @type AI_CAP_ZONE
-- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling.
-- @field Core.Zone#ZONE_BASE TargetZone The @{Zone} where the patrol needs to be executed.
-- @extends AI.AI_Patrol#AI_PATROL_ZONE
--- # AI_CAP_ZONE class, extends @{AI_CAP#AI_PATROL_ZONE}
--
-- The AI_CAP_ZONE class implements the core functions to patrol a @{Zone} by an AI @{Controllable} or @{Group}
-- and automatically engage any airborne enemies that are within a certain range or within a certain zone.
--
-- ![Process](..\Presentations\AI_CAP\Dia3.JPG)
--
-- The AI_CAP_ZONE is assigned a @{Group} and this must be done before the AI_CAP_ZONE process can be started using the **Start** event.
--
-- ![Process](..\Presentations\AI_CAP\Dia4.JPG)
--
-- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits.
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
--
-- ![Process](..\Presentations\AI_CAP\Dia5.JPG)
--
-- This cycle will continue.
--
-- ![Process](..\Presentations\AI_CAP\Dia6.JPG)
--
-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event.
--
-- ![Process](..\Presentations\AI_CAP\Dia9.JPG)
--
-- When enemies are detected, the AI will automatically engage the enemy.
--
-- ![Process](..\Presentations\AI_CAP\Dia10.JPG)
--
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB.
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
--
-- ![Process](..\Presentations\AI_CAP\Dia13.JPG)
--
-- ## 1. AI_CAP_ZONE constructor
--
-- * @{#AI_CAP_ZONE.New}(): Creates a new AI_CAP_ZONE object.
--
-- ## 2. AI_CAP_ZONE is a FSM
--
-- ![Process](..\Presentations\AI_CAP\Dia2.JPG)
--
-- ### 2.1 AI_CAP_ZONE States
--
-- * **None** ( Group ): The process is not started yet.
-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone.
-- * **Engaging** ( Group ): The AI is engaging the bogeys.
-- * **Returning** ( Group ): The AI is returning to Base..
--
-- ### 2.2 AI_CAP_ZONE Events
--
-- * **@{AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process.
-- * **@{AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone.
-- * **@{#AI_CAP_ZONE.Engage}**: Let the AI engage the bogeys.
-- * **@{#AI_CAP_ZONE.Abort}**: Aborts the engagement and return patrolling in the patrol zone.
-- * **@{AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base.
-- * **@{AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets.
-- * **@{AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
-- * **@{#AI_CAP_ZONE.Destroy}**: The AI has destroyed a bogey @{Unit}.
-- * **@{#AI_CAP_ZONE.Destroyed}**: The AI has destroyed all bogeys @{Unit}s assigned in the CAS task.
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB.
--
-- ## 3. Set the Range of Engagement
--
-- ![Range](..\Presentations\AI_CAP\Dia11.JPG)
--
-- An optional range can be set in meters,
-- that will define when the AI will engage with the detected airborne enemy targets.
-- The range can be beyond or smaller than the range of the Patrol Zone.
-- The range is applied at the position of the AI.
-- Use the method @{AI_CAP#AI_CAP_ZONE.SetEngageRange}() to define that range.
--
-- ## 4. Set the Zone of Engagement
--
-- ![Zone](..\Presentations\AI_CAP\Dia12.JPG)
--
-- An optional @{Zone} can be set,
-- that will define when the AI will engage with the detected airborne enemy targets.
-- Use the method @{AI_Cap#AI_CAP_ZONE.SetEngageZone}() to define that Zone.
--
-- ===
--
-- @field #AI_CAP_ZONE
AI_CAP_ZONE = {
ClassName = "AI_CAP_ZONE",
}
@@ -340,9 +369,12 @@ function AI_CAP_ZONE:onafterStart( Controllable, From, Event, To )
-- Call the parent Start event handler
self:GetParent(self).onafterStart( self, Controllable, From, Event, To )
self:HandleEvent( EVENTS.Dead )
end
-- todo: need to fix this global function
--- @param Wrapper.Controllable#CONTROLLABLE AIControllable
function _NewEngageCapRoute( AIControllable )
@@ -392,6 +424,18 @@ function AI_CAP_ZONE:onafterDetected( Controllable, From, Event, To )
end
--- @param #AI_CAP_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_CAP_ZONE:onafterAbort( Controllable, From, Event, To )
Controllable:ClearTasks()
self:__Route( 1 )
end
--- @param #AI_CAP_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
@@ -505,14 +549,9 @@ end
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param Core.Event#EVENTDATA EventData
function AI_CAP_ZONE:onafterDestroy( Controllable, From, Event, To, EventData )
if EventData.IniUnit then
self.DetectedUnits[EventData.IniUnit] = nil
end
Controllable:MessageToAll( "Destroyed a target", 15 , "Destroyed!" )
function AI_CAP_ZONE:onafterAccomplish( Controllable, From, Event, To )
self.Accomplished = true
self:SetDetectionOff()
end
--- @param #AI_CAP_ZONE self
@@ -520,9 +559,22 @@ end
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_CAP_ZONE:onafterAccomplish( Controllable, From, Event, To )
self.Accomplished = true
self:SetDetectionOff()
-- @param Core.Event#EVENTDATA EventData
function AI_CAP_ZONE:onafterDestroy( Controllable, From, Event, To, EventData )
if EventData.IniUnit then
self.DetectedUnits[EventData.IniUnit] = nil
end
end
--- @param #AI_CAP_ZONE self
-- @param Core.Event#EVENTDATA EventData
function AI_CAP_ZONE:OnEventDead( EventData )
self:F( { "EventDead", EventData } )
if EventData.IniDCSUnit then
if self.DetectedUnits and self.DetectedUnits[EventData.IniUnit] then
self:__Destroy( 1, EventData )
end
end
end

View File

@@ -1,15 +1,59 @@
--- Single-Player:**Yes** / Multi-Player:**Yes** / AI:**Yes** / Human:**No** / Types:**Air** --
-- **Provide Close Air Support to friendly ground troops.**
--- **AI** -- **Provide Close Air Support to friendly ground troops.**
--
-- ![Banner Image](..\Presentations\AI_CAS\Dia1.JPG)
--
-- ===
--
-- # 1) @{#AI_CAS_ZONE} class, extends @{AI_Patrol#AI_PATROL_ZONE}
--
-- @{#AI_CAS_ZONE} derives from the @{AI_Patrol#AI_PATROL_ZONE}, inheriting its methods and behaviour.
-- AI CAS classes makes AI Controllables execute a Close Air Support.
--
-- There are the following types of CAS classes defined:
--
-- * @{#AI_CAS_ZONE}: Perform a CAS in a zone.
--
-- ====
--
-- # Demo Missions
--
-- ### [AI_CAS Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/CAS%20-%20Close%20Air%20Support)
--
-- ### [AI_CAS Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/CAS%20-%20Close%20Air%20Support)
--
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
--
-- ====
--
-- # YouTube Channel
--
-- ### [AI_CAS YouTube Channel](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl3JBO1WDqqpyYRRmIkR2ir2)
--
-- ===
--
-- # **AUTHORS and CONTRIBUTIONS**
--
-- ### Contributions:
--
-- * **[Quax](https://forums.eagle.ru/member.php?u=90530)**: Concept, Advice & Testing.
-- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Concept, Advice & Testing.
-- * **[Gunterlund](http://forums.eagle.ru:8080/member.php?u=75036)**: Test case revision.
--
-- ### Authors:
--
-- * **FlightControl**: Concept, Design & Programming.
--
-- @module AI_Cas
--- AI_CAS_ZONE class
-- @type AI_CAS_ZONE
-- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling.
-- @field Core.Zone#ZONE_BASE TargetZone The @{Zone} where the patrol needs to be executed.
-- @extends AI.AI_Patrol#AI_PATROL_ZONE
--- # AI_CAS_ZONE class, extends @{AI_Patrol#AI_PATROL_ZONE}
--
-- AI_CAS_ZONE derives from the @{AI_Patrol#AI_PATROL_ZONE}, inheriting its methods and behaviour.
--
-- The @{#AI_CAS_ZONE} class implements the core functions to provide Close Air Support in an Engage @{Zone} by an AIR @{Controllable} or @{Group}.
-- The AI_CAS_ZONE class implements the core functions to provide Close Air Support in an Engage @{Zone} by an AIR @{Controllable} or @{Group}.
-- The AI_CAS_ZONE runs a process. It holds an AI in a Patrol Zone and when the AI is commanded to engage, it will fly to an Engage Zone.
--
-- ![HoldAndEngage](..\Presentations\AI_CAS\Dia3.JPG)
@@ -63,66 +107,37 @@
--
-- ![Engage Event](..\Presentations\AI_CAS\Dia12.JPG)
--
-- # 1.1) AI_CAS_ZONE constructor
-- # 1. AI_CAS_ZONE constructor
--
-- * @{#AI_CAS_ZONE.New}(): Creates a new AI_CAS_ZONE object.
--
-- ## 1.2) AI_CAS_ZONE is a FSM
-- ## 2. AI_CAS_ZONE is a FSM
--
-- ![Process](..\Presentations\AI_CAS\Dia2.JPG)
--
-- ### 1.2.1) AI_CAS_ZONE States
-- ### 2.1. AI_CAS_ZONE States
--
-- * **None** ( Group ): The process is not started yet.
-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone.
-- * **Engaging** ( Group ): The AI is engaging the targets in the Engage Zone, executing CAS.
-- * **Returning** ( Group ): The AI is returning to Base..
--
-- ### 1.2.2) AI_CAS_ZONE Events
-- ### 2.2. AI_CAS_ZONE Events
--
-- * **@{AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process.
-- * **@{AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone.
-- * **@{#AI_CAS_ZONE.Engage}**: Engage the AI to provide CAS in the Engage Zone, destroying any target it finds.
-- * **@{#AI_CAS_ZONE.Abort}**: Aborts the engagement and return patrolling in the patrol zone.
-- * **@{AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base.
-- * **@{AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets.
-- * **@{AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
-- * **@{#AI_CAS_ZONE.Destroy}**: The AI has destroyed a target @{Unit}.
-- * **@{#AI_CAS_ZONE.Destroyed}**: The AI has destroyed all target @{Unit}s assigned in the CAS task.
-- * **Status**: The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB.
--
-- * **Start** ( Group ): Start the process.
-- * **Route** ( Group ): Route the AI to a new random 3D point within the Patrol Zone.
-- * **Engage** ( Group ): Engage the AI to provide CAS in the Engage Zone, destroying any target it finds.
-- * **RTB** ( Group ): Route the AI to the home base.
-- * **Detect** ( Group ): The AI is detecting targets.
-- * **Detected** ( Group ): The AI has detected new targets.
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB.
--
-- ====
--
-- # **API CHANGE HISTORY**
--
-- The underlying change log documents the API changes. Please read this carefully. The following notation is used:
--
-- * **Added** parts are expressed in bold type face.
-- * _Removed_ parts are expressed in italic type face.
--
-- Hereby the change log:
--
-- 2017-01-15: Initial class and API.
--
-- ===
--
-- # **AUTHORS and CONTRIBUTIONS**
--
-- ### Contributions:
--
-- * **[Quax](https://forums.eagle.ru/member.php?u=90530)**: Concept, Advice & Testing.
-- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Concept, Advice & Testing.
-- * **[Gunterlund](http://forums.eagle.ru:8080/member.php?u=75036)**: Test case revision.
--
-- ### Authors:
--
-- * **FlightControl**: Concept, Design & Programming.
--
-- @module AI_Cas
--- AI_CAS_ZONE class
-- @type AI_CAS_ZONE
-- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling.
-- @field Core.Zone#ZONE_BASE TargetZone The @{Zone} where the patrol needs to be executed.
-- @extends AI.AI_Patrol#AI_PATROL_ZONE
--
-- @field #AI_CAS_ZONE
AI_CAS_ZONE = {
ClassName = "AI_CAS_ZONE",
}
@@ -158,11 +173,6 @@ function AI_CAS_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone.
-- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion.
-- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement.
-- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.
-- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.
-- @return #boolean Return false to cancel Transition.
@@ -173,20 +183,29 @@ function AI_CAS_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone.
-- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion.
-- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement.
-- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.
-- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.
--- Synchronous Event Trigger for Event Engage.
-- @function [parent=#AI_CAS_ZONE] Engage
-- @param #AI_CAS_ZONE self
-- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone.
-- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement.
-- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack.
-- If parameter is not defined the unit / controllable will choose expend on its own discretion.
-- Use the structure @{DCSTypes#AI.Task.WeaponExpend} to define the amount of weapons to be release at each attack.
-- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.
-- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.
--- Asynchronous Event Trigger for Event Engage.
-- @function [parent=#AI_CAS_ZONE] __Engage
-- @param #AI_CAS_ZONE self
-- @param #number Delay The delay in seconds.
-- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone.
-- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement.
-- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack.
-- If parameter is not defined the unit / controllable will choose expend on its own discretion.
-- Use the structure @{DCSTypes#AI.Task.WeaponExpend} to define the amount of weapons to be release at each attack.
-- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.
-- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.
--- OnLeave Transition Handler for State Engaging.
-- @function [parent=#AI_CAS_ZONE] OnLeaveEngaging
@@ -350,7 +369,7 @@ function AI_CAS_ZONE:onafterStart( Controllable, From, Event, To )
-- Call the parent Start event handler
self:GetParent(self).onafterStart( self, Controllable, From, Event, To )
self:HandleEvent( EVENTS.Dead, self.OnDead )
self:HandleEvent( EVENTS.Dead )
self:SetDetectionDeactivated() -- When not engaging, set the detection off.
end
@@ -360,9 +379,10 @@ function _NewEngageRoute( AIControllable )
AIControllable:T( "NewEngageRoute" )
local EngageZone = AIControllable:GetState( AIControllable, "EngageZone" ) -- AI.AI_Cas#AI_CAS_ZONE
EngageZone:__Engage( 1 )
EngageZone:__Engage( 1, EngageZone.EngageSpeed, EngageZone.EngageAltitude, EngageZone.EngageWeaponExpend, EngageZone.EngageAttackQty, EngageZone.EngageDirection )
end
--- @param #AI_CAS_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
@@ -394,7 +414,7 @@ function AI_CAS_ZONE:onafterTarget( Controllable, From, Event, To )
if Detected == true then
self:E( {"Target: ", DetectedUnit } )
self.DetectedUnits[DetectedUnit] = false
local AttackTask = Controllable:EnRouteTaskEngageUnit( DetectedUnit, 1, true, self.EngageWeaponExpend, self.EngageAttackQty, self.EngageDirection, self.EngageAltitude, nil )
local AttackTask = Controllable:TaskAttackUnit( DetectedUnit, false, self.EngageWeaponExpend, self.EngageAttackQty, self.EngageDirection, self.EngageAltitude, nil )
self.Controllable:PushTask( AttackTask, 1 )
end
end
@@ -409,6 +429,15 @@ function AI_CAS_ZONE:onafterTarget( Controllable, From, Event, To )
end
--- @param #AI_CAS_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_CAS_ZONE:onafterAbort( Controllable, From, Event, To )
Controllable:ClearTasks()
self:__Route( 1 )
end
--- @param #AI_CAS_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
@@ -416,12 +445,17 @@ end
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone.
-- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion.
-- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement.
-- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion.
-- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.
-- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.
function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To, EngageSpeed, EngageAltitude, EngageWeaponExpend, EngageAttackQty, EngageDirection )
self:E("onafterEngage")
function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
EngageSpeed,
EngageAltitude,
EngageWeaponExpend,
EngageAttackQty,
EngageDirection )
self:F("onafterEngage")
self.EngageSpeed = EngageSpeed or 400
self.EngageAltitude = EngageAltitude or 2000
@@ -430,7 +464,6 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To, EngageSpeed,
self.EngageDirection = EngageDirection
if Controllable:IsAlive() then
local EngageRoute = {}
@@ -451,29 +484,28 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To, EngageSpeed,
EngageRoute[#EngageRoute+1] = CurrentRoutePoint
if self.Controllable:IsNotInZone( self.EngageZone ) then
-- Find a random 2D point in EngageZone.
local ToEngageZoneVec2 = self.EngageZone:GetRandomVec2()
self:T2( ToEngageZoneVec2 )
-- Obtain a 3D @{Point} from the 2D point + altitude.
local ToEngageZonePointVec3 = POINT_VEC3:New( ToEngageZoneVec2.x, self.EngageAltitude, ToEngageZoneVec2.y )
-- Create a route point of type air.
local ToEngageZoneRoutePoint = ToEngageZonePointVec3:RoutePointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
self.EngageSpeed,
true
)
EngageRoute[#EngageRoute+1] = ToEngageZoneRoutePoint
local AttackTasks = {}
for DetectedUnitID, DetectedUnit in pairs( self.DetectedUnits ) do
local DetectedUnit = DetectedUnit -- Wrapper.Unit#UNIT
self:T( DetectedUnit )
if DetectedUnit:IsAlive() then
if DetectedUnit:IsInZone( self.EngageZone ) then
self:E( {"Engaging ", DetectedUnit } )
AttackTasks[#AttackTasks+1] = Controllable:TaskAttackUnit( DetectedUnit,
true,
EngageWeaponExpend,
EngageAttackQty,
EngageDirection
)
end
else
self.DetectedUnits[DetectedUnit] = nil
end
end
EngageRoute[1].task = Controllable:TaskCombo( AttackTasks )
--- Define a random point in the @{Zone}. The AI will fly to that point within the zone.
--- Find a random 2D point in EngageZone.
@@ -492,48 +524,40 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To, EngageSpeed,
true
)
ToTargetPointVec3:SmokeBlue()
EngageRoute[#EngageRoute+1] = ToTargetRoutePoint
--- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable...
Controllable:WayPointInitialize( EngageRoute )
--- Do a trick, link the NewEngageRoute function of the object to the AIControllable in a temporary variable ...
Controllable:SetState( Controllable, "EngageZone", self )
Controllable:WayPointFunction( #EngageRoute, 1, "_NewEngageRoute" )
--- NOW ROUTE THE GROUP!
Controllable:WayPointExecute( 1 )
Controllable:OptionROEOpenFire()
Controllable:OptionROTVertical()
-- local AttackTasks = {}
--
-- for DetectedUnitID, DetectedUnit in pairs( self.DetectedUnits ) do
-- local DetectedUnit = DetectedUnit -- Wrapper.Unit#UNIT
-- self:T( DetectedUnit )
-- if DetectedUnit:IsAlive() then
-- if DetectedUnit:IsInZone( self.EngageZone ) then
-- self:E( {"Engaging ", DetectedUnit } )
-- AttackTasks[#AttackTasks+1] = Controllable:TaskAttackUnit( DetectedUnit )
-- end
-- else
-- self.DetectedUnits[DetectedUnit] = nil
-- end
-- end
--
-- EngageRoute[1].task = Controllable:TaskCombo( AttackTasks )
--- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable...
self.Controllable:WayPointInitialize( EngageRoute )
--- Do a trick, link the NewEngageRoute function of the object to the AIControllable in a temporary variable ...
self.Controllable:SetState( self.Controllable, "EngageZone", self )
self.Controllable:WayPointFunction( #EngageRoute, 1, "_NewEngageRoute" )
--- NOW ROUTE THE GROUP!
self.Controllable:WayPointExecute( 1 )
self:SetDetectionInterval( 10 )
self:SetDetectionInterval( 2 )
self:SetDetectionActivated()
self:__Target( -10 ) -- Start Targetting
self:__Target( -2 ) -- Start Targetting
end
end
--- @param #AI_CAS_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_CAS_ZONE:onafterAccomplish( Controllable, From, Event, To )
self.Accomplished = true
self:SetDetectionDeactivated()
end
--- @param #AI_CAS_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
@@ -545,27 +569,18 @@ function AI_CAS_ZONE:onafterDestroy( Controllable, From, Event, To, EventData )
if EventData.IniUnit then
self.DetectedUnits[EventData.IniUnit] = nil
end
Controllable:MessageToAll( "Destroyed a target", 15 , "Destroyed!" )
end
--- @param #AI_CAS_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_CAS_ZONE:onafterAccomplish( Controllable, From, Event, To )
self.Accomplished = true
self:SetDetectionDeactivated()
end
--- @param #AI_CAS_ZONE self
-- @param Core.Event#EVENTDATA EventData
function AI_CAS_ZONE:OnDead( EventData )
self:T( { "EventDead", EventData } )
function AI_CAS_ZONE:OnEventDead( EventData )
self:F( { "EventDead", EventData } )
if EventData.IniDCSUnit then
self:__Destroy( 1, EventData )
if self.DetectedUnits and self.DetectedUnits[EventData.IniUnit] then
self:__Destroy( 1, EventData )
end
end
end

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,107 +1,30 @@
--- Single-Player:**Yes** / Multi-Player:**Yes** / AI:**Yes** / Human:**No** / Types:**Air** --
-- **Air Patrolling or Staging.**
--- **AI** -- **Air Patrolling or Staging.**
--
-- ![Banner Image](..\Presentations\AI_PATROL\Dia1.JPG)
--
-- ===
--
-- # 1) @{#AI_PATROL_ZONE} class, extends @{Fsm#FSM_CONTROLLABLE}
-- AI PATROL classes makes AI Controllables execute an Patrol.
--
-- The @{#AI_PATROL_ZONE} class implements the core functions to patrol a @{Zone} by an AI @{Controllable} or @{Group}.
-- There are the following types of PATROL classes defined:
--
-- ![Process](..\Presentations\AI_PATROL\Dia3.JPG)
--
-- The AI_PATROL_ZONE is assigned a @{Group} and this must be done before the AI_PATROL_ZONE process can be started using the **Start** event.
--
-- ![Process](..\Presentations\AI_PATROL\Dia4.JPG)
--
-- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits.
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
--
-- ![Process](..\Presentations\AI_PATROL\Dia5.JPG)
--
-- This cycle will continue.
--
-- ![Process](..\Presentations\AI_PATROL\Dia6.JPG)
--
-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event.
--
-- ![Process](..\Presentations\AI_PATROL\Dia9.JPG)
--
---- Note that the enemy is not engaged! To model enemy engagement, either tailor the **Detected** event, or
-- use derived AI_ classes to model AI offensive or defensive behaviour.
--
-- ![Process](..\Presentations\AI_PATROL\Dia10.JPG)
--
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB.
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
--
-- ![Process](..\Presentations\AI_PATROL\Dia11.JPG)
--
-- ## 1.1) AI_PATROL_ZONE constructor
-- * @{#AI_PATROL_ZONE}: Perform a PATROL in a zone.
--
-- * @{#AI_PATROL_ZONE.New}(): Creates a new AI_PATROL_ZONE object.
-- ====
--
-- ## 1.2) AI_PATROL_ZONE is a FSM
-- # Demo Missions
--
-- ![Process](..\Presentations\AI_PATROL\Dia2.JPG)
-- ### [AI_PATROL Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/PAT%20-%20Patrolling)
--
-- ### 1.2.1) AI_PATROL_ZONE States
--
-- * **None** ( Group ): The process is not started yet.
-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone.
-- * **Returning** ( Group ): The AI is returning to Base..
--
-- ### 1.2.2) AI_PATROL_ZONE Events
--
-- * **Start** ( Group ): Start the process.
-- * **Route** ( Group ): Route the AI to a new random 3D point within the Patrol Zone.
-- * **RTB** ( Group ): Route the AI to the home base.
-- * **Detect** ( Group ): The AI is detecting targets.
-- * **Detected** ( Group ): The AI has detected new targets.
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB.
--
-- ## 1.3) Set or Get the AI controllable
--
-- * @{#AI_PATROL_ZONE.SetControllable}(): Set the AIControllable.
-- * @{#AI_PATROL_ZONE.GetControllable}(): Get the AIControllable.
-- ### [AI_PATROL Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/PAT%20-%20Patrolling)
--
-- ## 1.4) Set the Speed and Altitude boundaries of the AI controllable
--
-- * @{#AI_PATROL_ZONE.SetSpeed}(): Set the patrol speed boundaries of the AI, for the next patrol.
-- * @{#AI_PATROL_ZONE.SetAltitude}(): Set altitude boundaries of the AI, for the next patrol.
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
--
-- ## 1.5) Manage the detection process of the AI controllable
-- ====
--
-- The detection process of the AI controllable can be manipulated.
-- Detection requires an amount of CPU power, which has an impact on your mission performance.
-- Only put detection on when absolutely necessary, and the frequency of the detection can also be set.
-- # YouTube Channel
--
-- * @{#AI_PATROL_ZONE.SetDetectionOn}(): Set the detection on. The AI will detect for targets.
-- * @{#AI_PATROL_ZONE.SetDetectionOff}(): Set the detection off, the AI will not detect for targets. The existing target list will NOT be erased.
--
-- The detection frequency can be set with @{#AI_PATROL_ZONE.SetDetectionInterval}( seconds ), where the amount of seconds specify how much seconds will be waited before the next detection.
-- Use the method @{#AI_PATROL_ZONE.GetDetectedUnits}() to obtain a list of the @{Unit}s detected by the AI.
--
-- The detection can be filtered to potential targets in a specific zone.
-- Use the method @{#AI_PATROL_ZONE.SetDetectionZone}() to set the zone where targets need to be detected.
-- Note that when the zone is too far away, or the AI is not heading towards the zone, or the AI is too high, no targets may be detected
-- according the weather conditions.
--
-- ## 1.6) Manage the "out of fuel" in the AI_PATROL_ZONE
--
-- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base.
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel treshold is calculated.
-- When the fuel treshold is reached, the AI will continue for a given time its patrol task in orbit,
-- while a new AI is targetted to the AI_PATROL_ZONE.
-- Once the time is finished, the old AI will return to the base.
-- Use the method @{#AI_PATROL_ZONE.ManageFuel}() to have this proces in place.
--
-- ## 1.7) Manage "damage" behaviour of the AI in the AI_PATROL_ZONE
--
-- When the AI is damaged, it is required that a new AIControllable is started. However, damage cannon be foreseen early on.
-- Therefore, when the damage treshold is reached, the AI will return immediately to the home base (RTB).
-- Use the method @{#AI_PATROL_ZONE.ManageDamage}() to have this proces in place.
-- ### [AI_PATROL YouTube Channel](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl35HvYZKA6G22WMt7iI3zky)
--
-- ====
--
@@ -156,6 +79,111 @@
-- @field Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h.
-- @field Functional.Spawn#SPAWN CoordTest
-- @extends Core.Fsm#FSM_CONTROLLABLE
--- # AI_PATROL_ZONE class, extends @{Fsm#FSM_CONTROLLABLE}
--
-- The AI_PATROL_ZONE class implements the core functions to patrol a @{Zone} by an AI @{Controllable} or @{Group}.
--
-- ![Process](..\Presentations\AI_PATROL\Dia3.JPG)
--
-- The AI_PATROL_ZONE is assigned a @{Group} and this must be done before the AI_PATROL_ZONE process can be started using the **Start** event.
--
-- ![Process](..\Presentations\AI_PATROL\Dia4.JPG)
--
-- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits.
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
--
-- ![Process](..\Presentations\AI_PATROL\Dia5.JPG)
--
-- This cycle will continue.
--
-- ![Process](..\Presentations\AI_PATROL\Dia6.JPG)
--
-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event.
--
-- ![Process](..\Presentations\AI_PATROL\Dia9.JPG)
--
---- Note that the enemy is not engaged! To model enemy engagement, either tailor the **Detected** event, or
-- use derived AI_ classes to model AI offensive or defensive behaviour.
--
-- ![Process](..\Presentations\AI_PATROL\Dia10.JPG)
--
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB.
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
--
-- ![Process](..\Presentations\AI_PATROL\Dia11.JPG)
--
-- ## 1. AI_PATROL_ZONE constructor
--
-- * @{#AI_PATROL_ZONE.New}(): Creates a new AI_PATROL_ZONE object.
--
-- ## 2. AI_PATROL_ZONE is a FSM
--
-- ![Process](..\Presentations\AI_PATROL\Dia2.JPG)
--
-- ### 2.1. AI_PATROL_ZONE States
--
-- * **None** ( Group ): The process is not started yet.
-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone.
-- * **Returning** ( Group ): The AI is returning to Base.
-- * **Stopped** ( Group ): The process is stopped.
-- * **Crashed** ( Group ): The AI has crashed or is dead.
--
-- ### 2.2. AI_PATROL_ZONE Events
--
-- * **Start** ( Group ): Start the process.
-- * **Stop** ( Group ): Stop the process.
-- * **Route** ( Group ): Route the AI to a new random 3D point within the Patrol Zone.
-- * **RTB** ( Group ): Route the AI to the home base.
-- * **Detect** ( Group ): The AI is detecting targets.
-- * **Detected** ( Group ): The AI has detected new targets.
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB.
--
-- ## 3. Set or Get the AI controllable
--
-- * @{#AI_PATROL_ZONE.SetControllable}(): Set the AIControllable.
-- * @{#AI_PATROL_ZONE.GetControllable}(): Get the AIControllable.
--
-- ## 4. Set the Speed and Altitude boundaries of the AI controllable
--
-- * @{#AI_PATROL_ZONE.SetSpeed}(): Set the patrol speed boundaries of the AI, for the next patrol.
-- * @{#AI_PATROL_ZONE.SetAltitude}(): Set altitude boundaries of the AI, for the next patrol.
--
-- ## 5. Manage the detection process of the AI controllable
--
-- The detection process of the AI controllable can be manipulated.
-- Detection requires an amount of CPU power, which has an impact on your mission performance.
-- Only put detection on when absolutely necessary, and the frequency of the detection can also be set.
--
-- * @{#AI_PATROL_ZONE.SetDetectionOn}(): Set the detection on. The AI will detect for targets.
-- * @{#AI_PATROL_ZONE.SetDetectionOff}(): Set the detection off, the AI will not detect for targets. The existing target list will NOT be erased.
--
-- The detection frequency can be set with @{#AI_PATROL_ZONE.SetDetectionInterval}( seconds ), where the amount of seconds specify how much seconds will be waited before the next detection.
-- Use the method @{#AI_PATROL_ZONE.GetDetectedUnits}() to obtain a list of the @{Unit}s detected by the AI.
--
-- The detection can be filtered to potential targets in a specific zone.
-- Use the method @{#AI_PATROL_ZONE.SetDetectionZone}() to set the zone where targets need to be detected.
-- Note that when the zone is too far away, or the AI is not heading towards the zone, or the AI is too high, no targets may be detected
-- according the weather conditions.
--
-- ## 6. Manage the "out of fuel" in the AI_PATROL_ZONE
--
-- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base.
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel treshold is calculated.
-- When the fuel treshold is reached, the AI will continue for a given time its patrol task in orbit,
-- while a new AI is targetted to the AI_PATROL_ZONE.
-- Once the time is finished, the old AI will return to the base.
-- Use the method @{#AI_PATROL_ZONE.ManageFuel}() to have this proces in place.
--
-- ## 7. Manage "damage" behaviour of the AI in the AI_PATROL_ZONE
--
-- When the AI is damaged, it is required that a new AIControllable is started. However, damage cannon be foreseen early on.
-- Therefore, when the damage treshold is reached, the AI will return immediately to the home base (RTB).
-- Use the method @{#AI_PATROL_ZONE.ManageDamage}() to have this proces in place.
--
-- ===
--
-- @field #AI_PATROL_ZONE
AI_PATROL_ZONE = {
ClassName = "AI_PATROL_ZONE",
}
@@ -201,6 +229,51 @@ function AI_PATROL_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltit
self:SetStartState( "None" )
self:AddTransition( "*", "Stop", "Stopped" )
--- OnLeave Transition Handler for State Stopped.
-- @function [parent=#AI_PATROL_ZONE] OnLeaveStopped
-- @param #AI_PATROL_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnEnter Transition Handler for State Stopped.
-- @function [parent=#AI_PATROL_ZONE] OnEnterStopped
-- @param #AI_PATROL_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- OnBefore Transition Handler for Event Stop.
-- @function [parent=#AI_PATROL_ZONE] OnBeforeStop
-- @param #AI_PATROL_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Stop.
-- @function [parent=#AI_PATROL_ZONE] OnAfterStop
-- @param #AI_PATROL_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Stop.
-- @function [parent=#AI_PATROL_ZONE] Stop
-- @param #AI_PATROL_ZONE self
--- Asynchronous Event Trigger for Event Stop.
-- @function [parent=#AI_PATROL_ZONE] __Stop
-- @param #AI_PATROL_ZONE self
-- @param #number Delay The delay in seconds.
self:AddTransition( "None", "Start", "Patrolling" )
--- OnBefore Transition Handler for Event Start.

View File

@@ -1,71 +1,6 @@
--- (SP) (MP) (FSM) Account for (Detect, count and report) DCS events occuring on DCS objects (units).
--- **Actions** - ACT_ACCOUNT_ classes **account for** (detect, count & report) various DCS events occuring on @{Unit}s.
--
-- ===
--
-- # @{#ACT_ACCOUNT} FSM class, extends @{Fsm#FSM_PROCESS}
--
-- ## ACT_ACCOUNT state machine:
--
-- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur.
-- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below.
-- Each derived class follows exactly the same process, using the same events and following the same state transitions,
-- but will have **different implementation behaviour** upon each event or state transition.
--
-- ### ACT_ACCOUNT **Events**:
--
-- These are the events defined in this class:
--
-- * **Start**: The process is started. The process will go into the Report state.
-- * **Event**: A relevant event has occured that needs to be accounted for. The process will go into the Account state.
-- * **Report**: The process is reporting to the player the accounting status of the DCS events.
-- * **More**: There are more DCS events that need to be accounted for. The process will go back into the Report state.
-- * **NoMore**: There are no more DCS events that need to be accounted for. The process will go into the Success state.
--
-- ### ACT_ACCOUNT **Event methods**:
--
-- Event methods are available (dynamically allocated by the state machine), that accomodate for state transitions occurring in the process.
-- There are two types of event methods, which you can use to influence the normal mechanisms in the state machine:
--
-- * **Immediate**: The event method has exactly the name of the event.
-- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed.
--
-- ### ACT_ACCOUNT **States**:
--
-- * **Assigned**: The player is assigned to the task. This is the initialization state for the process.
-- * **Waiting**: the process is waiting for a DCS event to occur within the simulator. This state is set automatically.
-- * **Report**: The process is Reporting to the players in the group of the unit. This state is set automatically every 30 seconds.
-- * **Account**: The relevant DCS event has occurred, and is accounted for.
-- * **Success (*)**: All DCS events were accounted for.
-- * **Failed (*)**: The process has failed.
--
-- (*) End states of the process.
--
-- ### ACT_ACCOUNT state transition methods:
--
-- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state.
-- There are 2 moments when state transition methods will be called by the state machine:
--
-- * **Before** the state transition.
-- The state transition method needs to start with the name **OnBefore + the name of the state**.
-- If the state transition method returns false, then the processing of the state transition will not be done!
-- If you want to change the behaviour of the AIControllable at this event, return false,
-- but then you'll need to specify your own logic using the AIControllable!
--
-- * **After** the state transition.
-- The state transition method needs to start with the name **OnAfter + the name of the state**.
-- These state transition methods need to provide a return value, which is specified at the function description.
--
-- # 1) @{#ACT_ACCOUNT_DEADS} FSM class, extends @{Fsm.Account#ACT_ACCOUNT}
--
-- The ACT_ACCOUNT_DEADS class accounts (detects, counts and reports) successful kills of DCS units.
-- The process is given a @{Set} of units that will be tracked upon successful destruction.
-- The process will end after each target has been successfully destroyed.
-- Each successful dead will trigger an Account state transition that can be scored, modified or administered.
--
--
-- ## ACT_ACCOUNT_DEADS constructor:
--
-- * @{#ACT_ACCOUNT_DEADS.New}(): Creates a new ACT_ACCOUNT_DEADS object.
-- ![Banner Image](..\Presentations\ACT_ACCOUNT\Dia1.JPG)
--
-- ===
--
@@ -74,7 +9,51 @@
do -- ACT_ACCOUNT
--- ACT_ACCOUNT class
--- # @{#ACT_ACCOUNT} FSM class, extends @{Fsm#FSM_PROCESS}
--
-- ## ACT_ACCOUNT state machine:
--
-- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur.
-- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below.
-- Each derived class follows exactly the same process, using the same events and following the same state transitions,
-- but will have **different implementation behaviour** upon each event or state transition.
--
-- ### ACT_ACCOUNT States
--
-- * **Asigned**: The player is assigned.
-- * **Waiting**: Waiting for an event.
-- * **Report**: Reporting.
-- * **Account**: Account for an event.
-- * **Accounted**: All events have been accounted for, end of the process.
-- * **Failed**: Failed the process.
--
-- ### ACT_ACCOUNT Events
--
-- * **Start**: Start the process.
-- * **Wait**: Wait for an event.
-- * **Report**: Report the status of the accounting.
-- * **Event**: An event happened, process the event.
-- * **More**: More targets.
-- * **NoMore (*)**: No more targets.
-- * **Fail (*)**: The action process has failed.
--
-- (*) End states of the process.
--
-- ### ACT_ACCOUNT state transition methods:
--
-- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state.
-- There are 2 moments when state transition methods will be called by the state machine:
--
-- * **Before** the state transition.
-- The state transition method needs to start with the name **OnBefore + the name of the state**.
-- If the state transition method returns false, then the processing of the state transition will not be done!
-- If you want to change the behaviour of the AIControllable at this event, return false,
-- but then you'll need to specify your own logic using the AIControllable!
--
-- * **After** the state transition.
-- The state transition method needs to start with the name **OnAfter + the name of the state**.
-- These state transition methods need to provide a return value, which is specified at the function description.
--
-- @type ACT_ACCOUNT
-- @field Set#SET_UNIT TargetSetUnit
-- @extends Core.Fsm#FSM_PROCESS
@@ -156,7 +135,18 @@ end -- ACT_ACCOUNT
do -- ACT_ACCOUNT_DEADS
--- ACT_ACCOUNT_DEADS class
--- # @{#ACT_ACCOUNT_DEADS} FSM class, extends @{Fsm.Account#ACT_ACCOUNT}
--
-- The ACT_ACCOUNT_DEADS class accounts (detects, counts and reports) successful kills of DCS units.
-- The process is given a @{Set} of units that will be tracked upon successful destruction.
-- The process will end after each target has been successfully destroyed.
-- Each successful dead will trigger an Account state transition that can be scored, modified or administered.
--
--
-- ## ACT_ACCOUNT_DEADS constructor:
--
-- * @{#ACT_ACCOUNT_DEADS.New}(): Creates a new ACT_ACCOUNT_DEADS object.
--
-- @type ACT_ACCOUNT_DEADS
-- @field Set#SET_UNIT TargetSetUnit
-- @extends #ACT_ACCOUNT
@@ -192,15 +182,6 @@ do -- ACT_ACCOUNT_DEADS
self.TaskName = FsmAccount.TaskName
end
function ACT_ACCOUNT_DEADS:_Destructor()
self:E("_Destructor")
self:EventRemoveAll()
end
--- Process Events
--- StateMachine callback function
@@ -209,7 +190,7 @@ do -- ACT_ACCOUNT_DEADS
-- @param #string Event
-- @param #string From
-- @param #string To
function ACT_ACCOUNT_DEADS:onenterReport( ProcessUnit, From, Event, To )
function ACT_ACCOUNT_DEADS:onenterReport( ProcessUnit, Task, From, Event, To )
self:E( { ProcessUnit, From, Event, To } )
self:Message( "Your group with assigned " .. self.TaskName .. " task has " .. self.TargetSetUnit:GetUnitTypesText() .. " targets left to be destroyed." )
@@ -222,18 +203,21 @@ do -- ACT_ACCOUNT_DEADS
-- @param #string Event
-- @param #string From
-- @param #string To
function ACT_ACCOUNT_DEADS:onenterAccount( ProcessUnit, From, Event, To, EventData )
function ACT_ACCOUNT_DEADS:onenterAccount( ProcessUnit, Task, From, Event, To, EventData )
self:T( { ProcessUnit, EventData, From, Event, To } )
self:T({self.Controllable})
self.TargetSetUnit:Flush()
self:T( { "Before sending Message", EventData.IniUnitName, self.TargetSetUnit:FindUnit( EventData.IniUnitName ) } )
if self.TargetSetUnit:FindUnit( EventData.IniUnitName ) then
self:T( "Sending Message" )
local TaskGroup = ProcessUnit:GetGroup()
self.TargetSetUnit:RemoveUnitsByName( EventData.IniUnitName )
self.TargetSetUnit:Remove( EventData.IniUnitName )
self:Message( "You hit a target. Your group with assigned " .. self.TaskName .. " task has " .. self.TargetSetUnit:Count() .. " targets ( " .. self.TargetSetUnit:GetUnitTypesText() .. " ) left to be destroyed." )
end
self:T( { "After sending Message" } )
end
--- StateMachine callback function
@@ -242,7 +226,7 @@ do -- ACT_ACCOUNT_DEADS
-- @param #string Event
-- @param #string From
-- @param #string To
function ACT_ACCOUNT_DEADS:onafterEvent( ProcessUnit, From, Event, To, EventData )
function ACT_ACCOUNT_DEADS:onafterEvent( ProcessUnit, Task, From, Event, To )
if self.TargetSetUnit:Count() > 0 then
self:__More( 1 )
@@ -259,7 +243,7 @@ do -- ACT_ACCOUNT_DEADS
self:T( { "EventDead", EventData } )
if EventData.IniDCSUnit then
self:__Event( 1, EventData )
self:Event( EventData )
end
end

View File

@@ -173,9 +173,7 @@ do -- ACT_ASSIGN_ACCEPT
local ProcessGroup = ProcessUnit:GetGroup()
self:Message( "You are assigned to the task " .. self.Task:GetName() )
self.Task:Assign()
self.Task:Assign( ProcessUnit, ProcessUnit:GetPlayerName() )
end
end -- ACT_ASSIGN_ACCEPT

View File

@@ -108,7 +108,7 @@ do -- ACT_ASSIST
function ACT_ASSIST:onafterStart( ProcessUnit, From, Event, To )
local ProcessGroup = ProcessUnit:GetGroup()
local MissionMenu = self:GetMission():GetMissionMenu( ProcessGroup )
local MissionMenu = self:GetMission():GetMenu( ProcessGroup )
local function MenuSmoke( MenuParam )
self:E( MenuParam )
@@ -125,6 +125,17 @@ do -- ACT_ASSIST
self.MenuSmokeRed = MENU_GROUP_COMMAND:New( ProcessGroup, "Drop Red smoke on targets", self.Menu, MenuSmoke, { self = self, SmokeColor = SMOKECOLOR.Red } )
self.MenuSmokeWhite = MENU_GROUP_COMMAND:New( ProcessGroup, "Drop White smoke on targets", self.Menu, MenuSmoke, { self = self, SmokeColor = SMOKECOLOR.White } )
end
--- StateMachine callback function
-- @param #ACT_ASSIST self
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
-- @param #string Event
-- @param #string From
-- @param #string To
function ACT_ASSIST:onafterStop( ProcessUnit, From, Event, To )
self.Menu:Remove() -- When stopped, remove the menus
end
end

View File

@@ -56,8 +56,7 @@ function PROCESS_JTAC:New( Task, ProcessUnit, TargetSetUnit, FACUnit )
endstates = { 'Failed' }
} )
_EVENTDISPATCHER:OnDead( self.EventDead, self )
self:HandleEvent( EVENTS.Dead, self.EventDead )
return self
end

View File

@@ -81,7 +81,8 @@ do -- ACT_ROUTE
-- @type ACT_ROUTE
-- @field Tasking.Task#TASK TASK
-- @field Wrapper.Unit#UNIT ProcessUnit
-- @field Core.Zone#ZONE_BASE TargetZone
-- @field Core.Zone#ZONE_BASE Zone
-- @field Core.Point#COORDINATE Coordinate
-- @extends Core.Fsm#FSM_PROCESS
ACT_ROUTE = {
ClassName = "ACT_ROUTE",
@@ -96,12 +97,13 @@ do -- ACT_ROUTE
-- Inherits from BASE
local self = BASE:Inherit( self, FSM_PROCESS:New( "ACT_ROUTE" ) ) -- Core.Fsm#FSM_PROCESS
self:AddTransition( "*", "Reset", "None" )
self:AddTransition( "None", "Start", "Routing" )
self:AddTransition( "*", "Report", "Reporting" )
self:AddTransition( "*", "Route", "Routing" )
self:AddTransition( "*", "Report", "*" )
self:AddTransition( "Routing", "Route", "Routing" )
self:AddTransition( "Routing", "Pause", "Pausing" )
self:AddTransition( "*", "Abort", "Aborted" )
self:AddTransition( "Routing", "Arrive", "Arrived" )
self:AddTransition( "*", "Cancel", "Cancelled" )
self:AddTransition( "Arrived", "Success", "Success" )
self:AddTransition( "*", "Fail", "Failed" )
self:AddTransition( "", "", "" )
@@ -109,11 +111,79 @@ do -- ACT_ROUTE
self:AddEndState( "Arrived" )
self:AddEndState( "Failed" )
self:AddEndState( "Cancelled" )
self:SetStartState( "None" )
self:SetStartState( "None" )
self:SetRouteMode( "C" )
return self
end
--- Set a Cancel Menu item.
-- @param #ACT_ROUTE self
-- @return #ACT_ROUTE
function ACT_ROUTE:SetMenuCancel( MenuGroup, MenuText, ParentMenu, MenuTime )
MENU_GROUP_COMMAND:New(
MenuGroup,
MenuText,
ParentMenu,
self.MenuCancel,
self
):SetTime(MenuTime)
return self
end
--- Set the route mode.
-- There are 2 route modes supported:
--
-- * SetRouteMode( "B" ): Route mode is Bearing and Range.
-- * SetRouteMode( "C" ): Route mode is LL or MGRS according coordinate system setup.
--
-- @param #ACT_ROUTE self
-- @return #ACT_ROUTE
function ACT_ROUTE:SetRouteMode( RouteMode )
self.RouteMode = RouteMode
return self
end
--- Get the routing text to be displayed.
-- The route mode determines the text displayed.
-- @param #ACT_ROUTE self
-- @return #string
function ACT_ROUTE:GetRouteText( FromCoordinate )
local RouteText = ""
if self.Coordinate and self.RouteMode == "B" then
RouteText = "Route to " .. FromCoordinate:GetBRText( self.Coordinate ) .. " km."
end
if self.Coordinate and self.RouteMode == "C" then
RouteText = "Route to " .. self.Coordinate:ToString()
end
if self.Zone and self.RouteMode == "B" then
local Coordinate = self.Zone:GetCoordinate()
RouteText = "Route to zone bearing " .. FromCoordinate:GetBRText( Coordinate ) .. " km."
end
if self.Zone and self.RouteMode == "C" then
local Coordinate = self.Zone:GetCoordinate()
RouteText = "Route to zone at " .. Coordinate:ToString()
end
return RouteText
end
function ACT_ROUTE:MenuCancel()
self:Cancel()
end
--- Task Events
@@ -177,6 +247,119 @@ do -- ACT_ROUTE
end -- ACT_ROUTE
do -- ACT_ROUTE_POINT
--- ACT_ROUTE_POINT class
-- @type ACT_ROUTE_POINT
-- @field Tasking.Task#TASK TASK
-- @extends #ACT_ROUTE
ACT_ROUTE_POINT = {
ClassName = "ACT_ROUTE_POINT",
}
--- Creates a new routing state machine.
-- The task will route a controllable to a Coordinate until the controllable is within the Range.
-- @param #ACT_ROUTE_POINT self
-- @param Core.Point#COORDINATE The Coordinate to Target.
-- @param #number Range The Distance to Target.
-- @param Core.Zone#ZONE_BASE Zone
function ACT_ROUTE_POINT:New( Coordinate, Range )
local self = BASE:Inherit( self, ACT_ROUTE:New() ) -- #ACT_ROUTE_POINT
self.Coordinate = Coordinate
self.Range = Range or 0
self.DisplayInterval = 30
self.DisplayCount = 30
self.DisplayMessage = true
self.DisplayTime = 10 -- 10 seconds is the default
return self
end
--- Creates a new routing state machine.
-- The task will route a controllable to a Coordinate until the controllable is within the Range.
-- @param #ACT_ROUTE_POINT self
function ACT_ROUTE_POINT:Init( FsmRoute )
self.Coordinate = FsmRoute.Coordinate
self.Range = FsmRoute.Range or 0
self.DisplayInterval = 30
self.DisplayCount = 30
self.DisplayMessage = true
self.DisplayTime = 10 -- 10 seconds is the default
self:SetStartState("None")
end
--- Set Coordinate
-- @param #ACT_ROUTE_POINT self
-- @param Core.Point#COORDINATE Coordinate The Coordinate to route to.
function ACT_ROUTE_POINT:SetCoordinate( Coordinate )
self:F2( { Coordinate } )
self.Coordinate = Coordinate
end
--- Get Coordinate
-- @param #ACT_ROUTE_POINT self
-- @return Core.Point#COORDINATE Coordinate The Coordinate to route to.
function ACT_ROUTE_POINT:GetCoordinate()
self:F2( { self.Coordinate } )
return self.Coordinate
end
--- Set Range around Coordinate
-- @param #ACT_ROUTE_POINT self
-- @param #number Range The Range to consider the arrival. Default is 10000 meters.
function ACT_ROUTE_POINT:SetRange( Range )
self:F2( { self.Range } )
self.Range = Range or 10000
end
--- Get Range around Coordinate
-- @param #ACT_ROUTE_POINT self
-- @return #number The Range to consider the arrival. Default is 10000 meters.
function ACT_ROUTE_POINT:GetRange()
return self.Range
end
--- Method override to check if the controllable has arrived.
-- @param #ACT_ROUTE_POINT self
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
-- @return #boolean
function ACT_ROUTE_POINT:onfuncHasArrived( ProcessUnit )
if ProcessUnit:IsAlive() then
local Distance = self.Coordinate:Get2DDistance( ProcessUnit:GetCoordinate() )
if Distance <= self.Range then
local RouteText = "You have arrived."
self:Message( RouteText )
return true
end
end
return false
end
--- Task Events
--- StateMachine callback function
-- @param #ACT_ROUTE_POINT self
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
-- @param #string Event
-- @param #string From
-- @param #string To
function ACT_ROUTE_POINT:onafterReport( ProcessUnit, From, Event, To )
local TaskUnitCoordinate = ProcessUnit:GetCoordinate()
local RouteText = self:GetRouteText( TaskUnitCoordinate )
self:Message( RouteText )
end
end -- ACT_ROUTE_POINT
do -- ACT_ROUTE_ZONE
@@ -184,7 +367,7 @@ do -- ACT_ROUTE_ZONE
-- @type ACT_ROUTE_ZONE
-- @field Tasking.Task#TASK TASK
-- @field Wrapper.Unit#UNIT ProcessUnit
-- @field Core.Zone#ZONE_BASE TargetZone
-- @field Core.Zone#ZONE_BASE Zone
-- @extends #ACT_ROUTE
ACT_ROUTE_ZONE = {
ClassName = "ACT_ROUTE_ZONE",
@@ -193,11 +376,11 @@ do -- ACT_ROUTE_ZONE
--- Creates a new routing state machine. The task will route a controllable to a ZONE until the controllable is within that ZONE.
-- @param #ACT_ROUTE_ZONE self
-- @param Core.Zone#ZONE_BASE TargetZone
function ACT_ROUTE_ZONE:New( TargetZone )
-- @param Core.Zone#ZONE_BASE Zone
function ACT_ROUTE_ZONE:New( Zone )
local self = BASE:Inherit( self, ACT_ROUTE:New() ) -- #ACT_ROUTE_ZONE
self.TargetZone = TargetZone
self.Zone = Zone
self.DisplayInterval = 30
self.DisplayCount = 30
@@ -209,7 +392,7 @@ do -- ACT_ROUTE_ZONE
function ACT_ROUTE_ZONE:Init( FsmRoute )
self.TargetZone = FsmRoute.TargetZone
self.Zone = FsmRoute.Zone
self.DisplayInterval = 30
self.DisplayCount = 30
@@ -217,18 +400,32 @@ do -- ACT_ROUTE_ZONE
self.DisplayTime = 10 -- 10 seconds is the default
end
--- Set Zone
-- @param #ACT_ROUTE_ZONE self
-- @param Core.Zone#ZONE_BASE Zone The Zone object where to route to.
function ACT_ROUTE_ZONE:SetZone( Zone )
self.Zone = Zone
end
--- Get Zone
-- @param #ACT_ROUTE_ZONE self
-- @return Core.Zone#ZONE_BASE Zone The Zone object where to route to.
function ACT_ROUTE_ZONE:GetZone()
return self.Zone
end
--- Method override to check if the controllable has arrived.
-- @param #ACT_ROUTE self
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
-- @return #boolean
function ACT_ROUTE_ZONE:onfuncHasArrived( ProcessUnit )
if ProcessUnit:IsInZone( self.TargetZone ) then
if ProcessUnit:IsInZone( self.Zone ) then
local RouteText = "You have arrived within the zone."
self:Message( RouteText )
end
return ProcessUnit:IsInZone( self.TargetZone )
return ProcessUnit:IsInZone( self.Zone )
end
--- Task Events
@@ -239,13 +436,13 @@ do -- ACT_ROUTE_ZONE
-- @param #string Event
-- @param #string From
-- @param #string To
function ACT_ROUTE_ZONE:onenterReporting( ProcessUnit, From, Event, To )
function ACT_ROUTE_ZONE:onafterReport( ProcessUnit, From, Event, To )
local ZoneVec2 = self.TargetZone:GetVec2()
local ZonePointVec2 = POINT_VEC2:New( ZoneVec2.x, ZoneVec2.y )
local ZoneVec2 = self.Zone:GetVec2()
local ZoneCoordinate = COORDINATE:New( ZoneVec2.x, ZoneVec2.y )
local TaskUnitVec2 = ProcessUnit:GetVec2()
local TaskUnitPointVec2 = POINT_VEC2:New( TaskUnitVec2.x, TaskUnitVec2.y )
local RouteText = "Route to " .. TaskUnitPointVec2:GetBRText( ZonePointVec2 ) .. " km to target."
local TaskUnitCoordinate = COORDINATE:New( TaskUnitVec2.x, TaskUnitVec2.y )
local RouteText = self:GetRouteText( TaskUnitCoordinate )
self:Message( RouteText )
end

View File

@@ -1,34 +1,111 @@
--- This module contains the BASE class.
--- **Core** - BASE forms **the basis of the MOOSE framework**. Each class within the MOOSE framework derives from BASE.
--
-- 1) @{#BASE} class
-- =================
-- The @{#BASE} class is the super class for all the classes defined within MOOSE.
-- ![Banner Image](..\Presentations\BASE\Dia1.JPG)
--
-- It handles:
-- ===
--
-- * The construction and inheritance of child classes.
-- * The tracing of objects during mission execution within the **DCS.log** file, under the **"Saved Games\DCS\Logs"** folder.
-- The @{#BASE} class is the core root class from where every other class in moose is derived.
--
-- Note: Normally you would not use the BASE class unless you are extending the MOOSE framework with new classes.
-- ===
--
-- # **API CHANGE HISTORY**
--
-- The underlying change log documents the API changes. Please read this carefully. The following notation is used:
--
-- * **Added** parts are expressed in bold type face.
-- * _Removed_ parts are expressed in italic type face.
--
-- YYYY-MM-DD: CLASS:**NewFunction**( Params ) replaces CLASS:_OldFunction_( Params )
-- YYYY-MM-DD: CLASS:**NewFunction( Params )** added
--
-- Hereby the change log:
--
-- ===
--
-- # **AUTHORS and CONTRIBUTIONS**
--
-- ### Contributions:
--
-- * None.
--
-- ### Authors:
--
-- * **FlightControl**: Design & Programming
--
-- @module Base
local _TraceOnOff = true
local _TraceLevel = 1
local _TraceAll = false
local _TraceClass = {}
local _TraceClassMethod = {}
local _ClassID = 0
--- @type BASE
-- @field ClassName The name of the class.
-- @field ClassID The ID number of the class.
-- @field ClassNameAndID The name of the class concatenated with the ID number of the class.
--- # 1) #BASE class
--
-- All classes within the MOOSE framework are derived from the BASE class.
--
-- BASE provides facilities for :
--
-- * The construction and inheritance of MOOSE classes.
-- * The class naming and numbering system.
-- * The class hierarchy search system.
-- * The tracing of information or objects during mission execution for debuggin purposes.
-- * The subscription to DCS events for event handling in MOOSE objects.
--
-- Note: The BASE class is an abstract class and is not meant to be used directly.
--
-- ## 1.1) BASE constructor
--
-- Any class derived from BASE, must use the @{Base#BASE.New) constructor within the @{Base#BASE.Inherit) method.
-- Any class derived from BASE, will use the @{Base#BASE.New} constructor embedded in the @{Base#BASE.Inherit} method.
-- See an example at the @{Base#BASE.New} method how this is done.
--
-- ## 1.2) BASE Trace functionality
-- ## 1.2) Trace information for debugging
--
-- The BASE class contains trace methods to trace progress within a mission execution of a certain object.
-- Note that these trace methods are inherited by each MOOSE class interiting BASE.
-- As such, each object created from derived class from BASE can use the tracing functions to trace its execution.
-- These trace methods are inherited by each MOOSE class interiting BASE, soeach object created from derived class from BASE can use the tracing methods to trace its execution.
--
-- ### 1.2.1) Tracing functions
-- Any type of information can be passed to these tracing methods. See the following examples:
--
-- self:E( "Hello" )
--
-- Result in the word "Hello" in the dcs.log.
--
-- local Array = { 1, nil, "h", { "a","b" }, "x" }
-- self:E( Array )
--
-- Results with the text [1]=1,[3]="h",[4]={[1]="a",[2]="b"},[5]="x"} in the dcs.log.
--
-- local Object1 = "Object1"
-- local Object2 = 3
-- local Object3 = { Object 1, Object 2 }
-- self:E( { Object1, Object2, Object3 } )
--
-- Results with the text [1]={[1]="Object",[2]=3,[3]={[1]="Object",[2]=3}} in the dcs.log.
--
-- local SpawnObject = SPAWN:New( "Plane" )
-- local GroupObject = GROUP:FindByName( "Group" )
-- self:E( { Spawn = SpawnObject, Group = GroupObject } )
--
-- Results with the text [1]={Spawn={....),Group={...}} in the dcs.log.
--
-- Below a more detailed explanation of the different method types for tracing.
--
-- ### 1.2.1) Tracing methods categories
--
-- There are basically 3 types of tracing methods available within BASE:
-- There are basically 3 types of tracing methods available:
--
-- * @{#BASE.F}: Trace the beginning of a function and its given parameters. An F is indicated at column 44 in the DCS.log file.
-- * @{#BASE.T}: Trace further logic within a function giving optional variables or parameters. A T is indicated at column 44 in the DCS.log file.
-- * @{#BASE.E}: Trace an exception within a function giving optional variables or parameters. An E is indicated at column 44 in the DCS.log file. An exception will always be traced.
-- * @{#BASE.F}: Used to trace the entrance of a function and its given parameters. An F is indicated at column 44 in the DCS.log file.
-- * @{#BASE.T}: Used to trace further logic within a function giving optional variables or parameters. A T is indicated at column 44 in the DCS.log file.
-- * @{#BASE.E}: Used to always trace information giving optional variables or parameters. An E is indicated at column 44 in the DCS.log file.
--
-- ### 1.2.2) Tracing levels
--
@@ -51,6 +128,7 @@
-- * Activate only the tracing of a certain class (name) through the @{#BASE.TraceClass}() method.
-- * Activate only the tracing of a certain method of a certain class through the @{#BASE.TraceClassMethod}() method.
-- * Activate only the tracing of a certain level through the @{#BASE.TraceLevel}() method.
--
-- ### 1.2.4) Check if tracing is on.
--
-- The method @{#BASE.IsTrace}() will validate if tracing is activated or not.
@@ -64,7 +142,7 @@
--
-- At first, the mission designer will need to **Subscribe** to a specific DCS event for the class.
-- So, when the DCS event occurs, the class will be notified of that event.
-- There are two functions which you use to subscribe to or unsubscribe from an event.
-- There are two methods which you use to subscribe to or unsubscribe from an event.
--
-- * @{#BASE.HandleEvent}(): Subscribe to a DCS Event.
-- * @{#BASE.UnHandleEvent}(): Unsubscribe from a DCS Event.
@@ -114,10 +192,12 @@
--
-- ## 1.5) All objects derived from BASE can have "States"
--
-- A mechanism is in place in MOOSE, that allows to let the objects administer **states**.
-- States are essentially properties of objects, which are identified by a **Key** and a **Value**.
-- The method @{#BASE.SetState}() can be used to set a Value with a reference Key to the object.
-- To **read or retrieve** a state Value based on a Key, use the @{#BASE.GetState} method.
-- A mechanism is in place in MOOSE, that allows to let the objects administer **states**.
-- States are essentially properties of objects, which are identified by a **Key** and a **Value**.
--
-- The method @{#BASE.SetState}() can be used to set a Value with a reference Key to the object.
-- To **read or retrieve** a state Value based on a Key, use the @{#BASE.GetState} method.
--
-- These two methods provide a very handy way to keep state at long lasting processes.
-- Values can be stored within the objects, and later retrieved or changed when needed.
-- There is one other important thing to note, the @{#BASE.SetState}() and @{#BASE.GetState} methods
@@ -125,74 +205,46 @@
-- Thus, if the state is to be set for the same object as the object for which the method is used, then provide the same
-- object name to the method.
--
-- ## 1.10) BASE Inheritance (tree) support
-- ## 1.10) Inheritance
--
-- The following methods are available to support inheritance:
-- The following methods are available to implement inheritance
--
-- * @{#BASE.Inherit}: Inherits from a class.
-- * @{#BASE.GetParent}: Returns the parent object from the object it is handling, or nil if there is no parent object.
--
-- ====
--
-- # **API CHANGE HISTORY**
--
-- The underlying change log documents the API changes. Please read this carefully. The following notation is used:
--
-- * **Added** parts are expressed in bold type face.
-- * _Removed_ parts are expressed in italic type face.
--
-- YYYY-MM-DD: CLASS:**NewFunction**( Params ) replaces CLASS:_OldFunction_( Params )
-- YYYY-MM-DD: CLASS:**NewFunction( Params )** added
--
-- Hereby the change log:
--
-- ===
--
-- # **AUTHORS and CONTRIBUTIONS**
-- @field #BASE BASE
--
-- ### Contributions:
--
-- * None.
--
-- ### Authors:
--
-- * **FlightControl**: Design & Programming
--
-- @module Base
local _TraceOnOff = true
local _TraceLevel = 1
local _TraceAll = false
local _TraceClass = {}
local _TraceClassMethod = {}
local _ClassID = 0
--- The BASE Class
-- @type BASE
-- @field ClassName The name of the class.
-- @field ClassID The ID number of the class.
-- @field ClassNameAndID The name of the class concatenated with the ID number of the class.
BASE = {
ClassName = "BASE",
ClassID = 0,
_Private = {},
Events = {},
States = {}
States = {},
_ = {},
}
--- The Formation Class
-- @type FORMATION
-- @field Cone A cone formation.
FORMATION = {
Cone = "Cone"
Cone = "Cone",
Vee = "Vee"
}
-- @todo need to investigate if the deepCopy is really needed... Don't think so.
--- BASE constructor.
--
-- This is an example how to use the BASE:New() constructor in a new class definition when inheriting from BASE.
--
-- function EVENT:New()
-- local self = BASE:Inherit( self, BASE:New() ) -- #EVENT
-- return self
-- end
--
-- @param #BASE self
-- @return #BASE
function BASE:New()
local self = routines.utils.deepCopy( self ) -- Create a new self instance
local MetaTable = {}
@@ -211,12 +263,14 @@ function BASE:_Destructor()
--self:EventRemoveAll()
end
-- THIS IS WHY WE NEED LUA 5.2 ...
function BASE:_SetDestructor()
-- TODO: Okay, this is really technical...
-- When you set a proxy to a table to catch __gc, weak tables don't behave like weak...
-- Therefore, I am parking this logic until I've properly discussed all this with the community.
--[[
local proxy = newproxy(true)
local proxyMeta = getmetatable(proxy)
@@ -231,7 +285,7 @@ function BASE:_SetDestructor()
-- table is about to be garbage-collected - then the __gc hook
-- will be invoked and the destructor called
rawset( self, '__proxy', proxy )
--]]
end
--- This is the worker method to inherit from a parent class.
@@ -247,13 +301,18 @@ function BASE:Inherit( Child, Parent )
setmetatable( Child, Parent )
Child.__index = Child
Child:_SetDestructor()
--Child:_SetDestructor()
end
--self:T( 'Inherited from ' .. Parent.ClassName )
return Child
end
--- This is the worker method to retrieve the Parent class.
--- This is the worker method to retrieve the Parent class.
-- Note that the Parent class must be passed to call the parent class method.
--
-- self:GetParent(self):ParentMethod()
--
--
-- @param #BASE self
-- @param #BASE Child is the Child class from which the Parent class needs to be retrieved.
-- @return #BASE
@@ -302,7 +361,7 @@ do -- Event Handling
-- @param #BASE self
-- @return #number The @{Event} processing Priority.
function BASE:GetEventPriority()
return self._Private.EventPriority or 5
return self._.EventPriority or 5
end
--- Set the Class @{Event} processing Priority.
@@ -312,7 +371,7 @@ do -- Event Handling
-- @param #number EventPriority The @{Event} processing Priority.
-- @return self
function BASE:SetEventPriority( EventPriority )
self._Private.EventPriority = EventPriority
self._.EventPriority = EventPriority
end
--- Remove all subscribed events
@@ -395,6 +454,12 @@ do -- Event Handling
-- @param #BASE self
-- @param Core.Event#EVENTDATA EventData The EventData structure.
--- Occurs when an object is dead.
-- initiator : The unit that is dead.
-- @function [parent=#BASE] OnEventDead
-- @param #BASE self
-- @param Core.Event#EVENTDATA EventData The EventData structure.
--- Occurs when an object is completely destroyed.
-- initiator : The unit that is was destroyed.
-- @function [parent=#BASE] OnEvent

File diff suppressed because it is too large Load Diff

View File

@@ -6,12 +6,14 @@
-- ===================================================
-- Mission designers can use the DATABASE class to refer to:
--
-- * STATICS
-- * UNITS
-- * GROUPS
-- * CLIENTS
-- * AIRPORTS
-- * AIRBASES
-- * PLAYERSJOINED
-- * PLAYERS
-- * CARGOS
--
-- On top, for internal MOOSE administration purposes, the DATBASE administers the Unit and Group TEMPLATES as defined within the Mission Editor.
--
@@ -44,16 +46,21 @@ DATABASE = {
Templates = {
Units = {},
Groups = {},
Statics = {},
ClientsByName = {},
ClientsByID = {},
},
UNITS = {},
UNITS_Index = {},
STATICS = {},
GROUPS = {},
PLAYERS = {},
PLAYERSJOINED = {},
CLIENTS = {},
CARGOS = {},
AIRBASES = {},
COUNTRY_ID = {},
COUNTRY_NAME = {},
NavPoints = {},
}
@@ -82,16 +89,18 @@ local _DATABASECategory =
function DATABASE:New()
-- Inherits from BASE
local self = BASE:Inherit( self, BASE:New() )
local self = BASE:Inherit( self, BASE:New() ) -- #DATABASE
self:SetEventPriority( 1 )
self:HandleEvent( EVENTS.Birth, self._EventOnBirth )
self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash )
self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash )
self:HandleEvent( EVENTS.NewCargo )
self:HandleEvent( EVENTS.DeleteCargo )
-- Follow alive players and clients
self:HandleEvent( EVENTS.PlayerEnterUnit, self._EventOnPlayerEnterUnit )
-- self:HandleEvent( EVENTS.PlayerEnterUnit, self._EventOnPlayerEnterUnit )
self:HandleEvent( EVENTS.PlayerLeaveUnit, self._EventOnPlayerLeaveUnit )
self:_RegisterTemplates()
@@ -100,6 +109,33 @@ function DATABASE:New()
self:_RegisterStatics()
self:_RegisterPlayers()
self:_RegisterAirbases()
self.UNITS_Position = 0
--- @param #DATABASE self
local function CheckPlayers( self )
local UNITS_Count = #self.UNITS_Index
if UNITS_Count > 0 then
self.UNITS_Position = ( ( self.UNITS_Position <= UNITS_Count ) and self.UNITS_Position + 1 ) or 1
local PlayerUnit = self.UNITS[self.UNITS_Index[self.UNITS_Position]]
if PlayerUnit then
local UnitName = PlayerUnit:GetName()
local PlayerName = PlayerUnit:GetPlayerName()
--self:E( { UNITS_Count, self.UNITS_Position, UnitName, PlayerName } )
if PlayerName and PlayerName ~= "" then
if self.PLAYERS[PlayerName] == nil or self.PLAYERS[PlayerName] ~= UnitName then
self:E( { "Add player for unit:", UnitName, PlayerName } )
self:AddPlayer( UnitName, PlayerName )
--_EVENTDISPATCHER:CreateEventPlayerEnterUnit( PlayerUnit )
end
end
end
end
end
self:E( "Scheduling" )
--local PlayerCheckSchedule = SCHEDULER:New( nil, CheckPlayers, { self }, 2, 0.1 )
return self
end
@@ -122,6 +158,8 @@ function DATABASE:AddUnit( DCSUnitName )
if not self.UNITS[DCSUnitName] then
local UnitRegister = UNIT:Register( DCSUnitName )
self.UNITS[DCSUnitName] = UNIT:Register( DCSUnitName )
table.insert( self.UNITS_Index, DCSUnitName )
end
return self.UNITS[DCSUnitName]
@@ -132,7 +170,7 @@ end
-- @param #DATABASE self
function DATABASE:DeleteUnit( DCSUnitName )
--self.UNITS[DCSUnitName] = nil
self.UNITS[DCSUnitName] = nil
end
--- Adds a Static based on the Static Name in the DATABASE.
@@ -164,22 +202,24 @@ end
--- Adds a Airbase based on the Airbase Name in the DATABASE.
-- @param #DATABASE self
function DATABASE:AddAirbase( DCSAirbaseName )
-- @param #string AirbaseName The name of the airbase
function DATABASE:AddAirbase( AirbaseName )
if not self.AIRBASES[DCSAirbaseName] then
self.AIRBASES[DCSAirbaseName] = AIRBASE:Register( DCSAirbaseName )
if not self.AIRBASES[AirbaseName] then
self.AIRBASES[AirbaseName] = AIRBASE:Register( AirbaseName )
end
end
--- Deletes a Airbase from the DATABASE based on the Airbase Name.
-- @param #DATABASE self
function DATABASE:DeleteAirbase( DCSAirbaseName )
-- @param #string AirbaseName The name of the airbase
function DATABASE:DeleteAirbase( AirbaseName )
--self.AIRBASES[DCSAirbaseName] = nil
self.AIRBASES[AirbaseName] = nil
end
--- Finds a AIRBASE based on the AirbaseName.
--- Finds an AIRBASE based on the AirbaseName.
-- @param #DATABASE self
-- @param #string AirbaseName
-- @return Wrapper.Airbase#AIRBASE The found AIRBASE.
@@ -189,6 +229,35 @@ function DATABASE:FindAirbase( AirbaseName )
return AirbaseFound
end
--- Adds a Cargo based on the Cargo Name in the DATABASE.
-- @param #DATABASE self
-- @param #string CargoName The name of the airbase
function DATABASE:AddCargo( Cargo )
if not self.CARGOS[Cargo.Name] then
self.CARGOS[Cargo.Name] = Cargo
end
end
--- Deletes a Cargo from the DATABASE based on the Cargo Name.
-- @param #DATABASE self
-- @param #string CargoName The name of the airbase
function DATABASE:DeleteCargo( CargoName )
self.CARGOS[CargoName] = nil
end
--- Finds an CARGO based on the CargoName.
-- @param #DATABASE self
-- @param #string CargoName
-- @return Wrapper.Cargo#CARGO The found CARGO.
function DATABASE:FindCargo( CargoName )
local CargoFound = self.CARGOS[CargoName]
return CargoFound
end
--- Finds a CLIENT based on the ClientName.
-- @param #DATABASE self
@@ -242,7 +311,7 @@ function DATABASE:AddPlayer( UnitName, PlayerName )
if PlayerName then
self:E( { "Add player for unit:", UnitName, PlayerName } )
self.PLAYERS[PlayerName] = self:FindUnit( UnitName )
self.PLAYERS[PlayerName] = UnitName
self.PLAYERSJOINED[PlayerName] = PlayerName
end
end
@@ -280,7 +349,7 @@ function DATABASE:Spawn( SpawnTemplate )
SpawnTemplate.CountryID = nil
SpawnTemplate.CategoryID = nil
self:_RegisterTemplate( SpawnTemplate, SpawnCoalitionID, SpawnCategoryID, SpawnCountryID )
self:_RegisterGroupTemplate( SpawnTemplate, SpawnCoalitionID, SpawnCategoryID, SpawnCountryID )
self:T3( SpawnTemplate )
coalition.addGroup( SpawnCountryID, SpawnCategoryID, SpawnTemplate )
@@ -316,7 +385,7 @@ end
-- @param #DATABASE self
-- @param #table GroupTemplate
-- @return #DATABASE self
function DATABASE:_RegisterTemplate( GroupTemplate, CoalitionID, CategoryID, CountryID )
function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionID, CategoryID, CountryID )
local GroupTemplateName = env.getValueDictByKey(GroupTemplate.name)
@@ -394,6 +463,54 @@ function DATABASE:GetGroupTemplate( GroupName )
return GroupTemplate
end
--- Private method that registers new Static Templates within the DATABASE Object.
-- @param #DATABASE self
-- @param #table GroupTemplate
-- @return #DATABASE self
function DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, CategoryID, CountryID )
local TraceTable = {}
local StaticTemplateName = env.getValueDictByKey(StaticTemplate.name)
self.Templates.Statics[StaticTemplateName] = self.Templates.Statics[StaticTemplateName] or {}
StaticTemplate.CategoryID = CategoryID
StaticTemplate.CoalitionID = CoalitionID
StaticTemplate.CountryID = CountryID
self.Templates.Statics[StaticTemplateName].StaticName = StaticTemplateName
self.Templates.Statics[StaticTemplateName].GroupTemplate = StaticTemplate
self.Templates.Statics[StaticTemplateName].UnitTemplate = StaticTemplate.units[1]
self.Templates.Statics[StaticTemplateName].CategoryID = CategoryID
self.Templates.Statics[StaticTemplateName].CoalitionID = CoalitionID
self.Templates.Statics[StaticTemplateName].CountryID = CountryID
TraceTable[#TraceTable+1] = "Static"
TraceTable[#TraceTable+1] = self.Templates.Statics[StaticTemplateName].GroupName
TraceTable[#TraceTable+1] = "Coalition"
TraceTable[#TraceTable+1] = self.Templates.Statics[StaticTemplateName].CoalitionID
TraceTable[#TraceTable+1] = "Category"
TraceTable[#TraceTable+1] = self.Templates.Statics[StaticTemplateName].CategoryID
TraceTable[#TraceTable+1] = "Country"
TraceTable[#TraceTable+1] = self.Templates.Statics[StaticTemplateName].CountryID
self:E( TraceTable )
end
--- @param #DATABASE self
function DATABASE:GetStaticUnitTemplate( StaticName )
local StaticTemplate = self.Templates.Statics[StaticName].UnitTemplate
StaticTemplate.SpawnCoalitionID = self.Templates.Statics[StaticName].CoalitionID
StaticTemplate.SpawnCategoryID = self.Templates.Statics[StaticName].CategoryID
StaticTemplate.SpawnCountryID = self.Templates.Statics[StaticName].CountryID
return StaticTemplate
end
function DATABASE:GetGroupNameFromUnitName( UnitName )
return self.Templates.Units[UnitName].GroupName
end
@@ -542,8 +659,14 @@ function DATABASE:_EventOnBirth( Event )
self:F2( { Event } )
if Event.IniDCSUnit then
self:AddUnit( Event.IniDCSUnitName )
self:AddGroup( Event.IniDCSGroupName )
if Event.IniObjectCategory == 3 then
self:AddStatic( Event.IniDCSUnitName )
else
if Event.IniObjectCategory == 1 then
self:AddUnit( Event.IniDCSUnitName )
self:AddGroup( Event.IniDCSGroupName )
end
end
self:_EventOnPlayerEnterUnit( Event )
end
end
@@ -556,9 +679,16 @@ function DATABASE:_EventOnDeadOrCrash( Event )
self:F2( { Event } )
if Event.IniDCSUnit then
if self.UNITS[Event.IniDCSUnitName] then
self:DeleteUnit( Event.IniDCSUnitName )
-- add logic to correctly remove a group once all units are destroyed...
if Event.IniObjectCategory == 3 then
if self.STATICS[Event.IniDCSUnitName] then
self:DeleteStatic( Event.IniDCSUnitName )
end
else
if Event.IniObjectCategory == 1 then
if self.UNITS[Event.IniDCSUnitName] then
self:DeleteUnit( Event.IniDCSUnitName )
end
end
end
end
end
@@ -571,11 +701,13 @@ function DATABASE:_EventOnPlayerEnterUnit( Event )
self:F2( { Event } )
if Event.IniUnit then
self:AddUnit( Event.IniDCSUnitName )
self:AddGroup( Event.IniDCSGroupName )
local PlayerName = Event.IniUnit:GetPlayerName()
if not self.PLAYERS[PlayerName] then
self:AddPlayer( Event.IniUnitName, PlayerName )
if Event.IniObjectCategory == 1 then
self:AddUnit( Event.IniDCSUnitName )
self:AddGroup( Event.IniDCSGroupName )
local PlayerName = Event.IniUnit:GetPlayerName()
if not self.PLAYERS[PlayerName] then
self:AddPlayer( Event.IniUnitName, PlayerName )
end
end
end
end
@@ -588,9 +720,11 @@ function DATABASE:_EventOnPlayerLeaveUnit( Event )
self:F2( { Event } )
if Event.IniUnit then
local PlayerName = Event.IniUnit:GetPlayerName()
if self.PLAYERS[PlayerName] then
self:DeletePlayer( PlayerName )
if Event.IniObjectCategory == 1 then
local PlayerName = Event.IniUnit:GetPlayerName()
if self.PLAYERS[PlayerName] then
self:DeletePlayer( PlayerName )
end
end
end
end
@@ -644,9 +778,22 @@ function DATABASE:ForEach( IteratorFunction, FinalizeFunction, arg, Set )
end
--- Iterate the DATABASE and call an iterator function for each **alive** STATIC, providing the STATIC and optional parameters.
-- @param #DATABASE self
-- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept a STATIC parameter.
-- @return #DATABASE self
function DATABASE:ForEachStatic( IteratorFunction, FinalizeFunction, ... ) --R2.1
self:F2( arg )
self:ForEach( IteratorFunction, FinalizeFunction, arg, self.STATICS )
return self
end
--- Iterate the DATABASE and call an iterator function for each **alive** UNIT, providing the UNIT and optional parameters.
-- @param #DATABASE self
-- @param #function IteratorFunction The function that will be called when there is an alive UNIT in the database. The function needs to accept a UNIT parameter.
-- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept a UNIT parameter.
-- @return #DATABASE self
function DATABASE:ForEachUnit( IteratorFunction, FinalizeFunction, ... )
self:F2( arg )
@@ -656,9 +803,10 @@ function DATABASE:ForEachUnit( IteratorFunction, FinalizeFunction, ... )
return self
end
--- Iterate the DATABASE and call an iterator function for each **alive** GROUP, providing the GROUP and optional parameters.
-- @param #DATABASE self
-- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the database. The function needs to accept a GROUP parameter.
-- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept a GROUP parameter.
-- @return #DATABASE self
function DATABASE:ForEachGroup( IteratorFunction, ... )
self:F2( arg )
@@ -671,7 +819,7 @@ end
--- Iterate the DATABASE and call an iterator function for each **ALIVE** player, providing the player name and optional parameters.
-- @param #DATABASE self
-- @param #function IteratorFunction The function that will be called when there is an player in the database. The function needs to accept the player name.
-- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept the player name.
-- @return #DATABASE self
function DATABASE:ForEachPlayer( IteratorFunction, ... )
self:F2( arg )
@@ -684,7 +832,7 @@ end
--- Iterate the DATABASE and call an iterator function for each player who has joined the mission, providing the Unit of the player and optional parameters.
-- @param #DATABASE self
-- @param #function IteratorFunction The function that will be called when there is was a player in the database. The function needs to accept a UNIT parameter.
-- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept a UNIT parameter.
-- @return #DATABASE self
function DATABASE:ForEachPlayerJoined( IteratorFunction, ... )
self:F2( arg )
@@ -696,7 +844,7 @@ end
--- Iterate the DATABASE and call an iterator function for each CLIENT, providing the CLIENT to the function and optional parameters.
-- @param #DATABASE self
-- @param #function IteratorFunction The function that will be called when there is an alive player in the database. The function needs to accept a CLIENT parameter.
-- @param #function IteratorFunction The function that will be called object in the database. The function needs to accept a CLIENT parameter.
-- @return #DATABASE self
function DATABASE:ForEachClient( IteratorFunction, ... )
self:F2( arg )
@@ -706,7 +854,44 @@ function DATABASE:ForEachClient( IteratorFunction, ... )
return self
end
--- Iterate the DATABASE and call an iterator function for each CARGO, providing the CARGO object to the function and optional parameters.
-- @param #DATABASE self
-- @param #function IteratorFunction The function that will be called for each object in the database. The function needs to accept a CLIENT parameter.
-- @return #DATABASE self
function DATABASE:ForEachCargo( IteratorFunction, ... )
self:F2( arg )
self:ForEach( IteratorFunction, arg, self.CARGOS )
return self
end
--- Handles the OnEventNewCargo event.
-- @param #DATABASE self
-- @param Core.Event#EVENTDATA EventData
function DATABASE:OnEventNewCargo( EventData )
self:F2( { EventData } )
if EventData.Cargo then
self:AddCargo( EventData.Cargo )
end
end
--- Handles the OnEventDeleteCargo.
-- @param #DATABASE self
-- @param Core.Event#EVENTDATA EventData
function DATABASE:OnEventDeleteCargo( EventData )
self:F2( { EventData } )
if EventData.Cargo then
self:DeleteCargo( EventData.Cargo.Name )
end
end
--- @param #DATABASE self
function DATABASE:_RegisterTemplates()
self:F2()
@@ -744,6 +929,9 @@ function DATABASE:_RegisterTemplates()
local CountryName = string.upper(cntry_data.name)
local CountryID = cntry_data.id
self.COUNTRY_ID[CountryName] = CountryID
self.COUNTRY_NAME[CountryID] = CountryName
--self.Units[coa_name][countryName] = {}
--self.Units[coa_name][countryName]["countryId"] = cntry_data.id
@@ -759,11 +947,18 @@ function DATABASE:_RegisterTemplates()
--self.Units[coa_name][countryName][category] = {}
for group_num, GroupTemplate in pairs(obj_type_data.group) do
for group_num, Template in pairs(obj_type_data.group) do
if GroupTemplate and GroupTemplate.units and type(GroupTemplate.units) == 'table' then --making sure again- this is a valid group
self:_RegisterTemplate(
GroupTemplate,
if obj_type_name ~= "static" and Template and Template.units and type(Template.units) == 'table' then --making sure again- this is a valid group
self:_RegisterGroupTemplate(
Template,
CoalitionSide,
_DATABASECategory[string.lower(CategoryName)],
CountryID
)
else
self:_RegisterStaticTemplate(
Template,
CoalitionSide,
_DATABASECategory[string.lower(CategoryName)],
CountryID

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,12 @@
--- This module contains the **FSM** (**F**inite **S**tate **M**achine) class and derived **FSM\_** classes.
-- ## Finite State Machines (FSM) are design patterns allowing efficient (long-lasting) processes and workflows.
--- **Core** - The **FSM** (**F**inite **S**tate **M**achine) class and derived **FSM\_** classes
-- are design patterns allowing efficient (long-lasting) processes and workflows.
--
-- ![Banner Image](..\Presentations\FSM\Dia1.JPG)
--
-- ===
--
-- A Finite State Machine (FSM) models a process flow that transitions between various **States** through triggered **Events**.
--
-- A FSM can only be in one of a finite number of states.
-- The machine is in only one state at a time; the state it is in at any given time is called the **current state**.
-- It can change from one state to another when initiated by an **__internal__ or __external__ triggering event**, which is called a **transition**.
@@ -46,227 +48,14 @@
-- I've reworked this development (taken the concept), and created a **hierarchical state machine** out of it, embedded within the DCS simulator.
-- Additionally, I've added extendability and created an API that allows seamless FSM implementation.
--
-- ===
--
-- # 1) @{#FSM} class, extends @{Base#BASE}
--
-- ![Transition Rules and Transition Handlers and Event Triggers](..\Presentations\FSM\Dia3.JPG)
-- The following derived classes are available in the MOOSE framework, that implement a specialised form of a FSM:
--
-- The FSM class is the base class of all FSM\_ derived classes. It implements the main functionality to define and execute Finite State Machines.
-- The derived FSM\_ classes extend the Finite State Machine functionality to run a workflow process for a specific purpose or component.
-- * @{#FSM_TASK}: Models Finite State Machines for @{Task}s.
-- * @{#FSM_PROCESS}: Models Finite State Machines for @{Task} actions, which control @{Client}s.
-- * @{#FSM_CONTROLLABLE}: Models Finite State Machines for @{Controllable}s, which are @{Group}s, @{Unit}s, @{Client}s.
-- * @{#FSM_SET}: Models Finite State Machines for @{Set}s. Note that these FSMs control multiple objects!!! So State concerns here
-- for multiple objects or the position of the state machine in the process.
--
-- Finite State Machines have **Transition Rules**, **Transition Handlers** and **Event Triggers**.
--
-- The **Transition Rules** define the "Process Flow Boundaries", that is,
-- the path that can be followed hopping from state to state upon triggered events.
-- If an event is triggered, and there is no valid path found for that event,
-- an error will be raised and the FSM will stop functioning.
--
-- The **Transition Handlers** are special methods that can be defined by the mission designer, following a defined syntax.
-- If the FSM object finds a method of such a handler, then the method will be called by the FSM, passing specific parameters.
-- The method can then define its own custom logic to implement the FSM workflow, and to conduct other actions.
--
-- The **Event Triggers** are methods that are defined by the FSM, which the mission designer can use to implement the workflow.
-- Most of the time, these Event Triggers are used within the Transition Handler methods, so that a workflow is created running through the state machine.
--
-- As explained above, a FSM supports **Linear State Transitions** and **Hierarchical State Transitions**, and both can be mixed to make a comprehensive FSM implementation.
-- The below documentation has a seperate chapter explaining both transition modes, taking into account the **Transition Rules**, **Transition Handlers** and **Event Triggers**.
--
-- ## 1.1) FSM Linear Transitions
--
-- Linear Transitions are Transition Rules allowing an FSM to transition from one or multiple possible **From** state(s) towards a **To** state upon a Triggered **Event**.
-- The Lineair transition rule evaluation will always be done from the **current state** of the FSM.
-- If no valid Transition Rule can be found in the FSM, the FSM will log an error and stop.
--
-- ### 1.1.1) FSM Transition Rules
--
-- The FSM has transition rules that it follows and validates, as it walks the process.
-- These rules define when an FSM can transition from a specific state towards an other specific state upon a triggered event.
--
-- The method @{#FSM.AddTransition}() specifies a new possible Transition Rule for the FSM.
--
-- The initial state can be defined using the method @{#FSM.SetStartState}(). The default start state of an FSM is "None".
--
-- Find below an example of a Linear Transition Rule definition for an FSM.
--
-- local Fsm3Switch = FSM:New() -- #FsmDemo
-- FsmSwitch:SetStartState( "Off" )
-- FsmSwitch:AddTransition( "Off", "SwitchOn", "On" )
-- FsmSwitch:AddTransition( "Off", "SwitchMiddle", "Middle" )
-- FsmSwitch:AddTransition( "On", "SwitchOff", "Off" )
-- FsmSwitch:AddTransition( "Middle", "SwitchOff", "Off" )
--
-- The above code snippet models a 3-way switch Linear Transition:
--
-- * It can be switched **On** by triggering event **SwitchOn**.
-- * It can be switched to the **Middle** position, by triggering event **SwitchMiddle**.
-- * It can be switched **Off** by triggering event **SwitchOff**.
-- * Note that once the Switch is **On** or **Middle**, it can only be switched **Off**.
--
-- ### Some additional comments:
--
-- Note that Linear Transition Rules **can be declared in a few variations**:
--
-- * The From states can be **a table of strings**, indicating that the transition rule will be valid **if the current state** of the FSM will be **one of the given From states**.
-- * The From state can be a **"*"**, indicating that **the transition rule will always be valid**, regardless of the current state of the FSM.
--
-- The below code snippet shows how the two last lines can be rewritten and consensed.
--
-- FsmSwitch:AddTransition( { "On", "Middle" }, "SwitchOff", "Off" )
--
-- ### 1.1.2) Transition Handling
--
-- ![Transition Handlers](..\Presentations\FSM\Dia4.JPG)
--
-- An FSM transitions in **4 moments** when an Event is being triggered and processed.
-- The mission designer can define for each moment specific logic within methods implementations following a defined API syntax.
-- These methods define the flow of the FSM process; because in those methods the FSM Internal Events will be triggered.
--
-- * To handle **State** transition moments, create methods starting with OnLeave or OnEnter concatenated with the State name.
-- * To handle **Event** transition moments, create methods starting with OnBefore or OnAfter concatenated with the Event name.
--
-- **The OnLeave and OnBefore transition methods may return false, which will cancel the transition!**
--
-- Transition Handler methods need to follow the above specified naming convention, but are also passed parameters from the FSM.
-- These parameters are on the correct order: From, Event, To:
--
-- * From = A string containing the From state.
-- * Event = A string containing the Event name that was triggered.
-- * To = A string containing the To state.
--
-- On top, each of these methods can have a variable amount of parameters passed. See the example in section [1.1.3](#1.1.3\)-event-triggers).
--
-- ### 1.1.3) Event Triggers
--
-- ![Event Triggers](..\Presentations\FSM\Dia5.JPG)
--
-- The FSM creates for each Event two **Event Trigger methods**.
-- There are two modes how Events can be triggered, which is **synchronous** and **asynchronous**:
--
-- * The method **FSM:Event()** triggers an Event that will be processed **synchronously** or **immediately**.
-- * The method **FSM:__Event( __seconds__ )** triggers an Event that will be processed **asynchronously** over time, waiting __x seconds__.
--
-- The destinction between these 2 Event Trigger methods are important to understand. An asynchronous call will "log" the Event Trigger to be executed at a later time.
-- Processing will just continue. Synchronous Event Trigger methods are useful to change states of the FSM immediately, but may have a larger processing impact.
--
-- The following example provides a little demonstration on the difference between synchronous and asynchronous Event Triggering.
--
-- function FSM:OnAfterEvent( From, Event, To, Amount )
-- self:T( { Amount = Amount } )
-- end
--
-- local Amount = 1
-- FSM:__Event( 5, Amount )
--
-- Amount = Amount + 1
-- FSM:Event( Text, Amount )
--
-- In this example, the **:OnAfterEvent**() Transition Handler implementation will get called when **Event** is being triggered.
-- Before we go into more detail, let's look at the last 4 lines of the example.
-- The last line triggers synchronously the **Event**, and passes Amount as a parameter.
-- The 3rd last line of the example triggers asynchronously **Event**.
-- Event will be processed after 5 seconds, and Amount is given as a parameter.
--
-- The output of this little code fragment will be:
--
-- * Amount = 2
-- * Amount = 2
--
-- Because ... When Event was asynchronously processed after 5 seconds, Amount was set to 2. So be careful when processing and passing values and objects in asynchronous processing!
--
-- ### 1.1.4) Linear Transition Example
--
-- This example is fully implemented in the MOOSE test mission on GITHUB: [FSM-100 - Transition Explanation](https://github.com/FlightControl-Master/MOOSE/blob/master/Moose%20Test%20Missions/FSM%20-%20Finite%20State%20Machine/FSM-100%20-%20Transition%20Explanation/FSM-100%20-%20Transition%20Explanation.lua)
--
-- It models a unit standing still near Batumi, and flaring every 5 seconds while switching between a Green flare and a Red flare.
-- The purpose of this example is not to show how exciting flaring is, but it demonstrates how a Linear Transition FSM can be build.
-- Have a look at the source code. The source code is also further explained below in this section.
--
-- The example creates a new FsmDemo object from class FSM.
-- It will set the start state of FsmDemo to state **Green**.
-- Two Linear Transition Rules are created, where upon the event **Switch**,
-- the FsmDemo will transition from state **Green** to **Red** and from **Red** back to **Green**.
--
-- ![Transition Example](..\Presentations\FSM\Dia6.JPG)
--
-- local FsmDemo = FSM:New() -- #FsmDemo
-- FsmDemo:SetStartState( "Green" )
-- FsmDemo:AddTransition( "Green", "Switch", "Red" )
-- FsmDemo:AddTransition( "Red", "Switch", "Green" )
--
-- In the above example, the FsmDemo could flare every 5 seconds a Green or a Red flare into the air.
-- The next code implements this through the event handling method **OnAfterSwitch**.
--
-- ![Transition Flow](..\Presentations\FSM\Dia7.JPG)
--
-- function FsmDemo:OnAfterSwitch( From, Event, To, FsmUnit )
-- self:T( { From, Event, To, FsmUnit } )
--
-- if From == "Green" then
-- FsmUnit:Flare(FLARECOLOR.Green)
-- else
-- if From == "Red" then
-- FsmUnit:Flare(FLARECOLOR.Red)
-- end
-- end
-- self:__Switch( 5, FsmUnit ) -- Trigger the next Switch event to happen in 5 seconds.
-- end
--
-- FsmDemo:__Switch( 5, FsmUnit ) -- Trigger the first Switch event to happen in 5 seconds.
--
-- The OnAfterSwitch implements a loop. The last line of the code fragment triggers the Switch Event within 5 seconds.
-- Upon the event execution (after 5 seconds), the OnAfterSwitch method is called of FsmDemo (cfr. the double point notation!!! ":").
-- The OnAfterSwitch method receives from the FSM the 3 transition parameter details ( From, Event, To ),
-- and one additional parameter that was given when the event was triggered, which is in this case the Unit that is used within OnSwitchAfter.
--
-- function FsmDemo:OnAfterSwitch( From, Event, To, FsmUnit )
--
-- For debugging reasons the received parameters are traced within the DCS.log.
--
-- self:T( { From, Event, To, FsmUnit } )
--
-- The method will check if the From state received is either "Green" or "Red" and will flare the respective color from the FsmUnit.
--
-- if From == "Green" then
-- FsmUnit:Flare(FLARECOLOR.Green)
-- else
-- if From == "Red" then
-- FsmUnit:Flare(FLARECOLOR.Red)
-- end
-- end
--
-- It is important that the Switch event is again triggered, otherwise, the FsmDemo would stop working after having the first Event being handled.
--
-- FsmDemo:__Switch( 5, FsmUnit ) -- Trigger the next Switch event to happen in 5 seconds.
--
-- The below code fragment extends the FsmDemo, demonstrating multiple **From states declared as a table**, adding a **Linear Transition Rule**.
-- The new event **Stop** will cancel the Switching process.
-- The transition for event Stop can be executed if the current state of the FSM is either "Red" or "Green".
--
-- local FsmDemo = FSM:New() -- #FsmDemo
-- FsmDemo:SetStartState( "Green" )
-- FsmDemo:AddTransition( "Green", "Switch", "Red" )
-- FsmDemo:AddTransition( "Red", "Switch", "Green" )
-- FsmDemo:AddTransition( { "Red", "Green" }, "Stop", "Stopped" )
--
-- The transition for event Stop can also be simplified, as any current state of the FSM is valid.
--
-- FsmDemo:AddTransition( "*", "Stop", "Stopped" )
--
-- So... When FsmDemo:Stop() is being triggered, the state of FsmDemo will transition from Red or Green to Stopped.
-- And there is no transition handling method defined for that transition, thus, no new event is being triggered causing the FsmDemo process flow to halt.
--
-- ## 1.5) FSM Hierarchical Transitions
--
-- Hierarchical Transitions allow to re-use readily available and implemented FSMs.
-- This becomes in very useful for mission building, where mission designers build complex processes and workflows,
-- combining smaller FSMs to one single FSM.
--
-- The FSM can embed **Sub-FSMs** that will execute and return **multiple possible Return (End) States**.
-- Depending upon **which state is returned**, the main FSM can continue the flow **triggering specific events**.
--
-- The method @{#FSM.AddProcess}() adds a new Sub-FSM to the FSM.
--
-- ====
--
-- # **API CHANGE HISTORY**
@@ -299,9 +88,268 @@
do -- FSM
--- FSM class
-- @type FSM
--- @type FSM
-- @extends Core.Base#BASE
--- # FSM class, extends @{Base#BASE}
--
-- A Finite State Machine (FSM) models a process flow that transitions between various **States** through triggered **Events**.
--
-- A FSM can only be in one of a finite number of states.
-- The machine is in only one state at a time; the state it is in at any given time is called the **current state**.
-- It can change from one state to another when initiated by an **__internal__ or __external__ triggering event**, which is called a **transition**.
-- An **FSM implementation** is defined by **a list of its states**, **its initial state**, and **the triggering events** for **each possible transition**.
-- An FSM implementation is composed out of **two parts**, a set of **state transition rules**, and an implementation set of **state transition handlers**, implementing those transitions.
--
-- The FSM class supports a **hierarchical implementation of a Finite State Machine**,
-- that is, it allows to **embed existing FSM implementations in a master FSM**.
-- FSM hierarchies allow for efficient FSM re-use, **not having to re-invent the wheel every time again** when designing complex processes.
--
-- ![Workflow Example](..\Presentations\FSM\Dia2.JPG)
--
-- The above diagram shows a graphical representation of a FSM implementation for a **Task**, which guides a Human towards a Zone,
-- orders him to destroy x targets and account the results.
-- Other examples of ready made FSM could be:
--
-- * route a plane to a zone flown by a human
-- * detect targets by an AI and report to humans
-- * account for destroyed targets by human players
-- * handle AI infantry to deploy from or embark to a helicopter or airplane or vehicle
-- * let an AI patrol a zone
--
-- The **MOOSE framework** uses extensively the FSM class and derived FSM\_ classes,
-- because **the goal of MOOSE is to simplify mission design complexity for mission building**.
-- By efficiently utilizing the FSM class and derived classes, MOOSE allows mission designers to quickly build processes.
-- **Ready made FSM-based implementations classes** exist within the MOOSE framework that **can easily be re-used,
-- and tailored** by mission designers through **the implementation of Transition Handlers**.
-- Each of these FSM implementation classes start either with:
--
-- * an acronym **AI\_**, which indicates an FSM implementation directing **AI controlled** @{GROUP} and/or @{UNIT}. These AI\_ classes derive the @{#FSM_CONTROLLABLE} class.
-- * an acronym **TASK\_**, which indicates an FSM implementation executing a @{TASK} executed by Groups of players. These TASK\_ classes derive the @{#FSM_TASK} class.
-- * an acronym **ACT\_**, which indicates an Sub-FSM implementation, directing **Humans actions** that need to be done in a @{TASK}, seated in a @{CLIENT} (slot) or a @{UNIT} (CA join). These ACT\_ classes derive the @{#FSM_PROCESS} class.
--
-- ![Transition Rules and Transition Handlers and Event Triggers](..\Presentations\FSM\Dia3.JPG)
--
-- The FSM class is the base class of all FSM\_ derived classes. It implements the main functionality to define and execute Finite State Machines.
-- The derived FSM\_ classes extend the Finite State Machine functionality to run a workflow process for a specific purpose or component.
--
-- Finite State Machines have **Transition Rules**, **Transition Handlers** and **Event Triggers**.
--
-- The **Transition Rules** define the "Process Flow Boundaries", that is,
-- the path that can be followed hopping from state to state upon triggered events.
-- If an event is triggered, and there is no valid path found for that event,
-- an error will be raised and the FSM will stop functioning.
--
-- The **Transition Handlers** are special methods that can be defined by the mission designer, following a defined syntax.
-- If the FSM object finds a method of such a handler, then the method will be called by the FSM, passing specific parameters.
-- The method can then define its own custom logic to implement the FSM workflow, and to conduct other actions.
--
-- The **Event Triggers** are methods that are defined by the FSM, which the mission designer can use to implement the workflow.
-- Most of the time, these Event Triggers are used within the Transition Handler methods, so that a workflow is created running through the state machine.
--
-- As explained above, a FSM supports **Linear State Transitions** and **Hierarchical State Transitions**, and both can be mixed to make a comprehensive FSM implementation.
-- The below documentation has a seperate chapter explaining both transition modes, taking into account the **Transition Rules**, **Transition Handlers** and **Event Triggers**.
--
-- ## FSM Linear Transitions
--
-- Linear Transitions are Transition Rules allowing an FSM to transition from one or multiple possible **From** state(s) towards a **To** state upon a Triggered **Event**.
-- The Lineair transition rule evaluation will always be done from the **current state** of the FSM.
-- If no valid Transition Rule can be found in the FSM, the FSM will log an error and stop.
--
-- ### FSM Transition Rules
--
-- The FSM has transition rules that it follows and validates, as it walks the process.
-- These rules define when an FSM can transition from a specific state towards an other specific state upon a triggered event.
--
-- The method @{#FSM.AddTransition}() specifies a new possible Transition Rule for the FSM.
--
-- The initial state can be defined using the method @{#FSM.SetStartState}(). The default start state of an FSM is "None".
--
-- Find below an example of a Linear Transition Rule definition for an FSM.
--
-- local Fsm3Switch = FSM:New() -- #FsmDemo
-- FsmSwitch:SetStartState( "Off" )
-- FsmSwitch:AddTransition( "Off", "SwitchOn", "On" )
-- FsmSwitch:AddTransition( "Off", "SwitchMiddle", "Middle" )
-- FsmSwitch:AddTransition( "On", "SwitchOff", "Off" )
-- FsmSwitch:AddTransition( "Middle", "SwitchOff", "Off" )
--
-- The above code snippet models a 3-way switch Linear Transition:
--
-- * It can be switched **On** by triggering event **SwitchOn**.
-- * It can be switched to the **Middle** position, by triggering event **SwitchMiddle**.
-- * It can be switched **Off** by triggering event **SwitchOff**.
-- * Note that once the Switch is **On** or **Middle**, it can only be switched **Off**.
--
-- #### Some additional comments:
--
-- Note that Linear Transition Rules **can be declared in a few variations**:
--
-- * The From states can be **a table of strings**, indicating that the transition rule will be valid **if the current state** of the FSM will be **one of the given From states**.
-- * The From state can be a **"*"**, indicating that **the transition rule will always be valid**, regardless of the current state of the FSM.
--
-- The below code snippet shows how the two last lines can be rewritten and consensed.
--
-- FsmSwitch:AddTransition( { "On", "Middle" }, "SwitchOff", "Off" )
--
-- ### Transition Handling
--
-- ![Transition Handlers](..\Presentations\FSM\Dia4.JPG)
--
-- An FSM transitions in **4 moments** when an Event is being triggered and processed.
-- The mission designer can define for each moment specific logic within methods implementations following a defined API syntax.
-- These methods define the flow of the FSM process; because in those methods the FSM Internal Events will be triggered.
--
-- * To handle **State** transition moments, create methods starting with OnLeave or OnEnter concatenated with the State name.
-- * To handle **Event** transition moments, create methods starting with OnBefore or OnAfter concatenated with the Event name.
--
-- **The OnLeave and OnBefore transition methods may return false, which will cancel the transition!**
--
-- Transition Handler methods need to follow the above specified naming convention, but are also passed parameters from the FSM.
-- These parameters are on the correct order: From, Event, To:
--
-- * From = A string containing the From state.
-- * Event = A string containing the Event name that was triggered.
-- * To = A string containing the To state.
--
-- On top, each of these methods can have a variable amount of parameters passed. See the example in section [1.1.3](#1.1.3\)-event-triggers).
--
-- ### Event Triggers
--
-- ![Event Triggers](..\Presentations\FSM\Dia5.JPG)
--
-- The FSM creates for each Event two **Event Trigger methods**.
-- There are two modes how Events can be triggered, which is **synchronous** and **asynchronous**:
--
-- * The method **FSM:Event()** triggers an Event that will be processed **synchronously** or **immediately**.
-- * The method **FSM:__Event( __seconds__ )** triggers an Event that will be processed **asynchronously** over time, waiting __x seconds__.
--
-- The destinction between these 2 Event Trigger methods are important to understand. An asynchronous call will "log" the Event Trigger to be executed at a later time.
-- Processing will just continue. Synchronous Event Trigger methods are useful to change states of the FSM immediately, but may have a larger processing impact.
--
-- The following example provides a little demonstration on the difference between synchronous and asynchronous Event Triggering.
--
-- function FSM:OnAfterEvent( From, Event, To, Amount )
-- self:T( { Amount = Amount } )
-- end
--
-- local Amount = 1
-- FSM:__Event( 5, Amount )
--
-- Amount = Amount + 1
-- FSM:Event( Text, Amount )
--
-- In this example, the **:OnAfterEvent**() Transition Handler implementation will get called when **Event** is being triggered.
-- Before we go into more detail, let's look at the last 4 lines of the example.
-- The last line triggers synchronously the **Event**, and passes Amount as a parameter.
-- The 3rd last line of the example triggers asynchronously **Event**.
-- Event will be processed after 5 seconds, and Amount is given as a parameter.
--
-- The output of this little code fragment will be:
--
-- * Amount = 2
-- * Amount = 2
--
-- Because ... When Event was asynchronously processed after 5 seconds, Amount was set to 2. So be careful when processing and passing values and objects in asynchronous processing!
--
-- ### Linear Transition Example
--
-- This example is fully implemented in the MOOSE test mission on GITHUB: [FSM-100 - Transition Explanation](https://github.com/FlightControl-Master/MOOSE/blob/master/Moose%20Test%20Missions/FSM%20-%20Finite%20State%20Machine/FSM-100%20-%20Transition%20Explanation/FSM-100%20-%20Transition%20Explanation.lua)
--
-- It models a unit standing still near Batumi, and flaring every 5 seconds while switching between a Green flare and a Red flare.
-- The purpose of this example is not to show how exciting flaring is, but it demonstrates how a Linear Transition FSM can be build.
-- Have a look at the source code. The source code is also further explained below in this section.
--
-- The example creates a new FsmDemo object from class FSM.
-- It will set the start state of FsmDemo to state **Green**.
-- Two Linear Transition Rules are created, where upon the event **Switch**,
-- the FsmDemo will transition from state **Green** to **Red** and from **Red** back to **Green**.
--
-- ![Transition Example](..\Presentations\FSM\Dia6.JPG)
--
-- local FsmDemo = FSM:New() -- #FsmDemo
-- FsmDemo:SetStartState( "Green" )
-- FsmDemo:AddTransition( "Green", "Switch", "Red" )
-- FsmDemo:AddTransition( "Red", "Switch", "Green" )
--
-- In the above example, the FsmDemo could flare every 5 seconds a Green or a Red flare into the air.
-- The next code implements this through the event handling method **OnAfterSwitch**.
--
-- ![Transition Flow](..\Presentations\FSM\Dia7.JPG)
--
-- function FsmDemo:OnAfterSwitch( From, Event, To, FsmUnit )
-- self:T( { From, Event, To, FsmUnit } )
--
-- if From == "Green" then
-- FsmUnit:Flare(FLARECOLOR.Green)
-- else
-- if From == "Red" then
-- FsmUnit:Flare(FLARECOLOR.Red)
-- end
-- end
-- self:__Switch( 5, FsmUnit ) -- Trigger the next Switch event to happen in 5 seconds.
-- end
--
-- FsmDemo:__Switch( 5, FsmUnit ) -- Trigger the first Switch event to happen in 5 seconds.
--
-- The OnAfterSwitch implements a loop. The last line of the code fragment triggers the Switch Event within 5 seconds.
-- Upon the event execution (after 5 seconds), the OnAfterSwitch method is called of FsmDemo (cfr. the double point notation!!! ":").
-- The OnAfterSwitch method receives from the FSM the 3 transition parameter details ( From, Event, To ),
-- and one additional parameter that was given when the event was triggered, which is in this case the Unit that is used within OnSwitchAfter.
--
-- function FsmDemo:OnAfterSwitch( From, Event, To, FsmUnit )
--
-- For debugging reasons the received parameters are traced within the DCS.log.
--
-- self:T( { From, Event, To, FsmUnit } )
--
-- The method will check if the From state received is either "Green" or "Red" and will flare the respective color from the FsmUnit.
--
-- if From == "Green" then
-- FsmUnit:Flare(FLARECOLOR.Green)
-- else
-- if From == "Red" then
-- FsmUnit:Flare(FLARECOLOR.Red)
-- end
-- end
--
-- It is important that the Switch event is again triggered, otherwise, the FsmDemo would stop working after having the first Event being handled.
--
-- FsmDemo:__Switch( 5, FsmUnit ) -- Trigger the next Switch event to happen in 5 seconds.
--
-- The below code fragment extends the FsmDemo, demonstrating multiple **From states declared as a table**, adding a **Linear Transition Rule**.
-- The new event **Stop** will cancel the Switching process.
-- The transition for event Stop can be executed if the current state of the FSM is either "Red" or "Green".
--
-- local FsmDemo = FSM:New() -- #FsmDemo
-- FsmDemo:SetStartState( "Green" )
-- FsmDemo:AddTransition( "Green", "Switch", "Red" )
-- FsmDemo:AddTransition( "Red", "Switch", "Green" )
-- FsmDemo:AddTransition( { "Red", "Green" }, "Stop", "Stopped" )
--
-- The transition for event Stop can also be simplified, as any current state of the FSM is valid.
--
-- FsmDemo:AddTransition( "*", "Stop", "Stopped" )
--
-- So... When FsmDemo:Stop() is being triggered, the state of FsmDemo will transition from Red or Green to Stopped.
-- And there is no transition handling method defined for that transition, thus, no new event is being triggered causing the FsmDemo process flow to halt.
--
-- ## FSM Hierarchical Transitions
--
-- Hierarchical Transitions allow to re-use readily available and implemented FSMs.
-- This becomes in very useful for mission building, where mission designers build complex processes and workflows,
-- combining smaller FSMs to one single FSM.
--
-- The FSM can embed **Sub-FSMs** that will execute and return **multiple possible Return (End) States**.
-- Depending upon **which state is returned**, the main FSM can continue the flow **triggering specific events**.
--
-- The method @{#FSM.AddProcess}() adds a new Sub-FSM to the FSM.
--
-- ===
--
-- @field #FSM FSM
--
FSM = {
ClassName = "FSM",
}
@@ -420,7 +468,6 @@ do -- FSM
for ProcessID, Process in pairs( self:GetProcesses() ) do
if Process.From == From and Process.Event == Event then
self:T( Process )
return Process.fsm
end
end
@@ -449,7 +496,7 @@ do -- FSM
-- @param #number Score is a number providing the score of the status.
-- @return #FSM self
function FSM:AddScore( State, ScoreText, Score )
self:F2( { State, ScoreText, Score } )
self:F( { State, ScoreText, Score } )
self._Scores[State] = self._Scores[State] or {}
self._Scores[State].ScoreText = ScoreText
@@ -467,14 +514,15 @@ do -- FSM
-- @param #number Score is a number providing the score of the status.
-- @return #FSM self
function FSM:AddScoreProcess( From, Event, State, ScoreText, Score )
self:F2( { Event, State, ScoreText, Score } )
self:F( { From, Event, State, ScoreText, Score } )
local Process = self:GetProcess( From, Event )
self:T( { Process = Process._Name, Scores = Process._Scores, State = State, ScoreText = ScoreText, Score = Score } )
Process._Scores[State] = Process._Scores[State] or {}
Process._Scores[State].ScoreText = ScoreText
Process._Scores[State].Score = Score
self:T( Process._Scores )
return Process
end
@@ -530,10 +578,20 @@ do -- FSM
function FSM:_call_handler( handler, params, EventName )
local ErrorHandler = function( errmsg )
env.info( "Error in SCHEDULER function:" .. errmsg )
if debug ~= nil then
env.info( debug.traceback() )
end
return errmsg
end
if self[handler] then
self:T( "Calling " .. handler )
self._EventSchedules[EventName] = nil
local Value = self[handler]( self, unpack(params) )
local Result, Value = xpcall( function() return self[handler]( self, unpack( params ) ) end, ErrorHandler )
return Value
end
end
@@ -710,10 +768,18 @@ end
do -- FSM_CONTROLLABLE
--- FSM_CONTROLLABLE class
-- @type FSM_CONTROLLABLE
--- @type FSM_CONTROLLABLE
-- @field Wrapper.Controllable#CONTROLLABLE Controllable
-- @extends Core.Fsm#FSM
--- # FSM_CONTROLLABLE, extends @{#FSM}
--
-- FSM_CONTROLLABLE class models Finite State Machines for @{Controllable}s, which are @{Group}s, @{Unit}s, @{Client}s.
--
-- ===
--
-- @field #FSM_CONTROLLABLE FSM_CONTROLLABLE
--
FSM_CONTROLLABLE = {
ClassName = "FSM_CONTROLLABLE",
}
@@ -732,8 +798,66 @@ do -- FSM_CONTROLLABLE
self:SetControllable( Controllable )
end
self:AddTransition( "*", "Stop", "Stopped" )
--- OnBefore Transition Handler for Event Stop.
-- @function [parent=#FSM_CONTROLLABLE] OnBeforeStop
-- @param #FSM_CONTROLLABLE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Stop.
-- @function [parent=#FSM_CONTROLLABLE] OnAfterStop
-- @param #FSM_CONTROLLABLE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Stop.
-- @function [parent=#FSM_CONTROLLABLE] Stop
-- @param #FSM_CONTROLLABLE self
--- Asynchronous Event Trigger for Event Stop.
-- @function [parent=#FSM_CONTROLLABLE] __Stop
-- @param #FSM_CONTROLLABLE self
-- @param #number Delay The delay in seconds.
--- OnLeave Transition Handler for State Stopped.
-- @function [parent=#FSM_CONTROLLABLE] OnLeaveStopped
-- @param #FSM_CONTROLLABLE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnEnter Transition Handler for State Stopped.
-- @function [parent=#FSM_CONTROLLABLE] OnEnterStopped
-- @param #FSM_CONTROLLABLE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
return self
end
--- OnAfter Transition Handler for Event Stop.
-- @function [parent=#FSM_CONTROLLABLE] OnAfterStop
-- @param #FSM_CONTROLLABLE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function FSM_CONTROLLABLE:OnAfterStop(Controllable,From,Event,To)
-- Clear all pending schedules
self.CallScheduler:Clear()
end
--- Sets the CONTROLLABLE object that the FSM_CONTROLLABLE governs.
-- @param #FSM_CONTROLLABLE self
@@ -776,10 +900,19 @@ end
do -- FSM_PROCESS
--- FSM_PROCESS class
-- @type FSM_PROCESS
--- @type FSM_PROCESS
-- @field Tasking.Task#TASK Task
-- @extends Core.Fsm#FSM_CONTROLLABLE
--- # FSM_PROCESS, extends @{#FSM}
--
-- FSM_PROCESS class models Finite State Machines for @{Task} actions, which control @{Client}s.
--
-- ===
--
-- @field #FSM_PROCESS FSM_PROCESS
--
FSM_PROCESS = {
ClassName = "FSM_PROCESS",
}
@@ -801,12 +934,34 @@ do -- FSM_PROCESS
function FSM_PROCESS:Init( FsmProcess )
self:T( "No Initialisation" )
end
function FSM_PROCESS:_call_handler( handler, params, EventName )
local ErrorHandler = function( errmsg )
env.info( "Error in FSM_PROCESS call handler:" .. errmsg )
if debug ~= nil then
env.info( debug.traceback() )
end
return errmsg
end
if self[handler] then
self:F3( "Calling " .. handler )
self._EventSchedules[EventName] = nil
local Result, Value = xpcall( function() return self[handler]( self, self.Controllable, self.Task, unpack( params ) ) end, ErrorHandler )
return Value
--return self[handler]( self, self.Controllable, unpack( params ) )
end
end
--- Creates a new FSM_PROCESS object based on this FSM_PROCESS.
-- @param #FSM_PROCESS self
-- @return #FSM_PROCESS
function FSM_PROCESS:Copy( Controllable, Task )
self:T( { self:GetClassNameAndID() } )
local NewFsm = self:New( Controllable, Task ) -- Core.Fsm#FSM_PROCESS
@@ -825,7 +980,7 @@ do -- FSM_PROCESS
-- Copy Processes
for ProcessID, Process in pairs( self:GetProcesses() ) do
self:T( { Process} )
self:E( { Process} )
local FsmProcess = NewFsm:AddProcess( Process.From, Process.Event, Process.fsm:Copy( Controllable, Task ), Process.ReturnEvents )
end
@@ -843,6 +998,27 @@ do -- FSM_PROCESS
return NewFsm
end
--- Removes an FSM_PROCESS object.
-- @param #FSM_PROCESS self
-- @return #FSM_PROCESS
function FSM_PROCESS:Remove()
self:F( { self:GetClassNameAndID() } )
self:F( "Clearing Schedules" )
self.CallScheduler:Clear()
-- Copy Processes
for ProcessID, Process in pairs( self:GetProcesses() ) do
self:E( { Process} )
if Process.fsm then
Process.fsm:Remove()
Process.fsm = nil
end
end
return self
end
--- Sets the task of the process.
-- @param #FSM_PROCESS self
@@ -941,14 +1117,14 @@ end
-- @param #string Event
-- @param #string From
-- @param #string To
function FSM_PROCESS:onstatechange( ProcessUnit, From, Event, To, Dummy )
function FSM_PROCESS:onstatechange( ProcessUnit, Task, From, Event, To, Dummy )
self:T( { ProcessUnit, From, Event, To, Dummy, self:IsTrace() } )
if self:IsTrace() then
MESSAGE:New( "@ Process " .. self:GetClassNameAndID() .. " : " .. Event .. " changed to state " .. To, 2 ):ToAll()
--MESSAGE:New( "@ Process " .. self:GetClassNameAndID() .. " : " .. Event .. " changed to state " .. To, 2 ):ToAll()
end
self:T( self._Scores[To] )
self:T( { Scores = self._Scores, To = To } )
-- TODO: This needs to be reworked with a callback functions allocated within Task, and set within the mission script from the Task Objects...
if self._Scores[To] then
@@ -967,7 +1143,16 @@ do -- FSM_TASK
--- FSM_TASK class
-- @type FSM_TASK
-- @field Tasking.Task#TASK Task
-- @extends Core.Fsm#FSM
-- @extends #FSM
--- # FSM_TASK, extends @{#FSM}
--
-- FSM_TASK class models Finite State Machines for @{Task}s.
--
-- ===
--
-- @field #FSM_TASK FSM_TASK
--
FSM_TASK = {
ClassName = "FSM_TASK",
}
@@ -1003,6 +1188,17 @@ do -- FSM_SET
-- @type FSM_SET
-- @field Core.Set#SET_BASE Set
-- @extends Core.Fsm#FSM
--- # FSM_SET, extends @{#FSM}
--
-- FSM_SET class models Finite State Machines for @{Set}s. Note that these FSMs control multiple objects!!! So State concerns here
-- for multiple objects or the position of the state machine in the process.
--
-- ===
--
-- @field #FSM_SET FSM_SET
--
FSM_SET = {
ClassName = "FSM_SET",
}

View File

@@ -1,4 +1,4 @@
--- This module contains the MENU classes.
--- **Core** -- MENU_ classes model the definition of **hierarchical menu structures** and **commands for players** within a mission.
--
-- ===
--
@@ -27,109 +27,26 @@
--
-- ===
--
-- The above menus classes **are derived** from 2 main **abstract** classes defined within the MOOSE framework (so don't use these):
-- # **AUTHORS and CONTRIBUTIONS**
--
-- 1) MENU_ BASE abstract base classes (don't use them)
-- ====================================================
-- The underlying base menu classes are **NOT** to be used within your missions.
-- These are simply abstract base classes defining a couple of fields that are used by the
-- derived MENU_ classes to manage menus.
-- ### Contributions:
--
-- 1.1) @{#MENU_BASE} class, extends @{Base#BASE}
-- --------------------------------------------------
-- The @{#MENU_BASE} class defines the main MENU class where other MENU classes are derived from.
--
-- 1.2) @{#MENU_COMMAND_BASE} class, extends @{Base#BASE}
-- ----------------------------------------------------------
-- The @{#MENU_COMMAND_BASE} class defines the main MENU class where other MENU COMMAND_ classes are derived from, in order to set commands.
--
-- ===
--
-- **The next menus define the MENU classes that you can use within your missions.**
--
-- 2) MENU MISSION classes
-- ======================
-- The underlying classes manage the menus for a complete mission file.
--
-- 2.1) @{#MENU_MISSION} class, extends @{Menu#MENU_BASE}
-- ---------------------------------------------------------
-- The @{Menu#MENU_MISSION} class manages the main menus for a complete mission.
-- You can add menus with the @{#MENU_MISSION.New} method, which constructs a MENU_MISSION object and returns you the object reference.
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_MISSION.Remove}.
--
-- 2.2) @{#MENU_MISSION_COMMAND} class, extends @{Menu#MENU_COMMAND_BASE}
-- -------------------------------------------------------------------------
-- The @{Menu#MENU_MISSION_COMMAND} class manages the command menus for a complete mission, which allow players to execute functions during mission execution.
-- You can add menus with the @{#MENU_MISSION_COMMAND.New} method, which constructs a MENU_MISSION_COMMAND object and returns you the object reference.
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_MISSION_COMMAND.Remove}.
--
-- ===
--
-- 3) MENU COALITION classes
-- =========================
-- The underlying classes manage the menus for whole coalitions.
--
-- 3.1) @{#MENU_COALITION} class, extends @{Menu#MENU_BASE}
-- ------------------------------------------------------------
-- The @{Menu#MENU_COALITION} class manages the main menus for coalitions.
-- You can add menus with the @{#MENU_COALITION.New} method, which constructs a MENU_COALITION object and returns you the object reference.
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_COALITION.Remove}.
--
-- 3.2) @{Menu#MENU_COALITION_COMMAND} class, extends @{Menu#MENU_COMMAND_BASE}
-- ----------------------------------------------------------------------------
-- The @{Menu#MENU_COALITION_COMMAND} class manages the command menus for coalitions, which allow players to execute functions during mission execution.
-- You can add menus with the @{#MENU_COALITION_COMMAND.New} method, which constructs a MENU_COALITION_COMMAND object and returns you the object reference.
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_COALITION_COMMAND.Remove}.
--
-- ===
--
-- 4) MENU GROUP classes
-- =====================
-- The underlying classes manage the menus for groups. Note that groups can be inactive, alive or can be destroyed.
--
-- 4.1) @{Menu#MENU_GROUP} class, extends @{Menu#MENU_BASE}
-- --------------------------------------------------------
-- The @{Menu#MENU_GROUP} class manages the main menus for coalitions.
-- You can add menus with the @{#MENU_GROUP.New} method, which constructs a MENU_GROUP object and returns you the object reference.
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_GROUP.Remove}.
--
-- 4.2) @{Menu#MENU_GROUP_COMMAND} class, extends @{Menu#MENU_COMMAND_BASE}
-- ------------------------------------------------------------------------
-- The @{Menu#MENU_GROUP_COMMAND} class manages the command menus for coalitions, which allow players to execute functions during mission execution.
-- You can add menus with the @{#MENU_GROUP_COMMAND.New} method, which constructs a MENU_GROUP_COMMAND object and returns you the object reference.
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_GROUP_COMMAND.Remove}.
--
-- ===
--
-- 5) MENU CLIENT classes
-- ======================
-- The underlying classes manage the menus for units with skill level client or player.
--
-- 5.1) @{Menu#MENU_CLIENT} class, extends @{Menu#MENU_BASE}
-- ---------------------------------------------------------
-- The @{Menu#MENU_CLIENT} class manages the main menus for coalitions.
-- You can add menus with the @{#MENU_CLIENT.New} method, which constructs a MENU_CLIENT object and returns you the object reference.
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_CLIENT.Remove}.
--
-- 5.2) @{Menu#MENU_CLIENT_COMMAND} class, extends @{Menu#MENU_COMMAND_BASE}
-- -------------------------------------------------------------------------
-- The @{Menu#MENU_CLIENT_COMMAND} class manages the command menus for coalitions, which allow players to execute functions during mission execution.
-- You can add menus with the @{#MENU_CLIENT_COMMAND.New} method, which constructs a MENU_CLIENT_COMMAND object and returns you the object reference.
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_CLIENT_COMMAND.Remove}.
--
-- ===
--
-- ### Contributions: -
-- ### Authors: FlightControl : Design & Programming
-- ### Authors:
--
-- * **FlightControl**: Design & Programming
--
-- @module Menu
do -- MENU_BASE
--- The MENU_BASE class
-- @type MENU_BASE
--- @type MENU_BASE
-- @extends Base#BASE
--- # MENU_BASE class, extends @{Base#BASE}
-- The MENU_BASE class defines the main MENU class where other MENU classes are derived from.
-- This is an abstract class, so don't use it.
-- @field #MENU_BASE
MENU_BASE = {
ClassName = "MENU_BASE",
MenuPath = nil,
@@ -138,6 +55,8 @@ do -- MENU_BASE
}
--- Consructor
-- @param #MENU_BASE
-- @return #MENU_BASE
function MENU_BASE:New( MenuText, ParentMenu )
local MenuParentPath = {}
@@ -150,18 +69,56 @@ do -- MENU_BASE
self.MenuPath = nil
self.MenuText = MenuText
self.MenuParentPath = MenuParentPath
self.Menus = {}
self.MenuCount = 0
self.MenuRemoveParent = false
self.MenuTime = timer.getTime()
return self
end
--- Gets a @{Menu} from a parent @{Menu}
-- @param #MENU_BASE self
-- @param #string MenuText The text of the child menu.
-- @return #MENU_BASE
function MENU_BASE:GetMenu( MenuText )
self:F2( { Menu = self.Menus[MenuText] } )
return self.Menus[MenuText]
end
--- Sets a @{Menu} to remove automatically the parent menu when the menu removed is the last child menu of that parent @{Menu}.
-- @param #MENU_BASE self
-- @param #boolean RemoveParent If true, the parent menu is automatically removed when this menu is the last child menu of that parent @{Menu}.
-- @return #MENU_BASE
function MENU_BASE:SetRemoveParent( RemoveParent )
self:F2( { RemoveParent } )
self.MenuRemoveParent = RemoveParent
return self
end
--- Sets a time stamp for later prevention of menu removal.
-- @param #MENU_BASE self
-- @param MenuTime
-- @return #MENU_BASE
function MENU_BASE:SetTime( MenuTime )
self.MenuTime = MenuTime
return self
end
end
do -- MENU_COMMAND_BASE
--- The MENU_COMMAND_BASE class
-- @type MENU_COMMAND_BASE
--- @type MENU_COMMAND_BASE
-- @field #function MenuCallHandler
-- @extends Menu#MENU_BASE
-- @extends Core.Menu#MENU_BASE
--- # MENU_COMMAND_BASE class, extends @{Base#BASE}
-- ----------------------------------------------------------
-- The MENU_COMMAND_BASE class defines the main MENU class where other MENU COMMAND_
-- classes are derived from, in order to set commands.
-- @field #MENU_COMMAND_BASE
MENU_COMMAND_BASE = {
ClassName = "MENU_COMMAND_BASE",
CommandMenuFunction = nil,
@@ -170,6 +127,8 @@ do -- MENU_COMMAND_BASE
}
--- Constructor
-- @param #MENU_COMMAND_BASE
-- @return #MENU_COMMAND_BASE
function MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, CommandMenuArguments )
local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) )
@@ -187,9 +146,15 @@ end
do -- MENU_MISSION
--- The MENU_MISSION class
-- @type MENU_MISSION
-- @extends Menu#MENU_BASE
--- @type MENU_MISSION
-- @extends Core.Menu#MENU_BASE
--- # MENU_MISSION class, extends @{Menu#MENU_BASE}
--
-- The MENU_MISSION class manages the main menus for a complete mission.
-- You can add menus with the @{#MENU_MISSION.New} method, which constructs a MENU_MISSION object and returns you the object reference.
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_MISSION.Remove}.
-- @field #MENU_MISSION
MENU_MISSION = {
ClassName = "MENU_MISSION"
}
@@ -198,7 +163,7 @@ do -- MENU_MISSION
-- @param #MENU_MISSION self
-- @param #string MenuText The text for the menu.
-- @param #table ParentMenu The parent menu. This parameter can be ignored if you want the menu to be located at the perent menu of DCS world (under F10 other).
-- @return #MENU_MISSION self
-- @return #MENU_MISSION
function MENU_MISSION:New( MenuText, ParentMenu )
local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) )
@@ -225,7 +190,7 @@ do -- MENU_MISSION
--- Removes the sub menus recursively of this MENU_MISSION. Note that the main menu is kept!
-- @param #MENU_MISSION self
-- @return #MENU_MISSION self
-- @return #MENU_MISSION
function MENU_MISSION:RemoveSubMenus()
self:F( self.MenuPath )
@@ -254,9 +219,16 @@ end
do -- MENU_MISSION_COMMAND
--- The MENU_MISSION_COMMAND class
-- @type MENU_MISSION_COMMAND
-- @extends Menu#MENU_COMMAND_BASE
--- @type MENU_MISSION_COMMAND
-- @extends Core.Menu#MENU_COMMAND_BASE
--- # MENU_MISSION_COMMAND class, extends @{Menu#MENU_COMMAND_BASE}
--
-- The MENU_MISSION_COMMAND class manages the command menus for a complete mission, which allow players to execute functions during mission execution.
-- You can add menus with the @{#MENU_MISSION_COMMAND.New} method, which constructs a MENU_MISSION_COMMAND object and returns you the object reference.
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_MISSION_COMMAND.Remove}.
--
-- @field #MENU_MISSION_COMMAND
MENU_MISSION_COMMAND = {
ClassName = "MENU_MISSION_COMMAND"
}
@@ -304,9 +276,16 @@ end
do -- MENU_COALITION
--- The MENU_COALITION class
-- @type MENU_COALITION
-- @extends Menu#MENU_BASE
--- @type MENU_COALITION
-- @extends Core.Menu#MENU_BASE
--- # MENU_COALITION class, extends @{Menu#MENU_BASE}
--
-- The @{Menu#MENU_COALITION} class manages the main menus for coalitions.
-- You can add menus with the @{#MENU_COALITION.New} method, which constructs a MENU_COALITION object and returns you the object reference.
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_COALITION.Remove}.
--
--
-- @usage
-- -- This demo creates a menu structure for the planes within the red coalition.
-- -- To test, join the planes, then look at the other radio menus (Option F10).
@@ -343,6 +322,8 @@ do -- MENU_COALITION
--
-- local MenuAdd = MENU_COALITION_COMMAND:New( coalition.side.RED, "Add Status Menu", MenuCoalitionRed, AddStatusMenu )
-- local MenuRemove = MENU_COALITION_COMMAND:New( coalition.side.RED, "Remove Status Menu", MenuCoalitionRed, RemoveStatusMenu )
--
-- @field #MENU_COALITION
MENU_COALITION = {
ClassName = "MENU_COALITION"
}
@@ -380,7 +361,7 @@ do -- MENU_COALITION
--- Removes the sub menus recursively of this MENU_COALITION. Note that the main menu is kept!
-- @param #MENU_COALITION self
-- @return #MENU_COALITION self
-- @return #MENU_COALITION
function MENU_COALITION:RemoveSubMenus()
self:F( self.MenuPath )
@@ -409,9 +390,16 @@ end
do -- MENU_COALITION_COMMAND
--- The MENU_COALITION_COMMAND class
-- @type MENU_COALITION_COMMAND
-- @extends Menu#MENU_COMMAND_BASE
--- @type MENU_COALITION_COMMAND
-- @extends Core.Menu#MENU_COMMAND_BASE
--- # MENU_COALITION_COMMAND class, extends @{Menu#MENU_COMMAND_BASE}
--
-- The MENU_COALITION_COMMAND class manages the command menus for coalitions, which allow players to execute functions during mission execution.
-- You can add menus with the @{#MENU_COALITION_COMMAND.New} method, which constructs a MENU_COALITION_COMMAND object and returns you the object reference.
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_COALITION_COMMAND.Remove}.
--
-- @field #MENU_COALITION_COMMAND
MENU_COALITION_COMMAND = {
ClassName = "MENU_COALITION_COMMAND"
}
@@ -423,7 +411,7 @@ do -- MENU_COALITION_COMMAND
-- @param Menu#MENU_COALITION ParentMenu The parent menu.
-- @param CommandMenuFunction A function that is called when the menu key is pressed.
-- @param CommandMenuArgument An argument for the function. There can only be ONE argument given. So multiple arguments must be wrapped into a table. See the below example how to do this.
-- @return #MENU_COALITION_COMMAND self
-- @return #MENU_COALITION_COMMAND
function MENU_COALITION_COMMAND:New( Coalition, MenuText, ParentMenu, CommandMenuFunction, ... )
local self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) )
@@ -468,7 +456,15 @@ do -- MENU_CLIENT
--- MENU_COALITION constructor. Creates a new radio command item for a coalition, which can invoke a function with parameters.
-- @type MENU_CLIENT
-- @extends Menu#MENU_BASE
-- @extends Core.Menu#MENU_BASE
--- # MENU_CLIENT class, extends @{Menu#MENU_BASE}
--
-- The MENU_CLIENT class manages the main menus for coalitions.
-- You can add menus with the @{#MENU_CLIENT.New} method, which constructs a MENU_CLIENT object and returns you the object reference.
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_CLIENT.Remove}.
--
-- @usage
-- -- This demo creates a menu structure for the two clients of planes.
-- -- Each client will receive a different menu structure.
@@ -518,6 +514,8 @@ do -- MENU_CLIENT
-- MENU_CLIENT_COMMAND:New( PlaneClient, "Remove Status Menu Plane 2", MenuManage, RemoveStatusMenu, PlaneClient )
-- end
-- end, {}, 10, 10 )
--
-- @field #MENU_CLIENT
MENU_CLIENT = {
ClassName = "MENU_CLIENT"
}
@@ -607,9 +605,16 @@ do -- MENU_CLIENT
end
--- The MENU_CLIENT_COMMAND class
-- @type MENU_CLIENT_COMMAND
-- @extends Menu#MENU_COMMAND
--- @type MENU_CLIENT_COMMAND
-- @extends Core.Menu#MENU_COMMAND
--- # MENU_CLIENT_COMMAND class, extends @{Menu#MENU_COMMAND_BASE}
--
-- The MENU_CLIENT_COMMAND class manages the command menus for coalitions, which allow players to execute functions during mission execution.
-- You can add menus with the @{#MENU_CLIENT_COMMAND.New} method, which constructs a MENU_CLIENT_COMMAND object and returns you the object reference.
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_CLIENT_COMMAND.Remove}.
--
-- @field #MENU_CLIENT_COMMAND
MENU_CLIENT_COMMAND = {
ClassName = "MENU_CLIENT_COMMAND"
}
@@ -693,9 +698,16 @@ do
-- These menu classes are handling this logic with this variable.
local _MENUGROUPS = {}
--- The MENU_GROUP class
-- @type MENU_GROUP
-- @extends Menu#MENU_BASE
--- @type MENU_GROUP
-- @extends Core.Menu#MENU_BASE
--- #MENU_GROUP class, extends @{Menu#MENU_BASE}
--
-- The MENU_GROUP class manages the main menus for coalitions.
-- You can add menus with the @{#MENU_GROUP.New} method, which constructs a MENU_GROUP object and returns you the object reference.
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_GROUP.Remove}.
--
-- @usage
-- -- This demo creates a menu structure for the two groups of planes.
-- -- Each group will receive a different menu structure.
@@ -746,6 +758,7 @@ do
-- end
-- end, {}, 10, 10 )
--
-- @field #MENU_GROUP
MENU_GROUP = {
ClassName = "MENU_GROUP"
}
@@ -767,9 +780,9 @@ do
self = MenuGroup._Menus[Path]
else
self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) )
MenuGroup._Menus[Path] = self
self.Menus = {}
--if MenuGroup:IsAlive() then
MenuGroup._Menus[Path] = self
--end
self.MenuGroup = MenuGroup
self.Path = Path
@@ -780,8 +793,10 @@ do
self:T( { "Adding Menu ", MenuText, self.MenuParentPath } )
self.MenuPath = missionCommands.addSubMenuForGroup( self.MenuGroupID, MenuText, self.MenuParentPath )
if ParentMenu and ParentMenu.Menus then
ParentMenu.Menus[self.MenuPath] = self
if self.ParentMenu and self.ParentMenu.Menus then
self.ParentMenu.Menus[MenuText] = self
self:F( { self.ParentMenu.Menus, MenuText } )
self.ParentMenu.MenuCount = self.ParentMenu.MenuCount + 1
end
end
@@ -792,42 +807,63 @@ do
--- Removes the sub menus recursively of this MENU_GROUP.
-- @param #MENU_GROUP self
-- @param MenuTime
-- @return #MENU_GROUP self
function MENU_GROUP:RemoveSubMenus()
self:F( self.MenuPath )
function MENU_GROUP:RemoveSubMenus( MenuTime )
--self:F2( { self.MenuPath, MenuTime, self.MenuTime } )
for MenuID, Menu in pairs( self.Menus ) do
Menu:Remove()
--self:T( { "Removing Group SubMenus:", self.MenuGroup:GetName(), self.MenuPath } )
for MenuText, Menu in pairs( self.Menus ) do
Menu:Remove( MenuTime )
end
end
--- Removes the main menu and sub menus recursively of this MENU_GROUP.
-- @param #MENU_GROUP self
-- @param MenuTime
-- @return #nil
function MENU_GROUP:Remove()
self:F( { self.MenuGroupID, self.MenuPath } )
function MENU_GROUP:Remove( MenuTime )
--self:F2( { self.MenuGroupID, self.MenuPath, MenuTime, self.MenuTime } )
self:RemoveSubMenus()
if self.MenuGroup._Menus[self.Path] then
self = self.MenuGroup._Menus[self.Path]
self:RemoveSubMenus( MenuTime )
missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath )
if self.ParentMenu then
self.ParentMenu.Menus[self.MenuPath] = nil
if not MenuTime or self.MenuTime ~= MenuTime then
if self.MenuGroup._Menus[self.Path] then
self = self.MenuGroup._Menus[self.Path]
missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath )
if self.ParentMenu then
self.ParentMenu.Menus[self.MenuText] = nil
self.ParentMenu.MenuCount = self.ParentMenu.MenuCount - 1
if self.ParentMenu.MenuCount == 0 then
if self.MenuRemoveParent == true then
self:T2( "Removing Parent Menu " )
self.ParentMenu:Remove()
end
end
end
self:T( { "Removing Group Menu:", MenuGroup = self.MenuGroup:GetName(), MenuPath = self.MenuGroup._Menus[self.Path].Path } )
self.MenuGroup._Menus[self.Path] = nil
self = nil
end
self:E( self.MenuGroup._Menus[self.Path] )
self.MenuGroup._Menus[self.Path] = nil
self = nil
end
return nil
end
--- The MENU_GROUP_COMMAND class
-- @type MENU_GROUP_COMMAND
-- @extends Menu#MENU_BASE
--- @type MENU_GROUP_COMMAND
-- @extends Core.Menu#MENU_BASE
--- # MENU_GROUP_COMMAND class, extends @{Menu#MENU_COMMAND_BASE}
--
-- The @{Menu#MENU_GROUP_COMMAND} class manages the command menus for coalitions, which allow players to execute functions during mission execution.
-- You can add menus with the @{#MENU_GROUP_COMMAND.New} method, which constructs a MENU_GROUP_COMMAND object and returns you the object reference.
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_GROUP_COMMAND.Remove}.
--
-- @field #MENU_GROUP_COMMAND
MENU_GROUP_COMMAND = {
ClassName = "MENU_GROUP_COMMAND"
}
@@ -839,16 +875,20 @@ do
-- @param ParentMenu The parent menu.
-- @param CommandMenuFunction A function that is called when the menu key is pressed.
-- @param CommandMenuArgument An argument for the function.
-- @return Menu#MENU_GROUP_COMMAND self
-- @return #MENU_GROUP_COMMAND
function MENU_GROUP_COMMAND:New( MenuGroup, MenuText, ParentMenu, CommandMenuFunction, ... )
MenuGroup._Menus = MenuGroup._Menus or {}
local Path = ( ParentMenu and ( table.concat( ParentMenu.MenuPath or {}, "@" ) .. "@" .. MenuText ) ) or MenuText
if MenuGroup._Menus[Path] then
self = MenuGroup._Menus[Path]
self:F2( { "Re-using Group Command Menu:", MenuGroup:GetName(), MenuText } )
else
self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) )
MenuGroup._Menus[Path] = self
--if MenuGroup:IsAlive() then
MenuGroup._Menus[Path] = self
--end
self.Path = Path
self.MenuGroup = MenuGroup
@@ -856,33 +896,45 @@ do
self.MenuText = MenuText
self.ParentMenu = ParentMenu
self:T( { "Adding Command Menu ", MenuText, self.MenuParentPath } )
self:F( { "Adding Group Command Menu:", MenuGroup = MenuGroup:GetName(), MenuText = MenuText, MenuPath = self.MenuParentPath } )
self.MenuPath = missionCommands.addCommandForGroup( self.MenuGroupID, MenuText, self.MenuParentPath, self.MenuCallHandler, arg )
if ParentMenu and ParentMenu.Menus then
ParentMenu.Menus[self.MenuPath] = self
if self.ParentMenu and self.ParentMenu.Menus then
self.ParentMenu.Menus[MenuText] = self
self.ParentMenu.MenuCount = self.ParentMenu.MenuCount + 1
self:F2( { ParentMenu.Menus, MenuText } )
end
end
--self:F( { MenuGroup:GetName(), MenuText, ParentMenu.MenuPath } )
return self
end
--- Removes a menu structure for a group.
-- @param #MENU_GROUP_COMMAND self
-- @param MenuTime
-- @return #nil
function MENU_GROUP_COMMAND:Remove()
self:F( { self.MenuGroupID, self.MenuPath } )
function MENU_GROUP_COMMAND:Remove( MenuTime )
--self:F2( { self.MenuGroupID, self.MenuPath, MenuTime, self.MenuTime } )
if self.MenuGroup._Menus[self.Path] then
self = self.MenuGroup._Menus[self.Path]
missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath )
self.ParentMenu.Menus[self.MenuPath] = nil
self:E( self.MenuGroup._Menus[self.Path] )
self.MenuGroup._Menus[self.Path] = nil
self = nil
if not MenuTime or self.MenuTime ~= MenuTime then
if self.MenuGroup._Menus[self.Path] then
self = self.MenuGroup._Menus[self.Path]
missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath )
self:T( { "Removing Group Command Menu:", MenuGroup = self.MenuGroup:GetName(), MenuText = self.MenuText, MenuPath = self.Path } )
self.ParentMenu.Menus[self.MenuText] = nil
self.ParentMenu.MenuCount = self.ParentMenu.MenuCount - 1
if self.ParentMenu.MenuCount == 0 then
if self.MenuRemoveParent == true then
self:T2( "Removing Parent Menu " )
self.ParentMenu:Remove()
end
end
self.MenuGroup._Menus[self.Path] = nil
self = nil
end
end
return nil

View File

@@ -1,30 +1,45 @@
--- This module contains the MESSAGE class.
--- **Core** - MESSAGE class takes are of the **real-time notifications** and **messages to players** during a simulation.
--
-- 1) @{Message#MESSAGE} class, extends @{Base#BASE}
-- =================================================
-- Message System to display Messages to Clients, Coalitions or All.
-- Messages are shown on the display panel for an amount of seconds, and will then disappear.
-- Messages can contain a category which is indicating the category of the message.
-- ![Banner Image](..\Presentations\MESSAGE\Dia1.JPG)
--
-- 1.1) MESSAGE construction methods
-- ---------------------------------
-- Messages are created with @{Message#MESSAGE.New}. Note that when the MESSAGE object is created, no message is sent yet.
-- To send messages, you need to use the To functions.
-- ===
--
-- 1.2) Send messages with MESSAGE To methods
-- ------------------------------------------
-- Messages are sent to:
--
-- * Clients with @{Message#MESSAGE.ToClient}.
-- * Coalitions with @{Message#MESSAGE.ToCoalition}.
-- * All Players with @{Message#MESSAGE.ToAll}.
--
-- @module Message
-- @author FlightControl
--- The MESSAGE class
-- @type MESSAGE
-- @extends Core.Base#BASE
--- # MESSAGE class, extends @{Base#BASE}
--
-- Message System to display Messages to Clients, Coalitions or All.
-- Messages are shown on the display panel for an amount of seconds, and will then disappear.
-- Messages can contain a category which is indicating the category of the message.
--
-- ## MESSAGE construction
--
-- Messages are created with @{Message#MESSAGE.New}. Note that when the MESSAGE object is created, no message is sent yet.
-- To send messages, you need to use the To functions.
--
-- ## Send messages to an audience
--
-- Messages are sent:
--
-- * To a @{Client} using @{Message#MESSAGE.ToClient}().
-- * To a @{Group} using @{Message#MESSAGE.ToGroup}()
-- * To a coalition using @{Message#MESSAGE.ToCoalition}().
-- * To the red coalition using @{Message#MESSAGE.ToRed}().
-- * To the blue coalition using @{Message#MESSAGE.ToBlue}().
-- * To all Players using @{Message#MESSAGE.ToAll}().
--
-- ## Send conditionally to an audience
--
-- Messages can be sent conditionally to an audience (when a condition is true):
--
-- * To all players using @{Message#MESSAGE.ToAllIf}().
-- * To a coalition using @{Message#MESSAGE.ToCoalitionIf}().
--
-- @field #MESSAGE
MESSAGE = {
ClassName = "MESSAGE",
MessageCategory = 0,
@@ -229,80 +244,3 @@ function MESSAGE:ToAllIf( Condition )
return self
end
----- The MESSAGEQUEUE class
---- @type MESSAGEQUEUE
--MESSAGEQUEUE = {
-- ClientGroups = {},
-- CoalitionSides = {}
--}
--
--function MESSAGEQUEUE:New( RefreshInterval )
-- local self = BASE:Inherit( self, BASE:New() )
-- self:F( { RefreshInterval } )
--
-- self.RefreshInterval = RefreshInterval
--
-- --self.DisplayFunction = routines.scheduleFunction( self._DisplayMessages, { self }, 0, RefreshInterval )
-- self.DisplayFunction = SCHEDULER:New( self, self._DisplayMessages, {}, 0, RefreshInterval )
--
-- return self
--end
--
----- This function is called automatically by the MESSAGEQUEUE scheduler.
--function MESSAGEQUEUE:_DisplayMessages()
--
-- -- First we display all messages that a coalition needs to receive... Also those who are not in a client (CA module clients...).
-- for CoalitionSideID, CoalitionSideData in pairs( self.CoalitionSides ) do
-- for MessageID, MessageData in pairs( CoalitionSideData.Messages ) do
-- if MessageData.MessageSent == false then
-- --trigger.action.outTextForCoalition( CoalitionSideID, MessageData.MessageCategory .. '\n' .. MessageData.MessageText:gsub("\n$",""):gsub("\n$",""), MessageData.MessageDuration )
-- MessageData.MessageSent = true
-- end
-- local MessageTimeLeft = ( MessageData.MessageTime + MessageData.MessageDuration ) - timer.getTime()
-- if MessageTimeLeft <= 0 then
-- MessageData = nil
-- end
-- end
-- end
--
-- -- Then we send the messages for each individual client, but also to be included are those Coalition messages for the Clients who belong to a coalition.
-- -- Because the Client messages will overwrite the Coalition messages (for that Client).
-- for ClientGroupName, ClientGroupData in pairs( self.ClientGroups ) do
-- for MessageID, MessageData in pairs( ClientGroupData.Messages ) do
-- if MessageData.MessageGroup == false then
-- trigger.action.outTextForGroup( Group.getByName(ClientGroupName):getID(), MessageData.MessageCategory .. '\n' .. MessageData.MessageText:gsub("\n$",""):gsub("\n$",""), MessageData.MessageDuration )
-- MessageData.MessageGroup = true
-- end
-- local MessageTimeLeft = ( MessageData.MessageTime + MessageData.MessageDuration ) - timer.getTime()
-- if MessageTimeLeft <= 0 then
-- MessageData = nil
-- end
-- end
--
-- -- Now check if the Client also has messages that belong to the Coalition of the Client...
-- for CoalitionSideID, CoalitionSideData in pairs( self.CoalitionSides ) do
-- for MessageID, MessageData in pairs( CoalitionSideData.Messages ) do
-- local CoalitionGroup = Group.getByName( ClientGroupName )
-- if CoalitionGroup and CoalitionGroup:getCoalition() == CoalitionSideID then
-- if MessageData.MessageCoalition == false then
-- trigger.action.outTextForGroup( Group.getByName(ClientGroupName):getID(), MessageData.MessageCategory .. '\n' .. MessageData.MessageText:gsub("\n$",""):gsub("\n$",""), MessageData.MessageDuration )
-- MessageData.MessageCoalition = true
-- end
-- end
-- local MessageTimeLeft = ( MessageData.MessageTime + MessageData.MessageDuration ) - timer.getTime()
-- if MessageTimeLeft <= 0 then
-- MessageData = nil
-- end
-- end
-- end
-- end
--
-- return true
--end
--
----- The _MessageQueue object is created when the MESSAGE class module is loaded.
----_MessageQueue = MESSAGEQUEUE:New( 0.5 )
--

View File

@@ -1,86 +1,22 @@
--- This module contains the POINT classes.
--
-- 1) @{Point#POINT_VEC3} class, extends @{Base#BASE}
-- ==================================================
-- The @{Point#POINT_VEC3} class defines a 3D point in the simulator.
--
-- **Important Note:** Most of the functions in this section were taken from MIST, and reworked to OO concepts.
-- In order to keep the credibility of the the author, I want to emphasize that the of the MIST framework was created by Grimes, who you can find on the Eagle Dynamics Forums.
--
-- ## 1.1) POINT_VEC3 constructor
--
-- A new POINT_VEC3 instance can be created with:
--
-- * @{Point#POINT_VEC3.New}(): a 3D point.
-- * @{Point#POINT_VEC3.NewFromVec3}(): a 3D point created from a @{DCSTypes#Vec3}.
--
-- ## 1.2) Manupulate the X, Y, Z coordinates of the point
--
-- A POINT_VEC3 class works in 3D space. It contains internally an X, Y, Z coordinate.
-- Methods exist to manupulate these coordinates.
--
-- The current X, Y, Z axis can be retrieved with the methods @{#POINT_VEC3.GetX}(), @{#POINT_VEC3.GetY}(), @{#POINT_VEC3.GetZ}() respectively.
-- The methods @{#POINT_VEC3.SetX}(), @{#POINT_VEC3.SetY}(), @{#POINT_VEC3.SetZ}() change the respective axis with a new value.
-- The current axis values can be changed by using the methods @{#POINT_VEC3.AddX}(), @{#POINT_VEC3.AddY}(), @{#POINT_VEC3.AddZ}()
-- to add or substract a value from the current respective axis value.
-- Note that the Set and Add methods return the current POINT_VEC3 object, so these manipulation methods can be chained... For example:
--
-- local Vec3 = PointVec3:AddX( 100 ):AddZ( 150 ):GetVec3()
--
--
-- ## 1.5) Smoke, flare, explode, illuminate
--
-- At the point a smoke, flare, explosion and illumination bomb can be triggered. Use the following methods:
--
-- ### 1.5.1) Smoke
--
-- * @{#POINT_VEC3.Smoke}(): To smoke the point in a certain color.
-- * @{#POINT_VEC3.SmokeBlue}(): To smoke the point in blue.
-- * @{#POINT_VEC3.SmokeRed}(): To smoke the point in red.
-- * @{#POINT_VEC3.SmokeOrange}(): To smoke the point in orange.
-- * @{#POINT_VEC3.SmokeWhite}(): To smoke the point in white.
-- * @{#POINT_VEC3.SmokeGreen}(): To smoke the point in green.
--
-- ### 1.5.2) Flare
--
-- * @{#POINT_VEC3.Flare}(): To flare the point in a certain color.
-- * @{#POINT_VEC3.FlareRed}(): To flare the point in red.
-- * @{#POINT_VEC3.FlareYellow}(): To flare the point in yellow.
-- * @{#POINT_VEC3.FlareWhite}(): To flare the point in white.
-- * @{#POINT_VEC3.FlareGreen}(): To flare the point in green.
--
-- ### 1.5.3) Explode
--
-- * @{#POINT_VEC3.Explosion}(): To explode the point with a certain intensity.
--
-- ### 1.5.4) Illuminate
--
-- * @{#POINT_VEC3.IlluminationBomb}(): To illuminate the point.
--
--- **Core** - **POINT\_VEC** classes define an **extensive API** to **manage 3D points** in the simulation space.
--
-- 2) @{Point#POINT_VEC2} class, extends @{Point#POINT_VEC3}
-- =========================================================
-- The @{Point#POINT_VEC2} class defines a 2D point in the simulator. The height coordinate (if needed) will be the land height + an optional added height specified.
-- ![Banner Image](..\Presentations\POINT\Dia1.JPG)
--
-- ====
--
-- 2.1) POINT_VEC2 constructor
-- ---------------------------
-- A new POINT_VEC2 instance can be created with:
-- # Demo Missions
--
-- * @{Point#POINT_VEC2.New}(): a 2D point, taking an additional height parameter.
-- * @{Point#POINT_VEC2.NewFromVec2}(): a 2D point created from a @{DCSTypes#Vec2}.
-- ### [POINT_VEC Demo Missions source code]()
--
-- ## 1.2) Manupulate the X, Altitude, Y coordinates of the 2D point
-- ### [POINT_VEC Demo Missions, only for beta testers]()
--
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
--
-- A POINT_VEC2 class works in 2D space, with an altitude setting. It contains internally an X, Altitude, Y coordinate.
-- Methods exist to manupulate these coordinates.
-- ====
--
-- The current X, Altitude, Y axis can be retrieved with the methods @{#POINT_VEC2.GetX}(), @{#POINT_VEC2.GetAlt}(), @{#POINT_VEC2.GetY}() respectively.
-- The methods @{#POINT_VEC2.SetX}(), @{#POINT_VEC2.SetAlt}(), @{#POINT_VEC2.SetY}() change the respective axis with a new value.
-- The current axis values can be changed by using the methods @{#POINT_VEC2.AddX}(), @{#POINT_VEC2.AddAlt}(), @{#POINT_VEC2.AddY}()
-- to add or substract a value from the current respective axis value.
-- Note that the Set and Add methods return the current POINT_VEC2 object, so these manipulation methods can be chained... For example:
-- # YouTube Channel
--
-- local Vec2 = PointVec2:AddX( 100 ):AddY( 2000 ):GetVec2()
-- ### [POINT_VEC YouTube Channel]()
--
-- ===
--
@@ -119,7 +55,6 @@
--- The POINT_VEC3 class
-- @type POINT_VEC3
-- @extends Core.Base#BASE
-- @field #number x The x coordinate in 3D space.
-- @field #number y The y coordinate in 3D space.
-- @field #number z The z coordiante in 3D space.
@@ -128,6 +63,126 @@
-- @field #POINT_VEC3.RoutePointAltType RoutePointAltType
-- @field #POINT_VEC3.RoutePointType RoutePointType
-- @field #POINT_VEC3.RoutePointAction RoutePointAction
-- @extends Core.Base#BASE
--- # POINT_VEC3 class, extends @{Base#BASE}
--
-- POINT_VEC3 defines a 3D point in the simulator and with its methods, you can use or manipulate the point in 3D space.
--
-- **Important Note:** Most of the functions in this section were taken from MIST, and reworked to OO concepts.
-- In order to keep the credibility of the the author,
-- I want to emphasize that the formulas embedded in the MIST framework were created by Grimes or previous authors,
-- who you can find on the Eagle Dynamics Forums.
--
--
-- ## POINT_VEC3 constructor
--
-- A new POINT_VEC3 object can be created with:
--
-- * @{#POINT_VEC3.New}(): a 3D point.
-- * @{#POINT_VEC3.NewFromVec3}(): a 3D point created from a @{DCSTypes#Vec3}.
--
--
-- ## Manupulate the X, Y, Z coordinates of the POINT_VEC3
--
-- A POINT_VEC3 class works in 3D space. It contains internally an X, Y, Z coordinate.
-- Methods exist to manupulate these coordinates.
--
-- The current X, Y, Z axis can be retrieved with the methods @{#POINT_VEC3.GetX}(), @{#POINT_VEC3.GetY}(), @{#POINT_VEC3.GetZ}() respectively.
-- The methods @{#POINT_VEC3.SetX}(), @{#POINT_VEC3.SetY}(), @{#POINT_VEC3.SetZ}() change the respective axis with a new value.
-- The current axis values can be changed by using the methods @{#POINT_VEC3.AddX}(), @{#POINT_VEC3.AddY}(), @{#POINT_VEC3.AddZ}()
-- to add or substract a value from the current respective axis value.
-- Note that the Set and Add methods return the current POINT_VEC3 object, so these manipulation methods can be chained... For example:
--
-- local Vec3 = PointVec3:AddX( 100 ):AddZ( 150 ):GetVec3()
--
--
-- ## Create waypoints for routes
--
-- A POINT_VEC3 can prepare waypoints for Ground and Air groups to be embedded into a Route.
--
-- * @{#POINT_VEC3.RoutePointAir}(): Build an air route point.
-- * @{#POINT_VEC3.RoutePointGround}(): Build a ground route point.
--
-- Route points can be used in the Route methods of the @{Group#GROUP} class.
--
--
-- ## Smoke, flare, explode, illuminate
--
-- At the point a smoke, flare, explosion and illumination bomb can be triggered. Use the following methods:
--
-- ### Smoke
--
-- * @{#POINT_VEC3.Smoke}(): To smoke the point in a certain color.
-- * @{#POINT_VEC3.SmokeBlue}(): To smoke the point in blue.
-- * @{#POINT_VEC3.SmokeRed}(): To smoke the point in red.
-- * @{#POINT_VEC3.SmokeOrange}(): To smoke the point in orange.
-- * @{#POINT_VEC3.SmokeWhite}(): To smoke the point in white.
-- * @{#POINT_VEC3.SmokeGreen}(): To smoke the point in green.
--
-- ### Flare
--
-- * @{#POINT_VEC3.Flare}(): To flare the point in a certain color.
-- * @{#POINT_VEC3.FlareRed}(): To flare the point in red.
-- * @{#POINT_VEC3.FlareYellow}(): To flare the point in yellow.
-- * @{#POINT_VEC3.FlareWhite}(): To flare the point in white.
-- * @{#POINT_VEC3.FlareGreen}(): To flare the point in green.
--
-- ### Explode
--
-- * @{#POINT_VEC3.Explosion}(): To explode the point with a certain intensity.
--
-- ### Illuminate
--
-- * @{#POINT_VEC3.IlluminationBomb}(): To illuminate the point.
--
--
-- ## 3D calculation methods
--
-- Various calculation methods exist to use or manipulate 3D space. Find below a short description of each method:
--
-- ### Distance
--
-- * @{#POINT_VEC3.Get3DDistance}(): Obtain the distance from the current 3D point to the provided 3D point in 3D space.
-- * @{#POINT_VEC3.Get2DDistance}(): Obtain the distance from the current 3D point to the provided 3D point in 2D space.
--
-- ### Angle
--
-- * @{#POINT_VEC3.GetAngleDegrees}(): Obtain the angle in degrees from the current 3D point with the provided 3D direction vector.
-- * @{#POINT_VEC3.GetAngleRadians}(): Obtain the angle in radians from the current 3D point with the provided 3D direction vector.
-- * @{#POINT_VEC3.GetDirectionVec3}(): Obtain the 3D direction vector from the current 3D point to the provided 3D point.
--
-- ### Translation
--
-- * @{#POINT_VEC3.Translate}(): Translate the current 3D point towards an other 3D point using the given Distance and Angle.
--
-- ### Get the North correction of the current location
--
-- * @{#POINT_VEC3.GetNorthCorrection}(): Obtains the north correction at the current 3D point.
--
--
-- ## Point Randomization
--
-- Various methods exist to calculate random locations around a given 3D point.
--
-- * @{#POINT_VEC3.GetRandomPointVec2InRadius}(): Provides a random 2D point around the current 3D point, in the given inner to outer band.
-- * @{#POINT_VEC3.GetRandomPointVec3InRadius}(): Provides a random 3D point around the current 3D point, in the given inner to outer band.
-- * @{#POINT_VEC3.GetRandomVec2InRadius}(): Provides a random 2D vector around the current 3D point, in the given inner to outer band.
-- * @{#POINT_VEC3.GetRandomVec3InRadius}(): Provides a random 3D vector around the current 3D point, in the given inner to outer band.
--
--
-- ## Metric system
--
-- * @{#POINT_VEC3.IsMetric}(): Returns if the 3D point is Metric or Nautical Miles.
-- * @{#POINT_VEC3.SetMetric}(): Sets the 3D point to Metric or Nautical Miles.
--
--
-- ## Coorinate text generation
--
-- * @{#POINT_VEC3.ToStringBR}(): Generates a Bearing & Range text in the format of DDD for DI where DDD is degrees and DI is distance.
-- * @{#POINT_VEC3.ToStringLL}(): Generates a Latutude & Longutude text.
--
-- @field #POINT_VEC3
POINT_VEC3 = {
ClassName = "POINT_VEC3",
Metric = true,
@@ -144,16 +199,85 @@ POINT_VEC3 = {
},
}
--- The POINT_VEC2 class
-- @type POINT_VEC2
-- @extends #POINT_VEC3
--- @type POINT_VEC2
-- @field Dcs.DCSTypes#Distance x The x coordinate in meters.
-- @field Dcs.DCSTypes#Distance y the y coordinate in meters.
-- @extends Core.Point#POINT_VEC3
--- # POINT_VEC2 class, extends @{Point#POINT_VEC3}
--
-- The @{Point#POINT_VEC2} class defines a 2D point in the simulator. The height coordinate (if needed) will be the land height + an optional added height specified.
--
-- ## POINT_VEC2 constructor
--
-- A new POINT_VEC2 instance can be created with:
--
-- * @{Point#POINT_VEC2.New}(): a 2D point, taking an additional height parameter.
-- * @{Point#POINT_VEC2.NewFromVec2}(): a 2D point created from a @{DCSTypes#Vec2}.
--
-- ## Manupulate the X, Altitude, Y coordinates of the 2D point
--
-- A POINT_VEC2 class works in 2D space, with an altitude setting. It contains internally an X, Altitude, Y coordinate.
-- Methods exist to manupulate these coordinates.
--
-- The current X, Altitude, Y axis can be retrieved with the methods @{#POINT_VEC2.GetX}(), @{#POINT_VEC2.GetAlt}(), @{#POINT_VEC2.GetY}() respectively.
-- The methods @{#POINT_VEC2.SetX}(), @{#POINT_VEC2.SetAlt}(), @{#POINT_VEC2.SetY}() change the respective axis with a new value.
-- The current Lat(itude), Alt(itude), Lon(gitude) values can also be retrieved with the methods @{#POINT_VEC2.GetLat}(), @{#POINT_VEC2.GetAlt}(), @{#POINT_VEC2.GetLon}() respectively.
-- The current axis values can be changed by using the methods @{#POINT_VEC2.AddX}(), @{#POINT_VEC2.AddAlt}(), @{#POINT_VEC2.AddY}()
-- to add or substract a value from the current respective axis value.
-- Note that the Set and Add methods return the current POINT_VEC2 object, so these manipulation methods can be chained... For example:
--
-- local Vec2 = PointVec2:AddX( 100 ):AddY( 2000 ):GetVec2()
--
-- @field #POINT_VEC2
POINT_VEC2 = {
ClassName = "POINT_VEC2",
}
--- @type COORDINATE
-- @field #number LL_Accuracy
-- @field #boolean LL_DMS
-- @field #number MGRS_Accuracy
-- @field #string System
-- @extends Core.Point#POINT_VEC2
--- # COORDINATE class, extends @{Point#COORDINATE}
--
-- The COORDINATE class defines a 2D coordinate in the simulator. The height coordinate (if needed) will be the land height + an optional added height specified.
-- A COORDINATE can be expressed in LL or in MGRS.
--
-- ## COORDINATE constructor
--
-- A new COORDINATE instance can be created with:
--
-- * @{Point#COORDINATE.New}(): a 2D point, taking an additional height parameter.
-- * @{Point#COORDINATE.NewFromVec2}(): a 2D point created from a @{DCSTypes#Vec2}.
--
-- ## Manupulate the X, Altitude, Y coordinates of the 2D point
--
-- A COORDINATE class works in 2D space, with an altitude setting. It contains internally an X, Altitude, Y coordinate.
-- Methods exist to manupulate these coordinates.
--
-- The current X, Altitude, Y axis can be retrieved with the methods @{#COORDINATE.GetX}(), @{#COORDINATE.GetAlt}(), @{#COORDINATE.GetY}() respectively.
-- The methods @{#COORDINATE.SetX}(), @{#COORDINATE.SetAlt}(), @{#COORDINATE.SetY}() change the respective axis with a new value.
-- The current Lat(itude), Alt(itude), Lon(gitude) values can also be retrieved with the methods @{#COORDINATE.GetLat}(), @{#COORDINATE.GetAlt}(), @{#COORDINATE.GetLon}() respectively.
-- The current axis values can be changed by using the methods @{#COORDINATE.AddX}(), @{#COORDINATE.AddAlt}(), @{#COORDINATE.AddY}()
-- to add or substract a value from the current respective axis value.
-- Note that the Set and Add methods return the current COORDINATE object, so these manipulation methods can be chained... For example:
--
-- local Vec2 = PointVec2:AddX( 100 ):AddY( 2000 ):GetVec2()
--
-- @field #COORDINATE
COORDINATE = {
ClassName = "COORDINATE",
LL_Accuracy = 2,
LL_DMS = true,
MGRS_Accuracy = 5,
System = "MGRS",
}
do -- POINT_VEC3
--- RoutePoint AltTypes
@@ -359,7 +483,7 @@ function POINT_VEC3:GetRandomVec3InRadius( OuterRadius, InnerRadius )
local RandomVec2 = self:GetRandomVec2InRadius( OuterRadius, InnerRadius )
local y = self:GetY() + math.random( InnerRadius, OuterRadius )
local RandomVec3 = { x = RandomVec2.x, y = y, z = RandomVec2.z }
local RandomVec3 = { x = RandomVec2.x, y = y, z = RandomVec2.y }
return RandomVec3
end
@@ -394,11 +518,11 @@ function POINT_VEC3:GetNorthCorrectionRadians()
end
--- Return a direction in radians from the POINT_VEC3 using a direction vector in Vec3 format.
--- Return an angle in radians from the POINT_VEC3 using a direction vector in Vec3 format.
-- @param #POINT_VEC3 self
-- @param Dcs.DCSTypes#Vec3 DirectionVec3 The direction vector in Vec3 format.
-- @return #number DirectionRadians The direction in radians.
function POINT_VEC3:GetDirectionRadians( DirectionVec3 )
-- @return #number DirectionRadians The angle in radians.
function POINT_VEC3:GetAngleRadians( DirectionVec3 )
local DirectionRadians = math.atan2( DirectionVec3.z, DirectionVec3.x )
--DirectionRadians = DirectionRadians + self:GetNorthCorrectionRadians()
if DirectionRadians < 0 then
@@ -407,6 +531,17 @@ function POINT_VEC3:GetDirectionRadians( DirectionVec3 )
return DirectionRadians
end
--- Return an angle in degrees from the POINT_VEC3 using a direction vector in Vec3 format.
-- @param #POINT_VEC3 self
-- @param Dcs.DCSTypes#Vec3 DirectionVec3 The direction vector in Vec3 format.
-- @return #number DirectionRadians The angle in degrees.
function POINT_VEC3:GetAngleDegrees( DirectionVec3 )
local AngleRadians = self:GetAngleRadians(DirectionVec3)
local Angle = UTILS.ToDegree( AngleRadians )
return Angle
end
--- Return the 2D distance in meters between the target POINT_VEC3 and the POINT_VEC3.
-- @param #POINT_VEC3 self
-- @param #POINT_VEC3 TargetPointVec3 The target POINT_VEC3.
@@ -448,17 +583,6 @@ function POINT_VEC3:ToStringBR( AngleRadians, Distance )
return s
end
--- Provides a Bearing / Range string
-- @param #POINT_VEC3 self
-- @param #number AngleRadians The angle in randians
-- @param #number Distance The distance
-- @return #string The BR Text
function POINT_VEC3:ToStringLL( acc, DMS )
acc = acc or 3
local lat, lon = coord.LOtoLL( self:GetVec3() )
return UTILS.tostringLL(lat, lon, acc, DMS)
end
--- Return the altitude text of the POINT_VEC3.
-- @param #POINT_VEC3 self
@@ -477,7 +601,7 @@ end
-- @return #string The BR text.
function POINT_VEC3:GetBRText( TargetPointVec3 )
local DirectionVec3 = self:GetDirectionVec3( TargetPointVec3 )
local AngleRadians = self:GetDirectionRadians( DirectionVec3 )
local AngleRadians = self:GetAngleRadians( DirectionVec3 )
local Distance = self:Get2DDistance( TargetPointVec3 )
return self:ToStringBR( AngleRadians, Distance )
end
@@ -525,9 +649,9 @@ function POINT_VEC3:RoutePointAir( AltType, Type, Action, Speed, SpeedLocked )
self:F2( { AltType, Type, Action, Speed, SpeedLocked } )
local RoutePoint = {}
RoutePoint.x = self:GetX()
RoutePoint.y = self:GetZ()
RoutePoint.alt = self:GetY()
RoutePoint.x = self.x
RoutePoint.y = self.z
RoutePoint.alt = self.y
RoutePoint.alt_type = AltType
RoutePoint.type = Type
@@ -566,8 +690,8 @@ function POINT_VEC3:RoutePointGround( Speed, Formation )
self:F2( { Formation, Speed } )
local RoutePoint = {}
RoutePoint.x = self:GetX()
RoutePoint.y = self:GetZ()
RoutePoint.x = self.x
RoutePoint.y = self.z
RoutePoint.action = Formation or ""
@@ -695,6 +819,25 @@ function POINT_VEC3:FlareRed( Azimuth )
self:Flare( FLARECOLOR.Red, Azimuth )
end
--- Returns if a PointVec3 has Line of Sight (LOS) with the ToPointVec3.
-- @param #POINT_VEC3 self
-- @param #POINT_VEC3 ToPointVec3
-- @return #boolean true If the ToPointVec3 has LOS with the PointVec3, otherwise false.
function POINT_VEC3:IsLOS( ToPointVec3 ) --R2.1
-- Measurement of visibility should not be from the ground, so Adding a hypotethical 2 meters to each PointVec3.
local FromVec3 = self:GetVec3()
FromVec3.y = FromVec3.y + 2
local ToVec3 = ToPointVec3:GetVec3()
ToVec3.y = ToVec3.y + 2
local IsLOS = land.isVisible( FromVec3, ToVec3 )
return IsLOS
end
end
do -- POINT_VEC2
@@ -768,13 +911,27 @@ function POINT_VEC2:GetY()
return self.z
end
--- Return the altitude of the land at the POINT_VEC2.
--- Return the altitude (height) of the land at the POINT_VEC2.
-- @param #POINT_VEC2 self
-- @return #number The land altitude.
function POINT_VEC2:GetAlt()
return land.getHeight( { x = self.x, y = self.z } )
end
--- Return Return the Lat(itude) coordinate of the POINT_VEC2 (ie: (parent)POINT_VEC3.x).
-- @param #POINT_VEC2 self
-- @return #number The x coodinate.
function POINT_VEC2:GetLat()
return self.x
end
--- Return the Lon(gitude) coordinate of the POINT_VEC2 (ie: (parent)POINT_VEC3.z).
-- @param #POINT_VEC2 self
-- @return #number The y coodinate.
function POINT_VEC2:GetLon()
return self.z
end
--- Set the x coordinate of the POINT_VEC2.
-- @param #POINT_VEC2 self
-- @param #number x The x coordinate.
@@ -793,6 +950,15 @@ function POINT_VEC2:SetY( y )
return self
end
--- Set the Lat(itude) coordinate of the POINT_VEC2 (ie: POINT_VEC3.x).
-- @param #POINT_VEC2 self
-- @param #number x The x coordinate.
-- @return #POINT_VEC2
function POINT_VEC2:SetLat( x )
self.x = x
return self
end
--- Set the altitude of the POINT_VEC2.
-- @param #POINT_VEC2 self
-- @param #number Altitude The land altitude. If nothing (nil) is given, then the current land altitude is set.
@@ -802,6 +968,15 @@ function POINT_VEC2:SetAlt( Altitude )
return self
end
--- Set the Lon(gitude) coordinate of the POINT_VEC2 (ie: POINT_VEC3.z).
-- @param #POINT_VEC2 self
-- @param #number y The y coordinate.
-- @return #POINT_VEC2
function POINT_VEC2:SetLon( z )
self.z = z
return self
end
--- Add to the x coordinate of the POINT_VEC2.
-- @param #POINT_VEC2 self
-- @param #number x The x coordinate.
@@ -861,7 +1036,7 @@ end
--- Return no text for the altitude of the POINT_VEC2.
-- @param #POINT_VEC2 self
-- @return #string Empty string.
function POINT_VEC2:GetAltitudeText()
function POINT_VEC2:GetAltitudeText()
return ''
end
@@ -870,7 +1045,7 @@ end
-- @param Dcs.DCSTypes#Distance Distance The Distance to be added in meters.
-- @param Dcs.DCSTypes#Angle Angle The Angle in degrees.
-- @return #POINT_VEC2 The new calculated POINT_VEC2.
function POINT_VEC2:Translate( Distance, Angle )
function POINT_VEC2:Translate( Distance, Angle )
local SX = self:GetX()
local SY = self:GetY()
local Radians = Angle / 180 * math.pi
@@ -882,4 +1057,159 @@ end
end
do -- COORDINATE
--- COORDINATE constructor.
-- @param #COORDINATE self
-- @param Dcs.DCSTypes#Distance x The x coordinate of the Vec3 point, pointing to the North.
-- @param Dcs.DCSTypes#Distance y The y coordinate of the Vec3 point, pointing to the Right.
-- @param Dcs.DCSTypes#Distance LandHeightAdd (optional) The default height if required to be evaluated will be the land height of the x, y coordinate. You can specify an extra height to be added to the land height.
-- @return Core.Point#COORDINATE
function COORDINATE:New( x, y, LandHeightAdd ) --R2.1 Fixes issue #424.
self = BASE:Inherit( self, POINT_VEC2:New( x, y, LandHeightAdd ) ) -- Core.Point#COORDINATE
self:F2( self )
return self
end
--- Create a new COORDINATE object from Vec2 coordinates.
-- @param #COORDINATE self
-- @param Dcs.DCSTypes#Vec2 Vec2 The Vec2 point.
-- @param Dcs.DCSTypes#Distance LandHeightAdd (optional) The default height if required to be evaluated will be the land height of the x, y coordinate. You can specify an extra height to be added to the land height.
-- @return Core.Point#COORDINATE self
function COORDINATE:NewFromVec2( Vec2, LandHeightAdd ) --R2.1 Fixes issue #424.
self = BASE:Inherit( self, POINT_VEC2:NewFromVec2( Vec2, LandHeightAdd ) ) -- Core.Point#COORDINATE
self:F2( self )
return self
end
--- Create a new COORDINATE object from Vec3 coordinates.
-- @param #COORDINATE self
-- @param Dcs.DCSTypes#Vec3 Vec3 The Vec3 point.
-- @return Core.Point#COORDINATE self
function COORDINATE:NewFromVec3( Vec3 ) --R2.1 Fixes issue #424.
self = BASE:Inherit( self, POINT_VEC2:NewFromVec3( Vec3 ) ) -- Core.Point#COORDINATE
self:F2( self )
return self
end
--- Provides a Lat Lon string
-- @param #COORDINATE self
-- @param #number LL_Accuracy The accurancy of significant numbers behind the comma... So Accurancy of 2 is 0.01.
-- @param #boolean LL_DMS true = Degrees, Minutes, Seconds; false = Degrees, Minutes
-- @return #string The LL Text
function COORDINATE:ToStringLL( LL_Accuracy, LL_DMS ) --R2.1 Fixes issue #424.
LL_Accuracy = LL_Accuracy or self.LL_Accuracy
LL_DMS = LL_DMS or self.LL_DMS
local lat, lon = coord.LOtoLL( self:GetVec3() )
return "LL:" .. UTILS.tostringLL( lat, lon, LL_Accuracy, LL_DMS )
end
--- Provides a MGRS string
-- @param #COORDINATE self
-- @param #number MGRS_Accuracy of the 5 digit code.
-- Precision depends on the Accuracy choosen:
-- * 0 = no digits - precision level 100 km
-- * 1 = 1 digits - precision level 10 km
-- * 2 = 2 digits - precision level 1 km
-- * 3 = 3 digits - precision level 100 m
-- * 4 = 4 digits - precision level 10 m.
-- @return #string The MGRS Text
function COORDINATE:ToStringMGRS( MGRS_Accuracy ) --R2.1 Fixes issue #424.
MGRS_Accuracy = MGRS_Accuracy or self.MGRS_Accuracy
local lat, lon = coord.LOtoLL( self:GetVec3() )
local MGRS = coord.LLtoMGRS( lat, lon )
return "MGRS:" .. UTILS.tostringMGRS( MGRS, MGRS_Accuracy )
end
--- Provides a coordinate string of the point, based on a coordinate format system:
-- * Uses default settings in COORDINATE.
-- * Can be overridden if for a GROUP containing x clients, a menu was selected to override the default.
--
-- @param #COORDINATE self
-- @return #string The coordinate Text in the configured coordinate system.
function COORDINATE:ToString() --R2.1 Fixes issue #424.
local Coordinate = COORDINATE -- Core.Point#COORDINATE
local CoordSystem = Coordinate.System
if CoordSystem == "LL" then
return self:ToStringLL( Coordinate.LL_Accuracy, Coordinate.LL_DMS )
end
if CoordSystem == "MGRS" then
return self:ToStringMGRS( Coordinate.MGRS_Accuracy )
end
return nil
end
--- @param #COORDINATE self
-- @return #string The coordinate Text in the configured coordinate system.
function COORDINATE:CoordinateMenu( RootMenu ) --R2.1 Fixes issue #424.
if self.SystemMenu then
self.SystemMenu:Remove()
self.SystemMenu = nil
end
self.SystemMenu = MENU_MISSION:New( "System Settings" )
local CoordinateMenu = MENU_MISSION:New( "Coordinates", self.SystemMenu )
local Coordinate = COORDINATE
if Coordinate.System == "LL" then
MENU_MISSION_COMMAND:New( "Activate MGRS", CoordinateMenu, Coordinate.MenuSystem, Coordinate, "MGRS" )
MENU_MISSION_COMMAND:New( "LL Accuracy 1", CoordinateMenu, Coordinate.MenuLL_Accuracy, Coordinate, 1 )
MENU_MISSION_COMMAND:New( "LL Accuracy 2", CoordinateMenu, Coordinate.MenuLL_Accuracy, Coordinate, 2 )
MENU_MISSION_COMMAND:New( "LL Accuracy 3", CoordinateMenu, Coordinate.MenuLL_Accuracy, Coordinate, 3 )
MENU_MISSION_COMMAND:New( "LL Decimal On", CoordinateMenu, Coordinate.MenuLL_DMS, Coordinate, true )
MENU_MISSION_COMMAND:New( "LL Decimal Off", CoordinateMenu, Coordinate.MenuLL_DMS, Coordinate, false )
end
if Coordinate.System == "MGRS" then
MENU_MISSION_COMMAND:New( "Activate LL", CoordinateMenu, Coordinate.MenuSystem, Coordinate, "LL" )
MENU_MISSION_COMMAND:New( "MGRS Accuracy 1", CoordinateMenu, Coordinate.MenuMGRS_Accuracy, Coordinate, 1 )
MENU_MISSION_COMMAND:New( "MGRS Accuracy 2", CoordinateMenu, Coordinate.MenuMGRS_Accuracy, Coordinate, 2 )
MENU_MISSION_COMMAND:New( "MGRS Accuracy 3", CoordinateMenu, Coordinate.MenuMGRS_Accuracy, Coordinate, 3 )
MENU_MISSION_COMMAND:New( "MGRS Accuracy 4", CoordinateMenu, Coordinate.MenuMGRS_Accuracy, Coordinate, 4 )
MENU_MISSION_COMMAND:New( "MGRS Accuracy 5", CoordinateMenu, Coordinate.MenuMGRS_Accuracy, Coordinate, 5 )
end
end
--- @param #COORDINATE self
function COORDINATE:MenuSystem( System ) --R2.1 Fixes issue #424.
self.System = System
self:CoordinateMenu()
end
--- @param #COORDINATE self
function COORDINATE:MenuLL_Accuracy( LL_Accuracy ) --R2.1 Fixes issue #424.
self.LL_Accuracy = LL_Accuracy
self:CoordinateMenu()
end
--- @param #COORDINATE self
function COORDINATE:MenuLL_DMS( LL_DMS ) --R2.1 Fixes issue #424.
self.LL_DMS = LL_DMS
self:CoordinateMenu()
end
--- @param #COORDINATE self
function COORDINATE:MenuMGRS_Accuracy( MGRS_Accuracy ) --R2.1 Fixes issue #424.
self.MGRS_Accuracy = MGRS_Accuracy
self:CoordinateMenu()
end
end

View File

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

View File

@@ -64,7 +64,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
-- Initialize the ObjectSchedulers array, which is a weakly coupled table.
-- If the object used as the key is nil, then the garbage collector will remove the item from the Functions array.
self.ObjectSchedulers = self.ObjectSchedulers or {} -- setmetatable( {}, { __mode = "v" } )
self.ObjectSchedulers = self.ObjectSchedulers or setmetatable( {}, { __mode = "v" } ) -- or {}
if Scheduler.MasterObject then
self.ObjectSchedulers[self.CallID] = Scheduler
@@ -181,13 +181,17 @@ function SCHEDULEDISPATCHER:Start( Scheduler, CallID )
if CallID then
local Schedule = self.Schedule[Scheduler]
Schedule[CallID].ScheduleID = timer.scheduleFunction(
Schedule[CallID].CallHandler,
CallID,
timer.getTime() + Schedule[CallID].Start
)
-- Only start when there is no ScheduleID defined!
-- This prevents to "Start" the scheduler twice with the same CallID...
if not Schedule[CallID].ScheduleID then
Schedule[CallID].ScheduleID = timer.scheduleFunction(
Schedule[CallID].CallHandler,
CallID,
timer.getTime() + Schedule[CallID].Start
)
end
else
for CallID, Schedule in pairs( self.Schedule[Scheduler] ) do
for CallID, Schedule in pairs( self.Schedule[Scheduler] or {} ) do
self:Start( Scheduler, CallID ) -- Recursive
end
end
@@ -198,13 +202,26 @@ function SCHEDULEDISPATCHER:Stop( Scheduler, CallID )
if CallID then
local Schedule = self.Schedule[Scheduler]
timer.removeFunction( Schedule[CallID].ScheduleID )
-- Only stop when there is a ScheduleID defined for the CallID.
-- So, when the scheduler was stopped before, do nothing.
if Schedule[CallID].ScheduleID then
timer.removeFunction( Schedule[CallID].ScheduleID )
Schedule[CallID].ScheduleID = nil
end
else
for CallID, Schedule in pairs( self.Schedule[Scheduler] ) do
for CallID, Schedule in pairs( self.Schedule[Scheduler] or {} ) do
self:Stop( Scheduler, CallID ) -- Recursive
end
end
end
function SCHEDULEDISPATCHER:Clear( Scheduler )
self:F2( { Scheduler = Scheduler } )
for CallID, Schedule in pairs( self.Schedule[Scheduler] or {} ) do
self:Stop( Scheduler, CallID ) -- Recursive
end
end

View File

@@ -1,30 +1,33 @@
--- This module contains the SCHEDULER class.
--- **Core** - SCHEDULER prepares and handles the **execution of functions over scheduled time (intervals)**.
--
-- # 1) @{Scheduler#SCHEDULER} class, extends @{Base#BASE}
-- ![Banner Image](..\Presentations\SCHEDULER\Dia1.JPG)
--
-- The @{Scheduler#SCHEDULER} class creates schedule.
--
-- ## 1.1) SCHEDULER constructor
-- ===
--
-- The SCHEDULER class is quite easy to use, but note that the New constructor has variable parameters:
--
-- * @{Scheduler#SCHEDULER.New}( nil ): Setup a new SCHEDULER object, which is persistently executed after garbage collection.
-- * @{Scheduler#SCHEDULER.New}( Object ): Setup a new SCHEDULER object, which is linked to the Object. When the Object is nillified or destroyed, the SCHEDULER object will also be destroyed and stopped after garbage collection.
-- * @{Scheduler#SCHEDULER.New}( nil, Function, FunctionArguments, Start, ... ): Setup a new persistent SCHEDULER object, and start a new schedule for the Function with the defined FunctionArguments according the Start and sequent parameters.
-- * @{Scheduler#SCHEDULER.New}( Object, Function, FunctionArguments, Start, ... ): Setup a new SCHEDULER object, linked to Object, and start a new schedule for the Function with the defined FunctionArguments according the Start and sequent parameters.
--
-- ## 1.2) SCHEDULER timer stopping and (re-)starting.
--
-- The SCHEDULER can be stopped and restarted with the following methods:
--
-- * @{Scheduler#SCHEDULER.Start}(): (Re-)Start the schedules within the SCHEDULER object. If a CallID is provided to :Start(), only the schedule referenced by CallID will be (re-)started.
-- * @{Scheduler#SCHEDULER.Stop}(): Stop the schedules within the SCHEDULER object. If a CallID is provided to :Stop(), then only the schedule referenced by CallID will be stopped.
--
-- ## 1.3) Create a new schedule
-- SCHEDULER manages the **scheduling of functions**:
--
-- With @{Scheduler#SCHEDULER.Schedule}() a new time event can be scheduled. This function is used by the :New() constructor when a new schedule is planned.
-- * optionally in an optional specified time interval,
-- * optionally **repeating** with a specified time repeat interval,
-- * optionally **randomizing** with a specified time interval randomization factor,
-- * optionally **stop** the repeating after a specified time interval.
--
-- ===
--
-- # Demo Missions
--
-- ### [SCHEDULER Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/SCH%20-%20Scheduler)
--
-- ### [SCHEDULER Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SCH%20-%20Scheduler)
--
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
--
-- ====
--
-- # YouTube Channel
--
-- ### [SCHEDULER YouTube Channel (none)]()
--
-- ====
--
-- ### Contributions:
--
@@ -34,10 +37,6 @@
--
-- * FlightControl : Design & Programming
--
-- ### Test Missions:
--
-- * SCH - Scheduler
--
-- ===
--
-- @module Scheduler
@@ -47,6 +46,153 @@
-- @type SCHEDULER
-- @field #number ScheduleID the ID of the scheduler.
-- @extends Core.Base#BASE
--- # SCHEDULER class, extends @{Base#BASE}
--
-- The SCHEDULER class creates schedule.
--
-- A SCHEDULER can manage **multiple** (repeating) schedules. Each planned or executing schedule has a unique **ScheduleID**.
-- The ScheduleID is returned when the method @{#SCHEDULER.Schedule}() is called.
-- It is recommended to store the ScheduleID in a variable, as it is used in the methods @{SCHEDULER.Start}() and @{SCHEDULER.Stop}(),
-- which can start and stop specific repeating schedules respectively within a SCHEDULER object.
--
-- ## SCHEDULER constructor
--
-- The SCHEDULER class is quite easy to use, but note that the New constructor has variable parameters:
--
-- The @{#SCHEDULER.New}() method returns 2 variables:
--
-- 1. The SCHEDULER object reference.
-- 2. The first schedule planned in the SCHEDULER object.
--
-- To clarify the different appliances, lets have a look at the following examples:
--
-- ### Construct a SCHEDULER object without a persistent schedule.
--
-- * @{#SCHEDULER.New}( nil ): Setup a new SCHEDULER object, which is persistently executed after garbage collection.
--
-- SchedulerObject = SCHEDULER:New()
-- SchedulerID = SchedulerObject:Schedule( nil, ScheduleFunction, {} )
--
-- The above example creates a new SchedulerObject, but does not schedule anything.
-- A separate schedule is created by using the SchedulerObject using the method :Schedule..., which returns a ScheduleID
--
-- ### Construct a SCHEDULER object without a volatile schedule, but volatile to the Object existence...
--
-- * @{#SCHEDULER.New}( Object ): Setup a new SCHEDULER object, which is linked to the Object. When the Object is nillified or destroyed, the SCHEDULER object will also be destroyed and stopped after garbage collection.
--
-- ZoneObject = ZONE:New( "ZoneName" )
-- SchedulerObject = SCHEDULER:New( ZoneObject )
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {} )
-- ...
-- ZoneObject = nil
-- garbagecollect()
--
-- The above example creates a new SchedulerObject, but does not schedule anything, and is bound to the existence of ZoneObject, which is a ZONE.
-- A separate schedule is created by using the SchedulerObject using the method :Schedule()..., which returns a ScheduleID
-- Later in the logic, the ZoneObject is put to nil, and garbage is collected.
-- As a result, the ScheduleObject will cancel any planned schedule.
--
-- ### Construct a SCHEDULER object with a persistent schedule.
--
-- * @{#SCHEDULER.New}( nil, Function, FunctionArguments, Start, ... ): Setup a new persistent SCHEDULER object, and start a new schedule for the Function with the defined FunctionArguments according the Start and sequent parameters.
--
-- SchedulerObject, SchedulerID = SCHEDULER:New( nil, ScheduleFunction, {} )
--
-- The above example creates a new SchedulerObject, and does schedule the first schedule as part of the call.
-- Note that 2 variables are returned here: SchedulerObject, ScheduleID...
--
-- ### Construct a SCHEDULER object without a schedule, but volatile to the Object existence...
--
-- * @{#SCHEDULER.New}( Object, Function, FunctionArguments, Start, ... ): Setup a new SCHEDULER object, linked to Object, and start a new schedule for the Function with the defined FunctionArguments according the Start and sequent parameters.
--
-- ZoneObject = ZONE:New( "ZoneName" )
-- SchedulerObject, SchedulerID = SCHEDULER:New( ZoneObject, ScheduleFunction, {} )
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {} )
-- ...
-- ZoneObject = nil
-- garbagecollect()
--
-- The above example creates a new SchedulerObject, and schedules a method call (ScheduleFunction),
-- and is bound to the existence of ZoneObject, which is a ZONE object (ZoneObject).
-- Both a ScheduleObject and a SchedulerID variable are returned.
-- Later in the logic, the ZoneObject is put to nil, and garbage is collected.
-- As a result, the ScheduleObject will cancel the planned schedule.
--
-- ## SCHEDULER timer stopping and (re-)starting.
--
-- The SCHEDULER can be stopped and restarted with the following methods:
--
-- * @{#SCHEDULER.Start}(): (Re-)Start the schedules within the SCHEDULER object. If a CallID is provided to :Start(), only the schedule referenced by CallID will be (re-)started.
-- * @{#SCHEDULER.Stop}(): Stop the schedules within the SCHEDULER object. If a CallID is provided to :Stop(), then only the schedule referenced by CallID will be stopped.
--
-- ZoneObject = ZONE:New( "ZoneName" )
-- SchedulerObject, SchedulerID = SCHEDULER:New( ZoneObject, ScheduleFunction, {} )
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 10 )
-- ...
-- SchedulerObject:Stop( SchedulerID )
-- ...
-- SchedulerObject:Start( SchedulerID )
--
-- The above example creates a new SchedulerObject, and does schedule the first schedule as part of the call.
-- Note that 2 variables are returned here: SchedulerObject, ScheduleID...
-- Later in the logic, the repeating schedule with SchedulerID is stopped.
-- A bit later, the repeating schedule with SchedulerId is (re)-started.
--
-- ## Create a new schedule
--
-- With the method @{#SCHEDULER.Schedule}() a new time event can be scheduled.
-- This method is used by the :New() constructor when a new schedule is planned.
--
-- Consider the following code fragment of the SCHEDULER object creation.
--
-- ZoneObject = ZONE:New( "ZoneName" )
-- SchedulerObject = SCHEDULER:New( ZoneObject )
--
-- Several parameters can be specified that influence the behaviour of a Schedule.
--
-- ### A single schedule, immediately executed
--
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {} )
--
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within milleseconds ...
--
-- ### A single schedule, planned over time
--
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {}, 10 )
--
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds ...
--
-- ### A schedule with a repeating time interval, planned over time
--
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60 )
--
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds,
-- and repeating 60 every seconds ...
--
-- ### A schedule with a repeating time interval, planned over time, with time interval randomization
--
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60, 0.5 )
--
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds,
-- and repeating 60 seconds, with a 50% time interval randomization ...
-- So the repeating time interval will be randomized using the **0.5**,
-- and will calculate between **60 - ( 60 * 0.5 )** and **60 + ( 60 * 0.5 )** for each repeat,
-- which is in this example between **30** and **90** seconds.
--
-- ### A schedule with a repeating time interval, planned over time, with time interval randomization, and stop after a time interval
--
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60, 0.5, 300 )
--
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds,
-- The schedule will repeat every 60 seconds.
-- So the repeating time interval will be randomized using the **0.5**,
-- and will calculate between **60 - ( 60 * 0.5 )** and **60 + ( 60 * 0.5 )** for each repeat,
-- which is in this example between **30** and **90** seconds.
-- The schedule will stop after **300** seconds.
--
-- @field #SCHEDULER
SCHEDULER = {
ClassName = "SCHEDULER",
Schedules = {},
@@ -147,6 +293,13 @@ function SCHEDULER:Remove( ScheduleID )
_SCHEDULEDISPATCHER:Remove( self, ScheduleID )
end
--- Clears all pending schedules.
-- @param #SCHEDULER self
function SCHEDULER:Clear()
self:F3( )
_SCHEDULEDISPATCHER:Clear( self )
end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,177 @@
--- (R2.1) **Core** -- Spawn dynamically new STATICs in your missions.
--
-- ![Banner Image](..\Presentations\SPAWNSTATIC\Dia1.JPG)
--
-- ====
--
-- SPAWNSTATIC spawns static structures in your missions dynamically. See below the SPAWNSTATIC class documentation.
--
-- ====
--
-- # Demo Missions
--
-- ### [SPAWNSTATIC Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/SPS - Spawning Statics)
--
-- ### [SPAWNSTATIC Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SPS%20-%20Spawning%20Statics)
--
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
--
-- ====
--
-- # YouTube Channel
--
-- ### [SPAWNSTATIC YouTube Channel]()
--
-- ====
--
-- # **API CHANGE HISTORY**
--
-- The underlying change log documents the API changes. Please read this carefully. The following notation is used:
--
-- * **Added** parts are expressed in bold type face.
-- * _Removed_ parts are expressed in italic type face.
--
-- Hereby the change log:
--
-- ===
--
-- # **AUTHORS and CONTRIBUTIONS**
--
-- ### Contributions:
--
-- ### Authors:
--
-- * **FlightControl**: Design & Programming
--
-- @module SpawnStatic
--- @type SPAWNSTATIC
-- @extends Core.Base#BASE
--- # SPAWNSTATIC class, extends @{Base#BASE}
--
-- The SPAWNSTATIC class allows to spawn dynamically new @{Static}s.
-- Through creating a copy of an existing static object template as defined in the Mission Editor (ME),
-- SPAWNSTATIC can retireve the properties of the defined static object template (like type, category etc), and "copy"
-- these properties to create a new static object and place it at the desired coordinate.
--
-- New spawned @{Static}s get **the same name** as the name of the template Static,
-- or gets the given name when a new name is provided at the Spawn method.
-- By default, spawned @{Static}s will follow a naming convention at run-time:
--
-- * Spawned @{Static}s will have the name _StaticName_#_nnn_, where _StaticName_ is the name of the **Template Static**,
-- and _nnn_ is a **counter from 0 to 99999**.
--
--
-- ## SPAWNSTATIC construction methods
--
-- Create a new SPAWNSTATIC object with the @{#SPAWNSTATIC.NewFromStatic}():
--
-- * @{#SPAWNSTATIC.NewFromStatic}(): Creates a new SPAWNSTATIC object given a name that is used as the base of the naming of each spawned Static.
--
-- ## **Spawn** methods
--
-- Groups can be spawned at different times and methods:
--
-- * @{#SPAWNSTATIC.SpawnFromPointVec2}(): Spawn a new group from a POINT_VEC2 coordinate.
-- (The group will be spawned at land height ).
-- * @{#SPAWNSTATIC.SpawnFromZone}(): Spawn a new group in a @{Zone}.
--
-- @field #SPAWNSTATIC SPAWNSTATIC
--
SPAWNSTATIC = {
ClassName = "SPAWNSTATIC",
}
--- @type SPAWNSTATIC.SpawnZoneTable
-- @list <Core.Zone#ZONE_BASE> SpawnZone
--- Creates the main object to spawn a @{Static} defined in the ME.
-- @param #SPAWNSTATIC self
-- @param #string SpawnTemplatePrefix is the name of the Group in the ME that defines the Template. Each new group will have the name starting with SpawnTemplatePrefix.
-- @return #SPAWNSTATIC
function SPAWNSTATIC:NewFromStatic( SpawnTemplatePrefix, CountryID ) --R2.1
local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC
self:F( { SpawnTemplatePrefix } )
local TemplateStatic = StaticObject.getByName( SpawnTemplatePrefix )
if TemplateStatic then
self.SpawnTemplatePrefix = SpawnTemplatePrefix
self.CountryID = CountryID
self.SpawnIndex = 0
else
error( "SPAWNSTATIC:New: There is no group declared in the mission editor with SpawnTemplatePrefix = '" .. SpawnTemplatePrefix .. "'" )
end
self:SetEventPriority( 5 )
return self
end
--- Creates the main object to spawn a @{Static} based on a type name.
-- @param #SPAWNSTATIC self
-- @param #string SpawnTypeName is the name of the type.
-- @return #SPAWNSTATIC
function SPAWNSTATIC:NewFromType( SpawnTypeName, SpawnShapeName, SpawnCategory, CountryID ) --R2.1
local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC
self:F( { SpawnTypeName } )
self.SpawnTypeName = SpawnTypeName
self.CountryID = CountryID
self.SpawnIndex = 0
self:SetEventPriority( 5 )
return self
end
--- Creates a new @{Static} from a POINT_VEC2.
-- @param #SPAWNSTATIC self
-- @param Core.Point#POINT_VEC2 PointVec2 The 2D coordinate where to spawn the static.
-- @param #number Heading The heading of the static, which is a number in degrees from 0 to 360.
-- @param #string (optional) The name of the new static.
-- @return #SPAWNSTATIC
function SPAWNSTATIC:SpawnFromPointVec2( PointVec2, Heading, NewName ) --R2.1
self:F( { PointVec2, Heading, NewName } )
local CountryName = _DATABASE.COUNTRY_NAME[self.CountryID]
local StaticTemplate = _DATABASE:GetStaticUnitTemplate( self.SpawnTemplatePrefix )
StaticTemplate.x = PointVec2:GetLat()
StaticTemplate.y = PointVec2:GetLon()
StaticTemplate.name = NewName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex )
StaticTemplate.heading = ( Heading / 180 ) * math.pi
StaticTemplate.CountryID = nil
StaticTemplate.CoalitionID = nil
StaticTemplate.CategoryID = nil
local Static = coalition.addStaticObject( self.CountryID, StaticTemplate )
self.SpawnIndex = self.SpawnIndex + 1
return Static
end
--- Creates a new @{Static} from a @{Zone}.
-- @param #SPAWNSTATIC self
-- @param Core.Zone#ZONE_BASE Zone The Zone where to spawn the static.
-- @param #number Heading The heading of the static, which is a number in degrees from 0 to 360.
-- @param #string (optional) The name of the new static.
-- @return #SPAWNSTATIC
function SPAWNSTATIC:SpawnFromZone( Zone, Heading, NewName ) --R2.1
self:F( { Zone, Heading, NewName } )
local Static = self:SpawnFromPointVec2( Zone:GetPointVec2(), Heading, NewName )
return Static
end

View File

@@ -0,0 +1,295 @@
--- **Core 2.1** -- Management of SPOT logistics, that can be transported from and to transportation carriers.
--
-- ![Banner Image](..\Presentations\SPOT\Dia1.JPG)
--
-- ===
--
-- SPOT implements the DCS Spot class functionality, but adds additional luxury to be able to:
--
-- * Spot for a defined duration.
-- * wiggle the spot at the target.
-- * Provide a @{Unit} as a target, instead of a point.
-- * Implement a status machine, LaseOn, LaseOff.
--
-- ====
--
-- # Demo Missions
--
-- ### [SPOT Demo Missions source code]()
--
-- ### [SPOT Demo Missions, only for beta testers]()
--
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
--
-- ====
--
-- # YouTube Channel
--
-- ### [SPOT YouTube Channel]()
--
-- ===
--
-- # **AUTHORS and CONTRIBUTIONS**
--
-- ### Contributions:
--
-- * [**Ciribob**](https://forums.eagle.ru/member.php?u=112175): Showing the way how to lase targets + how laser codes work!!! Explained the autolase script.
-- * [**EasyEB**](https://forums.eagle.ru/member.php?u=112055): Ideas and Beta Testing
-- * [**Wingthor**](https://forums.eagle.ru/member.php?u=123698): Beta Testing
--
--
-- ### Authors:
--
-- * **FlightControl**: Design & Programming
--
-- @module Spot
do
--- @type SPOT
-- @extends Core.Fsm#FSM
--- # SPOT class, extends @{Fsm#FSM}
--
-- SPOT implements the DCS Spot class functionality, but adds additional luxury to be able to:
--
-- * Mark targets for a defined duration.
-- * wiggle the spot at the target.
-- * Provide a @{Unit} as a target, instead of a point.
-- * Implement a status machine, LaseOn, LaseOff.
--
-- ## 1. SPOT constructor
--
-- * @{#SPOT.New}(..\Presentations\SPOT\Dia2.JPG): Creates a new SPOT object.
--
-- ## 2. SPOT is a FSM
--
-- ![Process]()
--
-- ### 2.1 SPOT States
--
-- * **Off**: Lasing is switched off.
-- * **On**: Lasing is switched on.
-- * **Destroyed**: Target is destroyed.
--
-- ### 2.2 SPOT Events
--
-- * **@{#SPOT.LaseOn}(Target, LaserCode, Duration)**: Lase to a target.
-- * **@{#SPOT.LaseOff}()**: Stop lasing the target.
-- * **@{#SPOT.Lasing}()**: Target is being lased.
-- * **@{#SPOT.Destroyed}()**: Triggered when target is destroyed.
--
-- ## 3. Check if a Target is being lased
--
-- The method @{#SPOT.IsLasing}() indicates whether lasing is on or off.
--
-- @field #SPOT
SPOT = {
ClassName = "SPOT",
}
--- SPOT Constructor.
-- @param #SPOT self
-- @param Wrapper.Unit#UNIT Recce
-- @param #number LaserCode
-- @param #number Duration
-- @return #SPOT
function SPOT:New( Recce )
local self = BASE:Inherit( self, FSM:New() ) -- #SPOT
self:F( {} )
self:SetStartState( "Off" )
self:AddTransition( "Off", "LaseOn", "On" )
--- LaseOn Handler OnBefore for SPOT
-- @function [parent=#SPOT] OnBeforeLaseOn
-- @param #SPOT self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #boolean
--- LaseOn Handler OnAfter for SPOT
-- @function [parent=#SPOT] OnAfterLaseOn
-- @param #SPOT self
-- @param #string From
-- @param #string Event
-- @param #string To
--- LaseOn Trigger for SPOT
-- @function [parent=#SPOT] LaseOn
-- @param #SPOT self
--- LaseOn Asynchronous Trigger for SPOT
-- @function [parent=#SPOT] __LaseOn
-- @param #SPOT self
-- @param #number Delay
self:AddTransition( "On", "Lasing", "On" )
self:AddTransition( { "On", "Destroyed" } , "LaseOff", "Off" )
--- LaseOff Handler OnBefore for SPOT
-- @function [parent=#SPOT] OnBeforeLaseOff
-- @param #SPOT self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #boolean
--- LaseOff Handler OnAfter for SPOT
-- @function [parent=#SPOT] OnAfterLaseOff
-- @param #SPOT self
-- @param #string From
-- @param #string Event
-- @param #string To
--- LaseOff Trigger for SPOT
-- @function [parent=#SPOT] LaseOff
-- @param #SPOT self
--- LaseOff Asynchronous Trigger for SPOT
-- @function [parent=#SPOT] __LaseOff
-- @param #SPOT self
-- @param #number Delay
self:AddTransition( "*" , "Destroyed", "Destroyed" )
--- Destroyed Handler OnBefore for SPOT
-- @function [parent=#SPOT] OnBeforeDestroyed
-- @param #SPOT self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #boolean
--- Destroyed Handler OnAfter for SPOT
-- @function [parent=#SPOT] OnAfterDestroyed
-- @param #SPOT self
-- @param #string From
-- @param #string Event
-- @param #string To
--- Destroyed Trigger for SPOT
-- @function [parent=#SPOT] Destroyed
-- @param #SPOT self
--- Destroyed Asynchronous Trigger for SPOT
-- @function [parent=#SPOT] __Destroyed
-- @param #SPOT self
-- @param #number Delay
self.Recce = Recce
self.LaseScheduler = SCHEDULER:New( self )
self:SetEventPriority( 5 )
self.Lasing = false
return self
end
--- @param #SPOT self
-- @param From
-- @param Event
-- @param To
-- @param Wrapper.Positionable#POSITIONABLE Target
-- @param #number LaserCode
-- @param #number Duration
function SPOT:onafterLaseOn( From, Event, To, Target, LaserCode, Duration )
self:E( { "LaseOn", Target, LaserCode, Duration } )
local function StopLase( self )
self:LaseOff()
end
self.Target = Target
self.LaserCode = LaserCode
self.Lasing = true
local RecceDcsUnit = self.Recce:GetDCSObject()
self.SpotIR = Spot.createInfraRed( RecceDcsUnit, { x = 0, y = 2, z = 0 }, Target:GetPointVec3():AddY(1):GetVec3() )
self.SpotLaser = Spot.createLaser( RecceDcsUnit, { x = 0, y = 2, z = 0 }, Target:GetPointVec3():AddY(1):GetVec3(), LaserCode )
if Duration then
self.ScheduleID = self.LaseScheduler:Schedule( self, StopLase, {self}, Duration )
end
self:HandleEvent( EVENTS.Dead )
self:__Lasing( -0.2 )
end
--- @param #SPOT self
-- @param Core.Event#EVENTDATA EventData
function SPOT:OnEventDead(EventData)
self:E( { Dead = EventData.IniDCSUnitName, Target = self.Target } )
if self.Target then
if EventData.IniDCSUnitName == self.Target:GetName() then
self:E( {"Target dead ", self.Target:GetName() } )
self:Destroyed()
self:LaseOff()
end
end
end
--- @param #SPOT self
-- @param From
-- @param Event
-- @param To
function SPOT:onafterLasing( From, Event, To )
if self.Target:IsAlive() then
self.SpotIR:setPoint( self.Target:GetPointVec3():AddY(1):AddY(math.random(-100,100)/100):AddX(math.random(-100,100)/100):GetVec3() )
self.SpotLaser:setPoint( self.Target:GetPointVec3():AddY(1):GetVec3() )
self:__Lasing( -0.2 )
else
self:E( { "Target is not alive", self.Target:IsAlive() } )
end
end
--- @param #SPOT self
-- @param From
-- @param Event
-- @param To
-- @return #SPOT
function SPOT:onafterLaseOff( From, Event, To )
self:E( {"Stopped lasing for ", self.Target:GetName() , SpotIR = self.SportIR, SpotLaser = self.SpotLaser } )
self.Lasing = false
self.SpotIR:destroy()
self.SpotLaser:destroy()
self.SpotIR = nil
self.SpotLaser = nil
if self.ScheduleID then
self.LaseScheduler:Stop(self.ScheduleID)
end
self.ScheduleID = nil
self.Target = nil
return self
end
--- Check if the SPOT is lasing
-- @param #SPOT self
-- @return #boolean true if it is lasing
function SPOT:IsLasing()
return self.Lasing
end
end

View File

@@ -1,4 +1,8 @@
--- This core module contains the ZONE classes, inherited from @{Zone#ZONE_BASE}.
--- **Core** - ZONE classes define **zones** within your mission of **various forms**, with **various capabilities**.
--
-- ![Banner Image](..\Presentations\ZONE\Dia1.JPG)
--
-- ===
--
-- There are essentially two core functions that zones accomodate:
--
@@ -16,127 +20,16 @@
--
-- Each of these ZONE classes have a zone name, and specific parameters defining the zone type:
--
-- * @{Zone#ZONE_BASE}: The ZONE_BASE class defining the base for all other zone classes.
-- * @{Zone#ZONE_RADIUS}: The ZONE_RADIUS class defined by a zone name, a location and a radius.
-- * @{Zone#ZONE}: The ZONE class, defined by the zone name as defined within the Mission Editor.
-- * @{Zone#ZONE_UNIT}: The ZONE_UNIT class defines by a zone around a @{Unit#UNIT} with a radius.
-- * @{Zone#ZONE_GROUP}: The ZONE_GROUP class defines by a zone around a @{Group#GROUP} with a radius.
-- * @{Zone#ZONE_POLYGON}: The ZONE_POLYGON class defines by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon.
-- * @{#ZONE_BASE}: The ZONE_BASE class defining the base for all other zone classes.
-- * @{#ZONE_RADIUS}: The ZONE_RADIUS class defined by a zone name, a location and a radius.
-- * @{#ZONE}: The ZONE class, defined by the zone name as defined within the Mission Editor.
-- * @{#ZONE_UNIT}: The ZONE_UNIT class defines by a zone around a @{Unit#UNIT} with a radius.
-- * @{#ZONE_GROUP}: The ZONE_GROUP class defines by a zone around a @{Group#GROUP} with a radius.
-- * @{#ZONE_POLYGON}: The ZONE_POLYGON class defines by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon.
--
-- ===
--
-- ===
--
-- # 1) @{Zone#ZONE_BASE} class, extends @{Base#BASE}
--
-- This class is an abstract BASE class for derived classes, and is not meant to be instantiated.
--
-- ## 1.1) Each zone has a name:
--
-- * @{#ZONE_BASE.GetName}(): Returns the name of the zone.
--
-- ## 1.2) Each zone implements two polymorphic functions defined in @{Zone#ZONE_BASE}:
--
-- * @{#ZONE_BASE.IsVec2InZone}(): Returns if a Vec2 is within the zone.
-- * @{#ZONE_BASE.IsVec3InZone}(): Returns if a Vec3 is within the zone.
--
-- ## 1.3) A zone has a probability factor that can be set to randomize a selection between zones:
--
-- * @{#ZONE_BASE.SetRandomizeProbability}(): Set the randomization probability of a zone to be selected, taking a value between 0 and 1 ( 0 = 0%, 1 = 100% )
-- * @{#ZONE_BASE.GetRandomizeProbability}(): Get the randomization probability of a zone to be selected, passing a value between 0 and 1 ( 0 = 0%, 1 = 100% )
-- * @{#ZONE_BASE.GetZoneMaybe}(): Get the zone taking into account the randomization probability. nil is returned if this zone is not a candidate.
--
-- ## 1.4) A zone manages Vectors:
--
-- * @{#ZONE_BASE.GetVec2}(): Returns the @{DCSTypes#Vec2} coordinate of the zone.
-- * @{#ZONE_BASE.GetRandomVec2}(): Define a random @{DCSTypes#Vec2} within the zone.
--
-- ## 1.5) A zone has a bounding square:
--
-- * @{#ZONE_BASE.GetBoundingSquare}(): Get the outer most bounding square of the zone.
--
-- ## 1.6) A zone can be marked:
--
-- * @{#ZONE_BASE.SmokeZone}(): Smokes the zone boundaries in a color.
-- * @{#ZONE_BASE.FlareZone}(): Flares the zone boundaries in a color.
--
-- ===
--
-- # 2) @{Zone#ZONE_RADIUS} class, extends @{Zone#ZONE_BASE}
--
-- The ZONE_RADIUS class defined by a zone name, a location and a radius.
-- This class implements the inherited functions from Core.Zone#ZONE_BASE taking into account the own zone format and properties.
--
-- ## 2.1) @{Zone#ZONE_RADIUS} constructor
--
-- * @{#ZONE_RADIUS.New}(): Constructor.
--
-- ## 2.2) Manage the radius of the zone
--
-- * @{#ZONE_RADIUS.SetRadius}(): Sets the radius of the zone.
-- * @{#ZONE_RADIUS.GetRadius}(): Returns the radius of the zone.
--
-- ## 2.3) Manage the location of the zone
--
-- * @{#ZONE_RADIUS.SetVec2}(): Sets the @{DCSTypes#Vec2} of the zone.
-- * @{#ZONE_RADIUS.GetVec2}(): Returns the @{DCSTypes#Vec2} of the zone.
-- * @{#ZONE_RADIUS.GetVec3}(): Returns the @{DCSTypes#Vec3} of the zone, taking an additional height parameter.
--
-- ## 2.4) Zone point randomization
--
-- Various functions exist to find random points within the zone.
--
-- * @{#ZONE_RADIUS.GetRandomVec2}(): Gets a random 2D point in the zone.
-- * @{#ZONE_RADIUS.GetRandomPointVec2}(): Gets a @{Point#POINT_VEC2} object representing a random 2D point in the zone.
-- * @{#ZONE_RADIUS.GetRandomPointVec3}(): Gets a @{Point#POINT_VEC3} object representing a random 3D point in the zone. Note that the height of the point is at landheight.
--
-- ===
--
-- # 3) @{Zone#ZONE} class, extends @{Zone#ZONE_RADIUS}
--
-- The ZONE class, defined by the zone name as defined within the Mission Editor.
-- This class implements the inherited functions from {Core.Zone#ZONE_RADIUS} taking into account the own zone format and properties.
--
-- ===
--
-- # 4) @{Zone#ZONE_UNIT} class, extends @{Zone#ZONE_RADIUS}
--
-- The ZONE_UNIT class defined by a zone around a @{Unit#UNIT} with a radius.
-- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties.
--
-- ===
--
-- # 5) @{Zone#ZONE_GROUP} class, extends @{Zone#ZONE_RADIUS}
--
-- The ZONE_GROUP class defines by a zone around a @{Group#GROUP} with a radius. The current leader of the group defines the center of the zone.
-- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties.
--
-- ===
--
-- # 6) @{Zone#ZONE_POLYGON_BASE} class, extends @{Zone#ZONE_BASE}
--
-- The ZONE_POLYGON_BASE class defined by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon.
-- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties.
-- This class is an abstract BASE class for derived classes, and is not meant to be instantiated.
--
-- ## 6.1) Zone point randomization
--
-- Various functions exist to find random points within the zone.
--
-- * @{#ZONE_POLYGON_BASE.GetRandomVec2}(): Gets a random 2D point in the zone.
-- * @{#ZONE_POLYGON_BASE.GetRandomPointVec2}(): Return a @{Point#POINT_VEC2} object representing a random 2D point within the zone.
-- * @{#ZONE_POLYGON_BASE.GetRandomPointVec3}(): Return a @{Point#POINT_VEC3} object representing a random 3D point at landheight within the zone.
--
--
-- ===
--
-- # 7) @{Zone#ZONE_POLYGON} class, extends @{Zone#ZONE_POLYGON_BASE}
--
-- The ZONE_POLYGON class defined by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon.
-- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties.
--
-- ====
--
-- **API CHANGE HISTORY**
-- ======================
-- # **API CHANGE HISTORY**
--
-- The underlying change log documents the API changes. Please read this carefully. The following notation is used:
--
@@ -171,14 +64,55 @@
-- ===
--
-- @module Zone
-- @author FlightControl
--- The ZONE_BASE class
-- @type ZONE_BASE
--- @type ZONE_BASE
-- @field #string ZoneName Name of the zone.
-- @field #number ZoneProbability A value between 0 and 1. 0 = 0% and 1 = 100% probability.
-- @extends Core.Base#BASE
--- # ZONE_BASE class, extends @{Base#BASE}
--
-- This class is an abstract BASE class for derived classes, and is not meant to be instantiated.
--
-- ## Each zone has a name:
--
-- * @{#ZONE_BASE.GetName}(): Returns the name of the zone.
--
-- ## Each zone implements two polymorphic functions defined in @{Zone#ZONE_BASE}:
--
-- * @{#ZONE_BASE.IsVec2InZone}(): Returns if a 2D vector is within the zone.
-- * @{#ZONE_BASE.IsVec3InZone}(): Returns if a 3D vector is within the zone.
-- * @{#ZONE_BASE.IsPointVec2InZone}(): Returns if a 2D point vector is within the zone.
-- * @{#ZONE_BASE.IsPointVec3InZone}(): Returns if a 3D point vector is within the zone.
--
-- ## A zone has a probability factor that can be set to randomize a selection between zones:
--
-- * @{#ZONE_BASE.SetZoneProbability}(): Set the randomization probability of a zone to be selected, taking a value between 0 and 1 ( 0 = 0%, 1 = 100% )
-- * @{#ZONE_BASE.GetZoneProbability}(): Get the randomization probability of a zone to be selected, passing a value between 0 and 1 ( 0 = 0%, 1 = 100% )
-- * @{#ZONE_BASE.GetZoneMaybe}(): Get the zone taking into account the randomization probability. nil is returned if this zone is not a candidate.
--
-- ## A zone manages vectors:
--
-- * @{#ZONE_BASE.GetVec2}(): Returns the 2D vector coordinate of the zone.
-- * @{#ZONE_BASE.GetVec3}(): Returns the 3D vector coordinate of the zone.
-- * @{#ZONE_BASE.GetPointVec2}(): Returns the 2D point vector coordinate of the zone.
-- * @{#ZONE_BASE.GetPointVec3}(): Returns the 3D point vector coordinate of the zone.
-- * @{#ZONE_BASE.GetRandomVec2}(): Define a random 2D vector within the zone.
-- * @{#ZONE_BASE.GetRandomPointVec2}(): Define a random 2D point vector within the zone.
-- * @{#ZONE_BASE.GetRandomPointVec3}(): Define a random 3D point vector within the zone.
--
-- ## A zone has a bounding square:
--
-- * @{#ZONE_BASE.GetBoundingSquare}(): Get the outer most bounding square of the zone.
--
-- ## A zone can be marked:
--
-- * @{#ZONE_BASE.SmokeZone}(): Smokes the zone boundaries in a color.
-- * @{#ZONE_BASE.FlareZone}(): Flares the zone boundaries in a color.
--
-- @field #ZONE_BASE
ZONE_BASE = {
ClassName = "ZONE_BASE",
ZoneName = "",
@@ -215,20 +149,21 @@ function ZONE_BASE:GetName()
return self.ZoneName
end
--- Returns if a location is within the zone.
--- Returns if a Vec2 is within the zone.
-- @param #ZONE_BASE self
-- @param Dcs.DCSTypes#Vec2 Vec2 The location to test.
-- @return #boolean true if the location is within the zone.
-- @param Dcs.DCSTypes#Vec2 Vec2 The Vec2 to test.
-- @return #boolean true if the Vec2 is within the zone.
function ZONE_BASE:IsVec2InZone( Vec2 )
self:F2( Vec2 )
return false
end
--- Returns if a point is within the zone.
--- Returns if a Vec3 is within the zone.
-- @param #ZONE_BASE self
-- @param Dcs.DCSTypes#Vec3 Vec3 The point to test.
-- @return #boolean true if the point is within the zone.
-- @return #boolean true if the Vec3 is within the zone.
function ZONE_BASE:IsVec3InZone( Vec3 )
self:F2( Vec3 )
@@ -237,6 +172,31 @@ function ZONE_BASE:IsVec3InZone( Vec3 )
return InZone
end
--- Returns if a PointVec2 is within the zone.
-- @param #ZONE_BASE self
-- @param Core.Point#POINT_VEC2 PointVec2 The PointVec2 to test.
-- @return #boolean true if the PointVec2 is within the zone.
function ZONE_BASE:IsPointVec2InZone( PointVec2 )
self:F2( PointVec2 )
local InZone = self:IsVec2InZone( PointVec2:GetVec2() )
return InZone
end
--- Returns if a PointVec3 is within the zone.
-- @param #ZONE_BASE self
-- @param Core.Point#POINT_VEC3 PointVec3 The PointVec3 to test.
-- @return #boolean true if the PointVec3 is within the zone.
function ZONE_BASE:IsPointVec3InZone( PointVec3 )
self:F2( PointVec3 )
local InZone = self:IsPointVec2InZone( PointVec3 )
return InZone
end
--- Returns the @{DCSTypes#Vec2} coordinate of the zone.
-- @param #ZONE_BASE self
-- @return #nil.
@@ -246,6 +206,91 @@ function ZONE_BASE:GetVec2()
return nil
end
--- Returns a @{Point#POINT_VEC2} of the zone.
-- @param #ZONE_BASE self
-- @param Dcs.DCSTypes#Distance Height The height to add to the land height where the center of the zone is located.
-- @return Core.Point#POINT_VEC2 The PointVec2 of the zone.
function ZONE_BASE:GetPointVec2()
self:F2( self.ZoneName )
local Vec2 = self:GetVec2()
local PointVec2 = POINT_VEC2:NewFromVec2( Vec2 )
self:T2( { PointVec2 } )
return PointVec2
end
--- Returns a @{Point#COORDINATE} of the zone.
-- @param #ZONE_BASE self
-- @param Dcs.DCSTypes#Distance Height The height to add to the land height where the center of the zone is located.
-- @return Core.Point#COORDINATE The Coordinate of the zone.
function ZONE_BASE:GetCoordinate()
self:F2( self.ZoneName )
local Vec2 = self:GetVec2()
local Coordinate = COORDINATE:NewFromVec2( Vec2 )
self:T2( { Coordinate } )
return Coordinate
end
--- Returns the @{DCSTypes#Vec3} of the zone.
-- @param #ZONE_BASE self
-- @param Dcs.DCSTypes#Distance Height The height to add to the land height where the center of the zone is located.
-- @return Dcs.DCSTypes#Vec3 The Vec3 of the zone.
function ZONE_BASE:GetVec3( Height )
self:F2( self.ZoneName )
Height = Height or 0
local Vec2 = self:GetVec2()
local Vec3 = { x = Vec2.x, y = land.getHeight( self:GetVec2() ) + Height, z = Vec2.y }
self:T2( { Vec3 } )
return Vec3
end
--- Returns a @{Point#POINT_VEC3} of the zone.
-- @param #ZONE_BASE self
-- @param Dcs.DCSTypes#Distance Height The height to add to the land height where the center of the zone is located.
-- @return Core.Point#POINT_VEC3 The PointVec3 of the zone.
function ZONE_BASE:GetPointVec3( Height )
self:F2( self.ZoneName )
local Vec3 = self:GetVec3( Height )
local PointVec3 = POINT_VEC3:NewFromVec3( Vec3 )
self:T2( { PointVec3 } )
return PointVec3
end
--- Returns a @{Point#COORDINATE} of the zone.
-- @param #ZONE_BASE self
-- @param Dcs.DCSTypes#Distance Height The height to add to the land height where the center of the zone is located.
-- @return Core.Point#COORDINATE The Coordinate of the zone.
function ZONE_BASE:GetCoordinate( Height ) --R2.1
self:F2( self.ZoneName )
local Vec3 = self:GetVec3( Height )
local PointVec3 = COORDINATE:NewFromVec3( Vec3 )
self:T2( { PointVec3 } )
return PointVec3
end
--- Define a random @{DCSTypes#Vec2} within the zone.
-- @param #ZONE_BASE self
-- @return Dcs.DCSTypes#Vec2 The Vec2 coordinates.
@@ -260,6 +305,13 @@ function ZONE_BASE:GetRandomPointVec2()
return nil
end
--- Define a random @{Point#POINT_VEC3} within the zone.
-- @param #ZONE_BASE self
-- @return Core.Point#POINT_VEC3 The PointVec3 coordinates.
function ZONE_BASE:GetRandomPointVec3()
return nil
end
--- Get the bounding square the zone.
-- @param #ZONE_BASE self
-- @return #nil The bounding square.
@@ -268,6 +320,12 @@ function ZONE_BASE:GetBoundingSquare()
return nil
end
--- Bound the zone boundaries with a tires.
-- @param #ZONE_BASE self
function ZONE_BASE:BoundZone()
self:F2()
end
--- Smokes the zone boundaries in a color.
-- @param #ZONE_BASE self
@@ -316,7 +374,37 @@ end
-- @type ZONE_RADIUS
-- @field Dcs.DCSTypes#Vec2 Vec2 The current location of the zone.
-- @field Dcs.DCSTypes#Distance Radius The radius of the zone.
-- @extends Core.Zone#ZONE_BASE
-- @extends #ZONE_BASE
--- # ZONE_RADIUS class, extends @{Zone#ZONE_BASE}
--
-- The ZONE_RADIUS class defined by a zone name, a location and a radius.
-- This class implements the inherited functions from Core.Zone#ZONE_BASE taking into account the own zone format and properties.
--
-- ## ZONE_RADIUS constructor
--
-- * @{#ZONE_RADIUS.New}(): Constructor.
--
-- ## Manage the radius of the zone
--
-- * @{#ZONE_RADIUS.SetRadius}(): Sets the radius of the zone.
-- * @{#ZONE_RADIUS.GetRadius}(): Returns the radius of the zone.
--
-- ## Manage the location of the zone
--
-- * @{#ZONE_RADIUS.SetVec2}(): Sets the @{DCSTypes#Vec2} of the zone.
-- * @{#ZONE_RADIUS.GetVec2}(): Returns the @{DCSTypes#Vec2} of the zone.
-- * @{#ZONE_RADIUS.GetVec3}(): Returns the @{DCSTypes#Vec3} of the zone, taking an additional height parameter.
--
-- ## Zone point randomization
--
-- Various functions exist to find random points within the zone.
--
-- * @{#ZONE_RADIUS.GetRandomVec2}(): Gets a random 2D point in the zone.
-- * @{#ZONE_RADIUS.GetRandomPointVec2}(): Gets a @{Point#POINT_VEC2} object representing a random 2D point in the zone.
-- * @{#ZONE_RADIUS.GetRandomPointVec3}(): Gets a @{Point#POINT_VEC3} object representing a random 3D point in the zone. Note that the height of the point is at landheight.
--
-- @field #ZONE_RADIUS
ZONE_RADIUS = {
ClassName="ZONE_RADIUS",
}
@@ -328,7 +416,7 @@ ZONE_RADIUS = {
-- @param Dcs.DCSTypes#Distance Radius The radius of the zone.
-- @return #ZONE_RADIUS self
function ZONE_RADIUS:New( ZoneName, Vec2, Radius )
local self = BASE:Inherit( self, ZONE_BASE:New( ZoneName ) )
local self = BASE:Inherit( self, ZONE_BASE:New( ZoneName ) ) -- #ZONE_RADIUS
self:F( { ZoneName, Vec2, Radius } )
self.Radius = Radius
@@ -337,6 +425,52 @@ function ZONE_RADIUS:New( ZoneName, Vec2, Radius )
return self
end
--- Bounds the zone with tires.
-- @param #ZONE_RADIUS self
-- @param #number Points (optional) The amount of points in the circle.
-- @param #boolean UnBound If true the tyres will be destroyed.
-- @return #ZONE_RADIUS self
function ZONE_RADIUS:BoundZone( Points, CountryID, UnBound )
local Point = {}
local Vec2 = self:GetVec2()
Points = Points and Points or 360
local Angle
local RadialBase = math.pi*2
--
for Angle = 0, 360, (360 / Points ) do
local Radial = Angle * RadialBase / 360
Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius()
Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius()
local CountryName = _DATABASE.COUNTRY_NAME[CountryID]
local Tire = {
["country"] = CountryName,
["category"] = "Fortifications",
["canCargo"] = false,
["shape_name"] = "H-tyre_B_WF",
["type"] = "Black_Tyre_WF",
--["unitId"] = Angle + 10000,
["y"] = Point.y,
["x"] = Point.x,
["name"] = string.format( "%s-Tire #%0d", self:GetName(), Angle ),
["heading"] = 0,
} -- end of ["group"]
local Group = coalition.addStaticObject( CountryID, Tire )
if UnBound and UnBound == true then
Group:destroy()
end
end
return self
end
--- Smokes the zone boundaries in a color.
-- @param #ZONE_RADIUS self
-- @param Utilities.Utils#SMOKECOLOR SmokeColor The smoke color.
@@ -542,9 +676,16 @@ end
--- The ZONE class, defined by the zone name as defined within the Mission Editor. The location and the radius are automatically collected from the mission settings.
-- @type ZONE
-- @extends Core.Zone#ZONE_RADIUS
--- @type ZONE
-- @extends #ZONE_RADIUS
--- # ZONE class, extends @{Zone#ZONE_RADIUS}
--
-- The ZONE class, defined by the zone name as defined within the Mission Editor.
-- This class implements the inherited functions from @{#ZONE_RADIUS} taking into account the own zone format and properties.
--
-- @field #ZONE
ZONE = {
ClassName="ZONE",
}
@@ -572,10 +713,16 @@ function ZONE:New( ZoneName )
end
--- The ZONE_UNIT class defined by a zone around a @{Unit#UNIT} with a radius.
-- @type ZONE_UNIT
--- @type ZONE_UNIT
-- @field Wrapper.Unit#UNIT ZoneUNIT
-- @extends Core.Zone#ZONE_RADIUS
--- # ZONE_UNIT class, extends @{Zone#ZONE_RADIUS}
--
-- The ZONE_UNIT class defined by a zone around a @{Unit#UNIT} with a radius.
-- This class implements the inherited functions from @{#ZONE_RADIUS} taking into account the own zone format and properties.
--
-- @field #ZONE_UNIT
ZONE_UNIT = {
ClassName="ZONE_UNIT",
}
@@ -601,7 +748,7 @@ end
-- @param #ZONE_UNIT self
-- @return Dcs.DCSTypes#Vec2 The location of the zone based on the @{Unit#UNIT}location.
function ZONE_UNIT:GetVec2()
self:F( self.ZoneName )
self:F2( self.ZoneName )
local ZoneVec2 = self.ZoneUNIT:GetVec2()
if ZoneVec2 then
@@ -611,7 +758,7 @@ function ZONE_UNIT:GetVec2()
return self.LastVec2
end
self:T( { ZoneVec2 } )
self:T2( { ZoneVec2 } )
return nil
end
@@ -656,10 +803,16 @@ function ZONE_UNIT:GetVec3( Height )
return Vec3
end
--- The ZONE_GROUP class defined by a zone around a @{Group}, taking the average center point of all the units within the Group, with a radius.
-- @type ZONE_GROUP
-- @field Wrapper.Group#GROUP ZoneGROUP
-- @extends Core.Zone#ZONE_RADIUS
--- @type ZONE_GROUP
-- @extends #ZONE_RADIUS
--- # ZONE_GROUP class, extends @{Zone#ZONE_RADIUS}
--
-- The ZONE_GROUP class defines by a zone around a @{Group#GROUP} with a radius. The current leader of the group defines the center of the zone.
-- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties.
--
-- @field #ZONE_GROUP
ZONE_GROUP = {
ClassName="ZONE_GROUP",
}
@@ -674,7 +827,7 @@ function ZONE_GROUP:New( ZoneName, ZoneGROUP, Radius )
local self = BASE:Inherit( self, ZONE_RADIUS:New( ZoneName, ZoneGROUP:GetVec2(), Radius ) )
self:F( { ZoneName, ZoneGROUP:GetVec2(), Radius } )
self.ZoneGROUP = ZoneGROUP
self._.ZoneGROUP = ZoneGROUP
return self
end
@@ -686,7 +839,7 @@ end
function ZONE_GROUP:GetVec2()
self:F( self.ZoneName )
local ZoneVec2 = self.ZoneGROUP:GetVec2()
local ZoneVec2 = self._.ZoneGROUP:GetVec2()
self:T( { ZoneVec2 } )
@@ -700,7 +853,7 @@ function ZONE_GROUP:GetRandomVec2()
self:F( self.ZoneName )
local Point = {}
local Vec2 = self.ZoneGROUP:GetVec2()
local Vec2 = self._.ZoneGROUP:GetVec2()
local angle = math.random() * math.pi*2;
Point.x = Vec2.x + math.cos( angle ) * math.random() * self:GetRadius();
@@ -713,12 +866,26 @@ end
-- Polygons
--- @type ZONE_POLYGON_BASE
-- --@field #ZONE_POLYGON_BASE.ListVec2 Polygon The polygon defined by an array of @{DCSTypes#Vec2}.
-- @extends #ZONE_BASE
--- The ZONE_POLYGON_BASE class defined by an array of @{DCSTypes#Vec2}, forming a polygon.
-- @type ZONE_POLYGON_BASE
-- @field #ZONE_POLYGON_BASE.ListVec2 Polygon The polygon defined by an array of @{DCSTypes#Vec2}.
-- @extends Core.Zone#ZONE_BASE
--- # ZONE_POLYGON_BASE class, extends @{Zone#ZONE_BASE}
--
-- The ZONE_POLYGON_BASE class defined by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon.
-- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties.
-- This class is an abstract BASE class for derived classes, and is not meant to be instantiated.
--
-- ## Zone point randomization
--
-- Various functions exist to find random points within the zone.
--
-- * @{#ZONE_POLYGON_BASE.GetRandomVec2}(): Gets a random 2D point in the zone.
-- * @{#ZONE_POLYGON_BASE.GetRandomPointVec2}(): Return a @{Point#POINT_VEC2} object representing a random 2D point within the zone.
-- * @{#ZONE_POLYGON_BASE.GetRandomPointVec3}(): Return a @{Point#POINT_VEC3} object representing a random 3D point at landheight within the zone.
--
-- @field #ZONE_POLYGON_BASE
ZONE_POLYGON_BASE = {
ClassName="ZONE_POLYGON_BASE",
}
@@ -739,28 +906,87 @@ function ZONE_POLYGON_BASE:New( ZoneName, PointsArray )
local i = 0
self.Polygon = {}
self._.Polygon = {}
for i = 1, #PointsArray do
self.Polygon[i] = {}
self.Polygon[i].x = PointsArray[i].x
self.Polygon[i].y = PointsArray[i].y
self._.Polygon[i] = {}
self._.Polygon[i].x = PointsArray[i].x
self._.Polygon[i].y = PointsArray[i].y
end
return self
end
--- Returns the center location of the polygon.
-- @param #ZONE_GROUP self
-- @return Dcs.DCSTypes#Vec2 The location of the zone based on the @{Group} location.
function ZONE_POLYGON_BASE:GetVec2()
self:F( self.ZoneName )
local Bounds = self:GetBoundingSquare()
return { x = ( Bounds.x2 + Bounds.x1 ) / 2, y = ( Bounds.y2 + Bounds.y1 ) / 2 }
end
--- Flush polygon coordinates as a table in DCS.log.
-- @param #ZONE_POLYGON_BASE self
-- @return #ZONE_POLYGON_BASE self
function ZONE_POLYGON_BASE:Flush()
self:F2()
self:E( { Polygon = self.ZoneName, Coordinates = self.Polygon } )
self:E( { Polygon = self.ZoneName, Coordinates = self._.Polygon } )
return self
end
--- Smokes the zone boundaries in a color.
-- @param #ZONE_POLYGON_BASE self
-- @param #boolean UnBound If true, the tyres will be destroyed.
-- @return #ZONE_POLYGON_BASE self
function ZONE_POLYGON_BASE:BoundZone( UnBound )
local i
local j
local Segments = 10
i = 1
j = #self._.Polygon
while i <= #self._.Polygon do
self:T( { i, j, self._.Polygon[i], self._.Polygon[j] } )
local DeltaX = self._.Polygon[j].x - self._.Polygon[i].x
local DeltaY = self._.Polygon[j].y - self._.Polygon[i].y
for Segment = 0, Segments do -- We divide each line in 5 segments and smoke a point on the line.
local PointX = self._.Polygon[i].x + ( Segment * DeltaX / Segments )
local PointY = self._.Polygon[i].y + ( Segment * DeltaY / Segments )
local Tire = {
["country"] = "USA",
["category"] = "Fortifications",
["canCargo"] = false,
["shape_name"] = "H-tyre_B_WF",
["type"] = "Black_Tyre_WF",
["y"] = PointY,
["x"] = PointX,
["name"] = string.format( "%s-Tire #%0d", self:GetName(), ((i - 1) * Segments) + Segment ),
["heading"] = 0,
} -- end of ["group"]
local Group = coalition.addStaticObject( country.id.USA, Tire )
if UnBound and UnBound == true then
Group:destroy()
end
end
j = i
i = i + 1
end
return self
end
--- Smokes the zone boundaries in a color.
-- @param #ZONE_POLYGON_BASE self
@@ -774,17 +1000,17 @@ function ZONE_POLYGON_BASE:SmokeZone( SmokeColor )
local Segments = 10
i = 1
j = #self.Polygon
j = #self._.Polygon
while i <= #self.Polygon do
self:T( { i, j, self.Polygon[i], self.Polygon[j] } )
while i <= #self._.Polygon do
self:T( { i, j, self._.Polygon[i], self._.Polygon[j] } )
local DeltaX = self.Polygon[j].x - self.Polygon[i].x
local DeltaY = self.Polygon[j].y - self.Polygon[i].y
local DeltaX = self._.Polygon[j].x - self._.Polygon[i].x
local DeltaY = self._.Polygon[j].y - self._.Polygon[i].y
for Segment = 0, Segments do -- We divide each line in 5 segments and smoke a point on the line.
local PointX = self.Polygon[i].x + ( Segment * DeltaX / Segments )
local PointY = self.Polygon[i].y + ( Segment * DeltaY / Segments )
local PointX = self._.Polygon[i].x + ( Segment * DeltaX / Segments )
local PointY = self._.Polygon[i].y + ( Segment * DeltaY / Segments )
POINT_VEC2:New( PointX, PointY ):Smoke( SmokeColor )
end
j = i
@@ -810,12 +1036,12 @@ function ZONE_POLYGON_BASE:IsVec2InZone( Vec2 )
local InPolygon = false
Next = 1
Prev = #self.Polygon
Prev = #self._.Polygon
while Next <= #self.Polygon do
self:T( { Next, Prev, self.Polygon[Next], self.Polygon[Prev] } )
if ( ( ( self.Polygon[Next].y > Vec2.y ) ~= ( self.Polygon[Prev].y > Vec2.y ) ) and
( Vec2.x < ( self.Polygon[Prev].x - self.Polygon[Next].x ) * ( Vec2.y - self.Polygon[Next].y ) / ( self.Polygon[Prev].y - self.Polygon[Next].y ) + self.Polygon[Next].x )
while Next <= #self._.Polygon do
self:T( { Next, Prev, self._.Polygon[Next], self._.Polygon[Prev] } )
if ( ( ( self._.Polygon[Next].y > Vec2.y ) ~= ( self._.Polygon[Prev].y > Vec2.y ) ) and
( Vec2.x < ( self._.Polygon[Prev].x - self._.Polygon[Next].x ) * ( Vec2.y - self._.Polygon[Next].y ) / ( self._.Polygon[Prev].y - self._.Polygon[Next].y ) + self._.Polygon[Next].x )
) then
InPolygon = not InPolygon
end
@@ -886,17 +1112,17 @@ end
-- @return #ZONE_POLYGON_BASE.BoundingSquare The bounding square.
function ZONE_POLYGON_BASE:GetBoundingSquare()
local x1 = self.Polygon[1].x
local y1 = self.Polygon[1].y
local x2 = self.Polygon[1].x
local y2 = self.Polygon[1].y
local x1 = self._.Polygon[1].x
local y1 = self._.Polygon[1].y
local x2 = self._.Polygon[1].x
local y2 = self._.Polygon[1].y
for i = 2, #self.Polygon do
self:T2( { self.Polygon[i], x1, y1, x2, y2 } )
x1 = ( x1 > self.Polygon[i].x ) and self.Polygon[i].x or x1
x2 = ( x2 < self.Polygon[i].x ) and self.Polygon[i].x or x2
y1 = ( y1 > self.Polygon[i].y ) and self.Polygon[i].y or y1
y2 = ( y2 < self.Polygon[i].y ) and self.Polygon[i].y or y2
for i = 2, #self._.Polygon do
self:T2( { self._.Polygon[i], x1, y1, x2, y2 } )
x1 = ( x1 > self._.Polygon[i].x ) and self._.Polygon[i].x or x1
x2 = ( x2 < self._.Polygon[i].x ) and self._.Polygon[i].x or x2
y1 = ( y1 > self._.Polygon[i].y ) and self._.Polygon[i].y or y1
y2 = ( y2 < self._.Polygon[i].y ) and self._.Polygon[i].y or y2
end
@@ -904,12 +1130,16 @@ function ZONE_POLYGON_BASE:GetBoundingSquare()
end
--- @type ZONE_POLYGON
-- @extends #ZONE_POLYGON_BASE
--- The ZONE_POLYGON class defined by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon.
-- @type ZONE_POLYGON
-- @extends Core.Zone#ZONE_POLYGON_BASE
--- # ZONE_POLYGON class, extends @{Zone#ZONE_POLYGON_BASE}
--
-- The ZONE_POLYGON class defined by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon.
-- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties.
--
-- @field #ZONE_POLYGON
ZONE_POLYGON = {
ClassName="ZONE_POLYGON",
}
@@ -925,7 +1155,7 @@ function ZONE_POLYGON:New( ZoneName, ZoneGroup )
local GroupPoints = ZoneGroup:GetTaskRoute()
local self = BASE:Inherit( self, ZONE_POLYGON_BASE:New( ZoneName, GroupPoints ) )
self:F( { ZoneName, ZoneGroup, self.Polygon } )
self:F( { ZoneName, ZoneGroup, self._.Polygon } )
return self
end

View File

@@ -29,7 +29,9 @@ CLEANUP = {
-- or
-- CleanUpTbilisi = CLEANUP:New( 'CLEAN Tbilisi', 150 )
-- CleanUpKutaisi = CLEANUP:New( 'CLEAN Kutaisi', 600 )
function CLEANUP:New( ZoneNames, TimeInterval ) local self = BASE:Inherit( self, BASE:New() )
function CLEANUP:New( ZoneNames, TimeInterval )
local self = BASE:Inherit( self, BASE:New() ) -- #CLEANUP
self:F( { ZoneNames, TimeInterval } )
if type( ZoneNames ) == 'table' then
@@ -41,7 +43,7 @@ function CLEANUP:New( ZoneNames, TimeInterval ) local self = BASE:Inherit( self,
self.TimeInterval = TimeInterval
end
_EVENTDISPATCHER:OnBirth( self._OnEventBirth, self )
self:HandleEvent( EVENTS.Birth )
self.CleanUpScheduler = SCHEDULER:New( self, self._CleanUpScheduler, {}, 1, TimeInterval )
@@ -102,32 +104,24 @@ function CLEANUP:_DestroyMissile( MissileObject )
end
end
function CLEANUP:_OnEventBirth( Event )
self:F( { Event } )
--- @param #CLEANUP self
-- @param Core.Event#EVENTDATA EventData
function CLEANUP:_OnEventBirth( EventData )
self:F( { EventData } )
self.CleanUpList[Event.IniDCSUnitName] = {}
self.CleanUpList[Event.IniDCSUnitName].CleanUpUnit = Event.IniDCSUnit
self.CleanUpList[Event.IniDCSUnitName].CleanUpGroup = Event.IniDCSGroup
self.CleanUpList[Event.IniDCSUnitName].CleanUpGroupName = Event.IniDCSGroupName
self.CleanUpList[Event.IniDCSUnitName].CleanUpUnitName = Event.IniDCSUnitName
_EVENTDISPATCHER:OnEngineShutDownForUnit( Event.IniDCSUnitName, self._EventAddForCleanUp, self )
_EVENTDISPATCHER:OnEngineStartUpForUnit( Event.IniDCSUnitName, self._EventAddForCleanUp, self )
_EVENTDISPATCHER:OnHitForUnit( Event.IniDCSUnitName, self._EventAddForCleanUp, self )
_EVENTDISPATCHER:OnPilotDeadForUnit( Event.IniDCSUnitName, self._EventCrash, self )
_EVENTDISPATCHER:OnDeadForUnit( Event.IniDCSUnitName, self._EventCrash, self )
_EVENTDISPATCHER:OnCrashForUnit( Event.IniDCSUnitName, self._EventCrash, self )
_EVENTDISPATCHER:OnShotForUnit( Event.IniDCSUnitName, self._EventShot, self )
--self:AddEvent( world.event.S_EVENT_ENGINE_SHUTDOWN, self._EventAddForCleanUp )
--self:AddEvent( world.event.S_EVENT_ENGINE_STARTUP, self._EventAddForCleanUp )
-- self:AddEvent( world.event.S_EVENT_HIT, self._EventAddForCleanUp ) -- , self._EventHitCleanUp )
-- self:AddEvent( world.event.S_EVENT_CRASH, self._EventCrash ) -- , self._EventHitCleanUp )
-- --self:AddEvent( world.event.S_EVENT_DEAD, self._EventCrash )
-- self:AddEvent( world.event.S_EVENT_SHOT, self._EventShot )
--
-- self:EnableEvents()
self.CleanUpList[EventData.IniDCSUnitName] = {}
self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnit = EventData.IniDCSUnit
self.CleanUpList[EventData.IniDCSUnitName].CleanUpGroup = EventData.IniDCSGroup
self.CleanUpList[EventData.IniDCSUnitName].CleanUpGroupName = EventData.IniDCSGroupName
self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnitName = EventData.IniDCSUnitName
EventData.IniUnit:HandleEvent( EVENTS.EngineShutdown , self._EventAddForCleanUp )
EventData.IniUnit:HandleEvent( EVENTS.EngineStartup, self._EventAddForCleanUp )
EventData.IniUnit:HandleEvent( EVENTS.Hit, self._EventAddForCleanUp )
EventData.IniUnit:HandleEvent( EVENTS.PilotDead, self._EventCrash )
EventData.IniUnit:HandleEvent( EVENTS.Dead, self._EventCrash )
EventData.IniUnit:HandleEvent( EVENTS.Crash, self._EventCrash )
EventData.IniUnit:HandleEvent( EVENTS.Shot, self._EventShot )
end

View File

@@ -0,0 +1,924 @@
--- **Funtional R2.1** -- Management of target **Designation**.
--
-- --![Banner Image](..\Presentations\DESIGNATE\Dia1.JPG)
--
-- ===
--
-- DESIGNATE is orchestrating the designation of potential targets executed by a Recce group,
-- and communicates these to a dedicated attacking group of players,
-- so that following a dynamically generated menu system,
-- each detected set of potential targets can be lased or smoked...
--
-- Targets can be:
--
-- * **Lased** for a period of time.
-- * **Smoked**. Artillery or airplanes with Illuminatino ordonance need to be present. (WIP, but early demo ready.)
-- * **Illuminated** through an illumination bomb. Artillery or airplanes with Illuminatino ordonance need to be present. (WIP, but early demo ready.
--
-- ===
--
-- # **AUTHORS and CONTRIBUTIONS**
--
-- ### Contributions:
--
-- * [**Ciribob**](https://forums.eagle.ru/member.php?u=112175): Showing the way how to lase targets + how laser codes work!!! Explained the autolase script.
-- * [**EasyEB**](https://forums.eagle.ru/member.php?u=112055): Ideas and Beta Testing
-- * [**Wingthor**](https://forums.eagle.ru/member.php?u=123698): Beta Testing
--
--
-- ### Authors:
--
-- * **FlightControl**: Design & Programming
--
-- @module Designate
do -- DESIGNATE
--- @type DESIGNATE
-- @extends Core.Fsm#FSM_PROCESS
--- # DESIGNATE class, extends @{Fsm#FSM}
--
-- DESIGNATE is orchestrating the designation of potential targets executed by a Recce group,
-- and communicates these to a dedicated attacking group of players,
-- so that following a dynamically generated menu system,
-- each detected set of potential targets can be lased or smoked...
--
-- Targets can be:
--
-- * **Lased** for a period of time.
-- * **Smoked**. Artillery or airplanes with Illuminatino ordonance need to be present. (WIP, but early demo ready.)
-- * **Illuminated** through an illumination bomb. Artillery or airplanes with Illuminatino ordonance need to be present. (WIP, but early demo ready.
--
-- The following terminology is being used throughout this document:
--
-- * The **DesignateObject** is the object of the DESIGNATE class, which is this class explained in the document.
-- * The **DetectionObject** is the object of a DETECTION_ class (DETECTION_TYPES, DETECTION_AREAS, DETECTION_UNITS), which is executing the detection and grouping of Targets into _DetectionItems_.
-- * **DetectionItems** is the list of detected target groupings by the _DetectionObject_. Each _DetectionItem_ contains a _TargetSet_.
-- * **DetectionItem** is one element of the _DetectionItems_ list, and contains a _TargetSet_.
-- * The **TargetSet** is a SET_UNITS collection of _Targets_, that have been detected by the _DetectionObject_.
-- * A **Target** is a detected UNIT object by the _DetectionObject_.
-- * A **Threat Level** is a number from 0 to 10 that is calculated based on the threat of the Target in an Air to Ground battle scenario.
-- * The **RecceSet** is a SET_GROUP collection that contains the **RecceGroups**.
-- * A **RecceGroup** is a GROUP object containing the **Recces**.
-- * A **Recce** is a UNIT object executing the reconnaissance as part the _DetectionObject_. A Recce can be of any UNIT type.
-- * An **AttackGroup** is a GROUP object that contain _Players_.
-- * A **Player** is an active CLIENT object containing a human player.
-- * A **Designate Menu** is the menu that is dynamically created during the designation process for each _AttackGroup_.
--
-- The RecceSet is continuously detecting for potential Targets, executing its task as part of the DetectionObject.
-- Once Targets have been detected, the DesignateObject will trigger the **Detect Event**.
--
-- As part of the Detect Event, the DetectionItems list is used by the DesignateObject to provide the Players with:
--
-- * The RecceGroups are reporting to each AttackGroup, sending **Messages** containing the Threat Level and the TargetSet composition.
-- * **Menu options** are created and updated for each AttackGroup, containing the Threat Level and the TargetSet composition.
--
-- A Player can then select an action from the Designate Menu.
--
-- **Note that each selected action will be executed for a TargetSet, thus the Target grouping done by the DetectionObject.**
--
-- Each **Menu Option** in the Designate Menu has two modes:
--
-- 1. If the TargetSet **is not being designated**, then the **Designate Menu** option for the target Set will provide options to **Lase** or **Smoke** the targets.
-- 2. If the Target Set **is being designated**, then the **Designate Menu** option will provide an option to stop or cancel the designation.
--
-- While designating, the RecceGroups will report any change in TargetSet composition or Target presence.
--
-- The following logic is executed when a TargetSet is selected to be *lased* from the Designation Menu:
--
-- * The RecceSet is searched for any Recce that is within *designation distance* from a Target in the TargetSet that is currently not being designated.
-- * If there is a Recce found that is currently no designating a target, and is within designation distance from the Target, then that Target will be designated.
-- * During designation, any Recce that does not have Line of Sight (LOS) and is not within disignation distance from the Target, will stop designating the Target, and a report is given.
-- * When a Recce is designating a Target, and that Target is destroyed, then the Recce will stop designating the Target, and will report the event.
-- * When a Recce is designating a Target, and that Recce is destroyed, then the Recce will be removed from the RecceSet and designation will stop without reporting.
-- * When all RecceGroups are destroyed from the RecceSet, then the DesignationObject will stop functioning, and nothing will be reported.
--
-- In this way, the DesignationObject assists players to designate ground targets for a coordinated attack!
--
-- Have FUN!
--
-- ## 1. DESIGNATE constructor
--
-- * @{#DESIGNATE.New}(): Creates a new DESIGNATE object.
--
-- ## 2. DESIGNATE is a FSM
--
-- ![Process](..\Presentations\DESIGNATE\Dia2.JPG)
--
-- ### 2.1 DESIGNATE States
--
-- * **Designating** ( Group ): The process is not started yet.
--
-- ### 2.2 DESIGNATE Events
--
-- * **@{#DESIGNATE.Detect}**: Detect targets.
-- * **@{#DESIGNATE.LaseOn}**: Lase the targets with the specified Index.
-- * **@{#DESIGNATE.LaseOff}**: Stop lasing the targets with the specified Index.
-- * **@{#DESIGNATE.Smoke}**: Smoke the targets with the specified Index.
-- * **@{#DESIGNATE.Status}**: Report designation status.
--
-- ## 3. Laser codes
--
-- ### 3.1 Set possible laser codes
--
-- An array of laser codes can be provided, that will be used by the DESIGNATE when lasing.
-- The laser code is communicated by the Recce when it is lasing a larget.
-- Note that the default laser code is 1113.
-- Working known laser codes are: 1113,1462,1483,1537,1362,1214,1131,1182,1644,1614,1515,1411,1621,1138,1542,1678,1573,1314,1643,1257,1467,1375,1341,1275,1237
--
-- Use the method @{#DESIGNATE.SetLaserCodes}() to set the possible laser codes to be selected from.
-- One laser code can be given or an sequence of laser codes through an table...
--
-- Designate:SetLaserCodes( 1214 )
--
-- The above sets one laser code with the value 1214.
--
-- Designate:SetLaserCodes( { 1214, 1131, 1614, 1138 } )
--
-- The above sets a collection of possible laser codes that can be assigned. **Note the { } notation!**
--
-- ### 3.2 Auto generate laser codes
--
-- Use the method @{#DESIGNATE.GenerateLaserCodes}() to generate all possible laser codes. Logic implemented and advised by Ciribob!
--
-- ## 4. Autolase to automatically lase detected targets.
--
-- DetectionItems can be auto lased once detected by Recces. As such, there is almost no action required from the Players using the Designate Menu.
-- The **auto lase** function can be activated through the Designation Menu.
-- Use the method @{#DESIGNATE.SetAutoLase}() to activate or deactivate the auto lase function programmatically.
-- Note that autolase will automatically activate lasing for ALL DetectedItems. Individual items can be switched-off if required using the Designation Menu.
--
-- Designate:SetAutoLase( true )
--
-- Activate the auto lasing.
--
-- ## 5. Target prioritization on threat level
--
-- Targets can be detected of different types in one DetectionItem. Depending on the type of the Target, a different threat level applies in an Air to Ground combat context.
-- SAMs are of a higher threat than normal tanks. So, if the Target type was recognized, the Recces will select those targets that form the biggest threat first,
-- and will continue this until the remaining vehicles with the lowest threat have been reached.
--
-- This threat level prioritization can be activated using the method @{#DESIGNATE.SetThreatLevelPrioritization}().
-- If not activated, Targets will be selected in a random order, but most like those first which are the closest to the Recce marking the Target.
--
-- Designate:SetThreatLevelPrioritization( true )
--
-- The example will activate the threat level prioritization for this the Designate object. Threats will be marked based on the threat level of the Target.
--
-- ## 6. Status Report
--
-- A status report is available that displays the current Targets detected, grouped per DetectionItem, and a list of which Targets are currently being marked.
--
-- * The status report can be shown by selecting "Status" -> "Report Status" from the Designation menu .
-- * The status report can be automatically flashed by selecting "Status" -> "Flash Status On".
-- * The automatic flashing of the status report can be deactivated by selecting "Status" -> "Flash Status Off".
-- * The flashing of the status menu is disabled by default.
-- * The method @{#DESIGNATE.FlashStatusMenu}() can be used to enable or disable to flashing of the status menu.
--
-- Designate:FlashStatusMenu( true )
--
-- The example will activate the flashing of the status menu for this Designate object.
--
-- @field #DESIGNATE
--
DESIGNATE = {
ClassName = "DESIGNATE",
}
--- DESIGNATE Constructor. This class is an abstract class and should not be instantiated.
-- @param #DESIGNATE self
-- @param Tasking.CommandCenter#COMMANDCENTER CC
-- @param Functional.Detection#DETECTION_BASE Detection
-- @param Core.Set#SET_GROUP AttackSet The Attack collection of GROUP objects to designate and report for.
-- @return #DESIGNATE
function DESIGNATE:New( CC, Detection, AttackSet )
local self = BASE:Inherit( self, FSM:New() ) -- #DESIGNATE
self:F( { Detection } )
self:SetStartState( "Designating" )
self:AddTransition( "*", "Detect", "*" )
--- Detect Handler OnBefore for DESIGNATE
-- @function [parent=#DESIGNATE] OnBeforeDetect
-- @param #DESIGNATE self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #boolean
--- Detect Handler OnAfter for DESIGNATE
-- @function [parent=#DESIGNATE] OnAfterDetect
-- @param #DESIGNATE self
-- @param #string From
-- @param #string Event
-- @param #string To
--- Detect Trigger for DESIGNATE
-- @function [parent=#DESIGNATE] Detect
-- @param #DESIGNATE self
--- Detect Asynchronous Trigger for DESIGNATE
-- @function [parent=#DESIGNATE] __Detect
-- @param #DESIGNATE self
-- @param #number Delay
self:AddTransition( "*", "LaseOn", "Lasing" )
--- LaseOn Handler OnBefore for DESIGNATE
-- @function [parent=#DESIGNATE ] OnBeforeLaseOn
-- @param #DESIGNATE self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #boolean
--- LaseOn Handler OnAfter for DESIGNATE
-- @function [parent=#DESIGNATE ] OnAfterLaseOn
-- @param #DESIGNATE self
-- @param #string From
-- @param #string Event
-- @param #string To
--- LaseOn Trigger for DESIGNATE
-- @function [parent=#DESIGNATE ] LaseOn
-- @param #DESIGNATE self
--- LaseOn Asynchronous Trigger for DESIGNATE
-- @function [parent=#DESIGNATE ] __LaseOn
-- @param #DESIGNATE self
-- @param #number Delay
self:AddTransition( "Lasing", "Lasing", "Lasing" )
self:AddTransition( "*", "LaseOff", "Designate" )
--- LaseOff Handler OnBefore for DESIGNATE
-- @function [parent=#DESIGNATE ] OnBeforeLaseOff
-- @param #DESIGNATE self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #boolean
--- LaseOff Handler OnAfter for DESIGNATE
-- @function [parent=#DESIGNATE ] OnAfterLaseOff
-- @param #DESIGNATE self
-- @param #string From
-- @param #string Event
-- @param #string To
--- LaseOff Trigger for DESIGNATE
-- @function [parent=#DESIGNATE ] LaseOff
-- @param #DESIGNATE self
--- LaseOff Asynchronous Trigger for DESIGNATE
-- @function [parent=#DESIGNATE ] __LaseOff
-- @param #DESIGNATE self
-- @param #number Delay
self:AddTransition( "*", "Smoke", "*" )
--- Smoke Handler OnBefore for DESIGNATE
-- @function [parent=#DESIGNATE ] OnBeforeSmoke
-- @param #DESIGNATE self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #boolean
--- Smoke Handler OnAfter for DESIGNATE
-- @function [parent=#DESIGNATE ] OnAfterSmoke
-- @param #DESIGNATE self
-- @param #string From
-- @param #string Event
-- @param #string To
--- Smoke Trigger for DESIGNATE
-- @function [parent=#DESIGNATE ] Smoke
-- @param #DESIGNATE self
--- Smoke Asynchronous Trigger for DESIGNATE
-- @function [parent=#DESIGNATE ] __Smoke
-- @param #DESIGNATE self
-- @param #number Delay
self:AddTransition( "*", "Illuminate", "*" )
--- Illuminate Handler OnBefore for DESIGNATE
-- @function [parent=#DESIGNATE] OnBeforeIlluminate
-- @param #DESIGNATE self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #boolean
--- Illuminate Handler OnAfter for DESIGNATE
-- @function [parent=#DESIGNATE] OnAfterIlluminate
-- @param #DESIGNATE self
-- @param #string From
-- @param #string Event
-- @param #string To
--- Illuminate Trigger for DESIGNATE
-- @function [parent=#DESIGNATE] Illuminate
-- @param #DESIGNATE self
--- Illuminate Asynchronous Trigger for DESIGNATE
-- @function [parent=#DESIGNATE] __Illuminate
-- @param #DESIGNATE self
-- @param #number Delay
self:AddTransition( "*", "Done", "*" )
self:AddTransition( "*", "Status", "*" )
--- Status Handler OnBefore for DESIGNATE
-- @function [parent=#DESIGNATE ] OnBeforeStatus
-- @param #DESIGNATE self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #boolean
--- Status Handler OnAfter for DESIGNATE
-- @function [parent=#DESIGNATE ] OnAfterStatus
-- @param #DESIGNATE self
-- @param #string From
-- @param #string Event
-- @param #string To
--- Status Trigger for DESIGNATE
-- @function [parent=#DESIGNATE ] Status
-- @param #DESIGNATE self
--- Status Asynchronous Trigger for DESIGNATE
-- @function [parent=#DESIGNATE ] __Status
-- @param #DESIGNATE self
-- @param #number Delay
self.CC = CC
self.Detection = Detection
self.AttackSet = AttackSet
self.RecceSet = Detection:GetDetectionSetGroup()
self.Recces = {}
self.Designating = {}
self.LaseDuration = 60
self:SetFlashStatusMenu( false )
self:SetDesignateMenu()
self:SetLaserCodes( 1688 ) -- set self.LaserCodes
self:SetAutoLase( false ) -- set self.Autolase
self:SetThreatLevelPrioritization( false ) -- self.ThreatLevelPrioritization, default is threat level priorization off
self.LaserCodesUsed = {}
self.Detection:__Start( 2 )
return self
end
--- Set the flashing of the status menu.
-- @param #DESIGNATE self
-- @param #boolean FlashMenu true: the status menu will be flashed every detection run; false: no flashing of the menu.
-- @return #DESIGNATE
function DESIGNATE:SetFlashStatusMenu( FlashMenu ) --R2.1
self.FlashStatusMenu = {}
self.AttackSet:ForEachGroup(
--- @param Wrapper.Group#GROUP GroupReport
function( AttackGroup )
self.FlashStatusMenu[AttackGroup] = FlashMenu
end
)
return self
end
--- Set an array of possible laser codes.
-- Each new lase will select a code from this table.
-- @param #DESIGNATE self
-- @param #list<#number> LaserCodes
-- @return #DESIGNATE
function DESIGNATE:SetLaserCodes( LaserCodes ) --R2.1
self.LaserCodes = ( type( LaserCodes ) == "table" ) and LaserCodes or { LaserCodes }
self:E(self.LaserCodes)
self.LaserCodesUsed = {}
return self
end
--- Generate an array of possible laser codes.
-- Each new lase will select a code from this table.
-- The entered value can range from 1111 - 1788,
-- -- but the first digit of the series must be a 1 or 2
-- -- and the last three digits must be between 1 and 8.
-- The range used to be bugged so its not 1 - 8 but 0 - 7.
-- function below will use the range 1-7 just in case
-- @param #DESIGNATE self
-- @return #DESIGNATE
function DESIGNATE:GenerateLaserCodes() --R2.1
self.LaserCodes = {}
local function containsDigit(_number, _numberToFind)
local _thisNumber = _number
local _thisDigit = 0
while _thisNumber ~= 0 do
_thisDigit = _thisNumber % 10
_thisNumber = math.floor(_thisNumber / 10)
if _thisDigit == _numberToFind then
return true
end
end
return false
end
-- generate list of laser codes
local _code = 1111
local _count = 1
while _code < 1777 and _count < 30 do
while true do
_code = _code + 1
if not containsDigit(_code, 8)
and not containsDigit(_code, 9)
and not containsDigit(_code, 0) then
self:T(_code)
table.insert( self.LaserCodes, _code )
break
end
end
_count = _count + 1
end
self.LaserCodesUsed = {}
return self
end
--- Set auto lase.
-- Auto lase will start lasing targets immediately when these are in range.
-- @param #DESIGNATE self
-- @param #boolean AutoLase
-- @return #DESIGNATE
function DESIGNATE:SetAutoLase( AutoLase ) --R2.1
self.AutoLase = AutoLase
local AutoLaseOnOff = ( AutoLase == true ) and "On" or "Off"
local CC = self.CC:GetPositionable()
if CC then
CC:MessageToSetGroup( "Auto Lase " .. AutoLaseOnOff .. ".", 15, self.AttackSet )
end
self:ActivateAutoLase()
self:SetDesignateMenu()
return self
end
--- Set priorization of Targets based on the **Threat Level of the Target** in an Air to Ground context.
-- @param #DESIGNATE self
-- @param #boolean Prioritize
-- @return #DESIGNATE
function DESIGNATE:SetThreatLevelPrioritization( Prioritize ) --R2.1
self.ThreatLevelPrioritization = Prioritize
return self
end
---
-- @param #DESIGNATE self
-- @return #DESIGNATE
function DESIGNATE:onafterDetect()
self:__Detect( -60 )
self:ActivateAutoLase()
self:SendStatus()
self:SetDesignateMenu()
return self
end
--- Sends the status to the Attack Groups.
-- @param #DESIGNATE self
-- @param Wrapper.Group#GROUP AttackGroup
-- @param #number Duration The time in seconds the report should be visible.
-- @return #DESIGNATE
function DESIGNATE:SendStatus( MenuAttackGroup, Duration )
Duration = Duration or 10
self.AttackSet:ForEachGroup(
--- @param Wrapper.Group#GROUP GroupReport
function( AttackGroup )
if self.FlashStatusMenu[AttackGroup] or ( MenuAttackGroup and ( AttackGroup:GetName() == MenuAttackGroup:GetName() ) ) then
local DetectedReport = REPORT:New( "Targets designated:\n" )
local DetectedItems = self.Detection:GetDetectedItems()
for Index, DetectedItemData in pairs( DetectedItems ) do
local Report = self.Detection:DetectedItemReportSummary( Index )
DetectedReport:Add(" - " .. Report)
end
local CC = self.CC:GetPositionable()
CC:MessageToGroup( DetectedReport:Text( "\n" ), Duration, AttackGroup )
local DesignationReport = REPORT:New( "Targets marked:\n" )
self.RecceSet:ForEachGroup(
function( RecceGroup )
local RecceUnits = RecceGroup:GetUnits()
for UnitID, RecceData in pairs( RecceUnits ) do
local Recce = RecceData -- Wrapper.Unit#UNIT
if Recce:IsLasing() then
DesignationReport:Add( " - " .. Recce:GetMessageText( "Marking " .. Recce:GetSpot().Target:GetTypeName() .. " with laser " .. Recce:GetSpot().LaserCode .. "." ) )
end
end
end
)
CC:MessageToGroup( DesignationReport:Text(), Duration, AttackGroup )
end
end
)
return self
end
--- Coordinates the Auto Lase.
-- @param #DESIGNATE self
-- @return #DESIGNATE
function DESIGNATE:ActivateAutoLase()
self.AttackSet:Flush()
self.AttackSet:ForEachGroup(
--- @param Wrapper.Group#GROUP GroupReport
function( AttackGroup )
local DetectedItems = self.Detection:GetDetectedItems()
for Index, DetectedItemData in pairs( DetectedItems ) do
if self.AutoLase then
if not self.Designating[Index] then
self:LaseOn( Index, self.LaseDuration )
end
end
end
end
)
return self
end
--- Sets the Designate Menu.
-- @param #DESIGNATE self
-- @return #DESIGNATE
function DESIGNATE:SetDesignateMenu()
self.AttackSet:Flush()
self.AttackSet:ForEachGroup(
--- @param Wrapper.Group#GROUP GroupReport
function( AttackGroup )
local DesignateMenu = AttackGroup:GetState( AttackGroup, "DesignateMenu" ) -- Core.Menu#MENU_GROUP
if DesignateMenu then
DesignateMenu:Remove()
DesignateMenu = nil
self:E("Remove Menu")
end
DesignateMenu = MENU_GROUP:New( AttackGroup, "Designate" )
self:E(DesignateMenu)
AttackGroup:SetState( AttackGroup, "DesignateMenu", DesignateMenu )
-- Set Menu option for auto lase
if self.AutoLase then
MENU_GROUP_COMMAND:New( AttackGroup, "Auto Lase Off", DesignateMenu, self.MenuAutoLase, self, false )
else
MENU_GROUP_COMMAND:New( AttackGroup, "Auto Lase On", DesignateMenu, self.MenuAutoLase, self, true )
end
local StatusMenu = MENU_GROUP:New( AttackGroup, "Status", DesignateMenu )
MENU_GROUP_COMMAND:New( AttackGroup, "Report Status 15s", StatusMenu, self.MenuStatus, self, AttackGroup, 15 )
MENU_GROUP_COMMAND:New( AttackGroup, "Report Status 30s", StatusMenu, self.MenuStatus, self, AttackGroup, 30 )
MENU_GROUP_COMMAND:New( AttackGroup, "Report Status 60s", StatusMenu, self.MenuStatus, self, AttackGroup, 60 )
if self.FlashStatusMenu[AttackGroup] then
MENU_GROUP_COMMAND:New( AttackGroup, "Flash Status Report Off", StatusMenu, self.MenuFlashStatus, self, AttackGroup, false )
else
MENU_GROUP_COMMAND:New( AttackGroup, "Flash Status Report On", StatusMenu, self.MenuFlashStatus, self, AttackGroup, true )
end
local DetectedItems = self.Detection:GetDetectedItems()
for Index, DetectedItemData in pairs( DetectedItems ) do
local Report = self.Detection:DetectedItemMenu( Index )
if not self.Designating[Index] then
local DetectedMenu = MENU_GROUP:New( AttackGroup, Report, DesignateMenu )
MENU_GROUP_COMMAND:New( AttackGroup, "Lase target 60 secs", DetectedMenu, self.MenuLaseOn, self, Index, 60 )
MENU_GROUP_COMMAND:New( AttackGroup, "Lase target 120 secs", DetectedMenu, self.MenuLaseOn, self, Index, 120 )
MENU_GROUP_COMMAND:New( AttackGroup, "Smoke red", DetectedMenu, self.MenuSmoke, self, Index, SMOKECOLOR.Red )
MENU_GROUP_COMMAND:New( AttackGroup, "Smoke blue", DetectedMenu, self.MenuSmoke, self, Index, SMOKECOLOR.Blue )
MENU_GROUP_COMMAND:New( AttackGroup, "Smoke green", DetectedMenu, self.MenuSmoke, self, Index, SMOKECOLOR.Green )
MENU_GROUP_COMMAND:New( AttackGroup, "Smoke white", DetectedMenu, self.MenuSmoke, self, Index, SMOKECOLOR.White )
MENU_GROUP_COMMAND:New( AttackGroup, "Smoke orange", DetectedMenu, self.MenuSmoke, self, Index, SMOKECOLOR.Orange )
MENU_GROUP_COMMAND:New( AttackGroup, "Illuminate", DetectedMenu, self.MenuIlluminate, self, Index )
else
if self.Designating[Index] == "Laser" then
Report = "Lasing " .. Report
elseif self.Designating[Index] == "Smoke" then
Report = "Smoking " .. Report
elseif self.Designating[Index] == "Illuminate" then
Report = "Illuminating " .. Report
end
local DetectedMenu = MENU_GROUP:New( AttackGroup, Report, DesignateMenu )
if self.Designating[Index] == "Laser" then
MENU_GROUP_COMMAND:New( AttackGroup, "Stop lasing", DetectedMenu, self.MenuLaseOff, self, Index )
else
end
end
end
end
)
return self
end
---
-- @param #DESIGNATE self
function DESIGNATE:MenuStatus( AttackGroup, Duration )
self:E("Status")
self:SendStatus( AttackGroup, Duration )
end
---
-- @param #DESIGNATE self
function DESIGNATE:MenuFlashStatus( AttackGroup, Flash )
self:E("Flash Status")
self.FlashStatusMenu[AttackGroup] = Flash
self:SetDesignateMenu()
end
---
-- @param #DESIGNATE self
function DESIGNATE:MenuAutoLase( AutoLase )
self:E("AutoLase")
self:SetAutoLase( AutoLase )
end
---
-- @param #DESIGNATE self
function DESIGNATE:MenuSmoke( Index, Color )
self:E("Designate through Smoke")
self.Designating[Index] = "Smoke"
self:__Smoke( 1, Index, Color )
end
---
-- @param #DESIGNATE self
function DESIGNATE:MenuIlluminate( Index )
self:E("Designate through Illumination")
self.Designating[Index] = "Illuminate"
self:__Illuminate( 1, Index )
end
---
-- @param #DESIGNATE self
function DESIGNATE:MenuLaseOn( Index, Duration )
self:E("Designate through Lase")
self:__LaseOn( 1, Index, Duration )
end
---
-- @param #DESIGNATE self
function DESIGNATE:MenuLaseOff( Index, Duration )
self:E("Lasing off")
self.Designating[Index] = nil
self:__LaseOff( 1, Index )
end
---
-- @param #DESIGNATE self
function DESIGNATE:onafterLaseOn( From, Event, To, Index, Duration )
self.Designating[Index] = "Laser"
self:Lasing( Index, Duration )
end
---
-- @param #DESIGNATE self
-- @return #DESIGNATE
function DESIGNATE:onafterLasing( From, Event, To, Index, Duration )
local TargetSetUnit = self.Detection:GetDetectedSet( Index )
TargetSetUnit:Flush()
for TargetUnit, RecceData in pairs( self.Recces ) do
local Recce = RecceData -- Wrapper.Unit#UNIT
if not Recce:IsLasing() then
local LaserCode = Recce:GetLaserCode() --(Not deleted when stopping with lasing).
self.LaserCodesUsed[LaserCode] = nil
self.Recces[TargetUnit] = nil
end
end
TargetSetUnit:ForEachUnitPerThreatLevel( 10, 0,
--- @param Wrapper.Unit#UNIT SmokeUnit
function( TargetUnit )
self:E("In procedure")
if TargetUnit:IsAlive() then
local Recce = self.Recces[TargetUnit]
if not Recce then
for RecceGroupID, RecceGroup in pairs( self.RecceSet:GetSet() ) do
for UnitID, UnitData in pairs( RecceGroup:GetUnits() or {} ) do
local RecceUnit = UnitData -- Wrapper.Unit#UNIT
if RecceUnit:IsLasing() == false then
if RecceUnit:IsDetected( TargetUnit ) and RecceUnit:IsLOS( TargetUnit ) then
local LaserCodeIndex = math.random( 1, #self.LaserCodes )
local LaserCode = self.LaserCodes[LaserCodeIndex]
if not self.LaserCodesUsed[LaserCode] then
self.LaserCodesUsed[LaserCode] = LaserCodeIndex
local Spot = RecceUnit:LaseUnit( TargetUnit, LaserCode, Duration )
local AttackSet = self.AttackSet
function Spot:OnAfterDestroyed( From, Event, To )
self:E( "Destroyed Message" )
self.Recce:MessageToSetGroup( "Target " .. TargetUnit:GetTypeName() .. " destroyed. " .. TargetSetUnit:Count() .. " targets left.", 5, AttackSet )
end
self.Recces[TargetUnit] = RecceUnit
RecceUnit:MessageToSetGroup( "Marking " .. TargetUnit:GetTypeName() .. " with laser " .. RecceUnit:GetSpot().LaserCode .. " for " .. Duration .. "s.", 5, self.AttackSet )
break
end
else
RecceUnit:MessageToSetGroup( "Can't mark " .. TargetUnit:GetTypeName(), 5, self.AttackSet )
end
else
-- The Recce is lasing, but the Target is not detected or within LOS. So stop lasing and send a report.
if not RecceUnit:IsDetected( TargetUnit ) or not RecceUnit:IsLOS( TargetUnit ) then
local Recce = self.Recces[TargetUnit] -- Wrapper.Unit#UNIT
if Recce then
Recce:LaseOff()
Recce:MessageToGroup( "Target " .. TargetUnit:GetTypeName() "out of LOS. Cancelling lase!", 5, self.AttackSet )
end
end
end
end
end
else
Recce:MessageToSetGroup( "Marking " .. TargetUnit:GetTypeName() .. " with laser " .. Recce.LaserCode .. ".", 5, self.AttackSet )
end
end
end
)
self:__Lasing( 15, Index, Duration )
self:SetDesignateMenu()
end
---
-- @param #DESIGNATE self
-- @return #DESIGNATE
function DESIGNATE:onafterLaseOff( From, Event, To, Index )
local CC = self.CC:GetPositionable()
if CC then
CC:MessageToSetGroup( "Stopped lasing.", 5, self.AttackSet )
end
local TargetSetUnit = self.Detection:GetDetectedSet( Index )
local Recces = self.Recces
for TargetID, RecceData in pairs( Recces ) do
local Recce = RecceData -- Wrapper.Unit#UNIT
Recce:MessageToSetGroup( "Stopped lasing " .. Recce:GetSpot().Target:GetTypeName() .. ".", 5, self.AttackSet )
Recce:LaseOff()
end
Recces = nil
self.Recces = {}
self.LaserCodesUsed = {}
self:SetDesignateMenu()
end
---
-- @param #DESIGNATE self
-- @return #DESIGNATE
function DESIGNATE:onafterSmoke( From, Event, To, Index, Color )
local TargetSetUnit = self.Detection:GetDetectedSet( Index )
local TargetSetUnitCount = TargetSetUnit:Count()
TargetSetUnit:ForEachUnit(
--- @param Wrapper.Unit#UNIT SmokeUnit
function( SmokeUnit )
self:E("In procedure")
if math.random( 1, TargetSetUnitCount ) == math.random( 1, TargetSetUnitCount ) then
local RecceGroup = self.RecceSet:FindNearestGroupFromPointVec2(SmokeUnit:GetPointVec2())
local RecceUnit = RecceGroup:GetUnit( 1 )
if RecceUnit then
RecceUnit:MessageToSetGroup( "Smoking " .. SmokeUnit:GetTypeName() .. ".", 5, self.AttackSet )
SCHEDULER:New( self,
function()
if SmokeUnit:IsAlive() then
SmokeUnit:Smoke( Color, 150 )
end
self:Done( Index )
end, {}, math.random( 5, 20 )
)
end
end
end
)
end
--- Illuminating
-- @param #DESIGNATE self
-- @return #DESIGNATE
function DESIGNATE:onafterIlluminate( From, Event, To, Index )
local TargetSetUnit = self.Detection:GetDetectedSet( Index )
local TargetUnit = TargetSetUnit:GetFirst()
if TargetUnit then
local RecceGroup = self.RecceSet:FindNearestGroupFromPointVec2(TargetUnit:GetPointVec2())
local RecceUnit = RecceGroup:GetUnit( 1 )
if RecceUnit then
RecceUnit:MessageToSetGroup( "Illuminating " .. TargetUnit:GetTypeName() .. ".", 5, self.AttackSet )
SCHEDULER:New( self,
function()
if TargetUnit:IsAlive() then
TargetUnit:GetPointVec3():AddY(300):IlluminationBomb()
end
self:Done( Index )
end, {}, math.random( 5, 20 )
)
end
end
end
--- Done
-- @param #DESIGNATE self
-- @return #DESIGNATE
function DESIGNATE:onafterDone( From, Event, To, Index )
self.Designating[Index] = nil
self:SetDesignateMenu()
end
end
-- Help from Ciribob

File diff suppressed because it is too large Load Diff

View File

@@ -84,7 +84,7 @@
--
-- ESCORT initialization methods.
-- ==============================
-- The following menus are created within the RADIO MENU of an active unit hosted by a player:
-- The following menus are created within the RADIO MENU (F10) of an active unit hosted by a player:
--
-- * @{#ESCORT.MenuFollowAt}: Creates a menu to make the escort follow the client.
-- * @{#ESCORT.MenuHoldAtEscortPosition}: Creates a menu to hold the escort at its current position.
@@ -128,6 +128,7 @@
-- @Field Dcs.DCSTypes#AI.Option.Air.val.ROE OptionROE Which ROE is set to the EscortGroup.
-- @field Dcs.DCSTypes#AI.Option.Air.val.REACTION_ON_THREAT OptionReactionOnThreat Which REACTION_ON_THREAT is set to the EscortGroup.
-- @field Core.Menu#MENU_CLIENT EscortMenuResumeMission
-- @field Functional.Detection#DETECTION_BASE Detection
ESCORT = {
ClassName = "ESCORT",
EscortName = nil, -- The Escort Name
@@ -176,14 +177,22 @@ ESCORT = {
-- -- Now use these 2 objects to construct the new EscortPlanes object.
-- EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the mission. You are escorted by a plane with code name 'Desert', which can be instructed through the F10 radio menu." )
function ESCORT:New( EscortClient, EscortGroup, EscortName, EscortBriefing )
local self = BASE:Inherit( self, BASE:New() )
local self = BASE:Inherit( self, BASE:New() ) -- #ESCORT
self:F( { EscortClient, EscortGroup, EscortName } )
self.EscortClient = EscortClient -- Wrapper.Client#CLIENT
self.EscortGroup = EscortGroup -- Wrapper.Group#GROUP
self.EscortName = EscortName
self.EscortBriefing = EscortBriefing
self.EscortSetGroup = SET_GROUP:New()
self.EscortSetGroup:AddObject( self.EscortGroup )
self.EscortSetGroup:Flush()
self.Detection = DETECTION_UNITS:New( self.EscortSetGroup, 15000 )
self.EscortGroup.Detection = self.Detection
-- Set EscortGroup known at EscortClient.
if not self.EscortClient._EscortGroups then
self.EscortClient._EscortGroups = {}
@@ -193,7 +202,7 @@ function ESCORT:New( EscortClient, EscortGroup, EscortName, EscortBriefing )
self.EscortClient._EscortGroups[EscortGroup:GetName()] = {}
self.EscortClient._EscortGroups[EscortGroup:GetName()].EscortGroup = self.EscortGroup
self.EscortClient._EscortGroups[EscortGroup:GetName()].EscortName = self.EscortName
self.EscortClient._EscortGroups[EscortGroup:GetName()].Targets = {}
self.EscortClient._EscortGroups[EscortGroup:GetName()].Detection = self.EscortGroup.Detection
end
self.EscortMenu = MENU_CLIENT:New( self.EscortClient, self.EscortName )
@@ -218,13 +227,30 @@ function ESCORT:New( EscortClient, EscortGroup, EscortName, EscortBriefing )
self.FollowDistance = 100
self.CT1 = 0
self.GT1 = 0
self.FollowScheduler = SCHEDULER:New( self, self._FollowScheduler, {}, 1, .5, .01 )
self.EscortMode = ESCORT.MODE.MISSION
self.FollowScheduler:Stop()
self.FollowScheduler, self.FollowSchedule = SCHEDULER:New( self, self._FollowScheduler, {}, 1, .5, .01 )
self.FollowScheduler:Stop( self.FollowSchedule )
self.EscortMode = ESCORT.MODE.MISSION
return self
end
--- Set a Detection method for the EscortClient to be reported upon.
-- Detection methods are based on the derived classes from DETECTION_BASE.
-- @param #ESCORT self
-- @param Function.Detection#DETECTION_BASE Detection
function ESCORT:SetDetection( Detection )
self.Detection = Detection
self.EscortGroup.Detection = self.Detection
self.EscortClient._EscortGroups[self.EscortGroup:GetName()].Detection = self.EscortGroup.Detection
Detection:__Start( 1 )
end
--- This function is for test, it will put on the frequency of the FollowScheduler a red smoke at the direction vector calculated for the escort to fly to.
-- This allows to visualize where the escort is flying to.
-- @param #ESCORT self
@@ -282,7 +308,7 @@ function ESCORT:MenuFollowAt( Distance )
self.EscortMenuJoinUpAndFollow = {}
end
self.EscortMenuJoinUpAndFollow[#self.EscortMenuJoinUpAndFollow+1] = MENU_CLIENT_COMMAND:New( self.EscortClient, "Join-Up and Follow at " .. Distance, self.EscortMenuReportNavigation, ESCORT._JoinUpAndFollow, { ParamSelf = self, ParamDistance = Distance } )
self.EscortMenuJoinUpAndFollow[#self.EscortMenuJoinUpAndFollow+1] = MENU_CLIENT_COMMAND:New( self.EscortClient, "Join-Up and Follow at " .. Distance, self.EscortMenuReportNavigation, ESCORT._JoinUpAndFollow, self, Distance )
self.EscortMode = ESCORT.MODE.FOLLOW
end
@@ -340,11 +366,10 @@ function ESCORT:MenuHoldAtEscortPosition( Height, Seconds, MenuTextFormat )
MenuText,
self.EscortMenuHold,
ESCORT._HoldPosition,
{ ParamSelf = self,
ParamOrbitGroup = self.EscortGroup,
ParamHeight = Height,
ParamSeconds = Seconds
}
self,
self.EscortGroup,
Height,
Seconds
)
end
@@ -461,9 +486,8 @@ function ESCORT:MenuScanForTargets( Height, Seconds, MenuTextFormat )
MenuText,
self.EscortMenuScan,
ESCORT._ScanTargets,
{ ParamSelf = self,
ParamScanDuration = 30
}
self,
30
)
end
@@ -493,11 +517,11 @@ function ESCORT:MenuFlare( MenuTextFormat )
end
if not self.EscortMenuFlare then
self.EscortMenuFlare = MENU_CLIENT:New( self.EscortClient, MenuText, self.EscortMenuReportNavigation, ESCORT._Flare, { ParamSelf = self } )
self.EscortMenuFlareGreen = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release green flare", self.EscortMenuFlare, ESCORT._Flare, { ParamSelf = self, ParamColor = FLARECOLOR.Green, ParamMessage = "Released a green flare!" } )
self.EscortMenuFlareRed = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release red flare", self.EscortMenuFlare, ESCORT._Flare, { ParamSelf = self, ParamColor = FLARECOLOR.Red, ParamMessage = "Released a red flare!" } )
self.EscortMenuFlareWhite = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release white flare", self.EscortMenuFlare, ESCORT._Flare, { ParamSelf = self, ParamColor = FLARECOLOR.White, ParamMessage = "Released a white flare!" } )
self.EscortMenuFlareYellow = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release yellow flare", self.EscortMenuFlare, ESCORT._Flare, { ParamSelf = self, ParamColor = FLARECOLOR.Yellow, ParamMessage = "Released a yellow flare!" } )
self.EscortMenuFlare = MENU_CLIENT:New( self.EscortClient, MenuText, self.EscortMenuReportNavigation, ESCORT._Flare, self )
self.EscortMenuFlareGreen = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release green flare", self.EscortMenuFlare, ESCORT._Flare, self, FLARECOLOR.Green, "Released a green flare!" )
self.EscortMenuFlareRed = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release red flare", self.EscortMenuFlare, ESCORT._Flare, self, FLARECOLOR.Red, "Released a red flare!" )
self.EscortMenuFlareWhite = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release white flare", self.EscortMenuFlare, ESCORT._Flare, self, FLARECOLOR.White, "Released a white flare!" )
self.EscortMenuFlareYellow = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release yellow flare", self.EscortMenuFlare, ESCORT._Flare, self, FLARECOLOR.Yellow, "Released a yellow flare!" )
end
return self
@@ -526,12 +550,12 @@ function ESCORT:MenuSmoke( MenuTextFormat )
end
if not self.EscortMenuSmoke then
self.EscortMenuSmoke = MENU_CLIENT:New( self.EscortClient, "Smoke", self.EscortMenuReportNavigation, ESCORT._Smoke, { ParamSelf = self } )
self.EscortMenuSmokeGreen = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release green smoke", self.EscortMenuSmoke, ESCORT._Smoke, { ParamSelf = self, ParamColor = UNIT.SmokeColor.Green, ParamMessage = "Releasing green smoke!" } )
self.EscortMenuSmokeRed = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release red smoke", self.EscortMenuSmoke, ESCORT._Smoke, { ParamSelf = self, ParamColor = UNIT.SmokeColor.Red, ParamMessage = "Releasing red smoke!" } )
self.EscortMenuSmokeWhite = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release white smoke", self.EscortMenuSmoke, ESCORT._Smoke, { ParamSelf = self, ParamColor = UNIT.SmokeColor.White, ParamMessage = "Releasing white smoke!" } )
self.EscortMenuSmokeOrange = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release orange smoke", self.EscortMenuSmoke, ESCORT._Smoke, { ParamSelf = self, ParamColor = UNIT.SmokeColor.Orange, ParamMessage = "Releasing orange smoke!" } )
self.EscortMenuSmokeBlue = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release blue smoke", self.EscortMenuSmoke, ESCORT._Smoke, { ParamSelf = self, ParamColor = UNIT.SmokeColor.Blue, ParamMessage = "Releasing blue smoke!" } )
self.EscortMenuSmoke = MENU_CLIENT:New( self.EscortClient, "Smoke", self.EscortMenuReportNavigation, ESCORT._Smoke, self )
self.EscortMenuSmokeGreen = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release green smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.Green, "Releasing green smoke!" )
self.EscortMenuSmokeRed = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release red smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.Red, "Releasing red smoke!" )
self.EscortMenuSmokeWhite = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release white smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.White, "Releasing white smoke!" )
self.EscortMenuSmokeOrange = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release orange smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.Orange, "Releasing orange smoke!" )
self.EscortMenuSmokeBlue = MENU_CLIENT_COMMAND:New( self.EscortClient, "Release blue smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.Blue, "Releasing blue smoke!" )
end
end
@@ -556,9 +580,9 @@ function ESCORT:MenuReportTargets( Seconds )
end
-- Report Targets
self.EscortMenuReportNearbyTargetsNow = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets now!", self.EscortMenuReportNearbyTargets, ESCORT._ReportNearbyTargetsNow, { ParamSelf = self } )
self.EscortMenuReportNearbyTargetsOn = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets on", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, { ParamSelf = self, ParamReportTargets = true } )
self.EscortMenuReportNearbyTargetsOff = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets off", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, { ParamSelf = self, ParamReportTargets = false, } )
self.EscortMenuReportNearbyTargetsNow = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets now!", self.EscortMenuReportNearbyTargets, ESCORT._ReportNearbyTargetsNow, self )
self.EscortMenuReportNearbyTargetsOn = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets on", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, self, true )
self.EscortMenuReportNearbyTargetsOff = MENU_CLIENT_COMMAND:New( self.EscortClient, "Report targets off", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, self, false )
-- Attack Targets
self.EscortMenuAttackNearbyTargets = MENU_CLIENT:New( self.EscortClient, "Attack targets", self.EscortMenu )
@@ -595,16 +619,16 @@ function ESCORT:MenuROE( MenuTextFormat )
-- Rules of Engagement
self.EscortMenuROE = MENU_CLIENT:New( self.EscortClient, "ROE", self.EscortMenu )
if self.EscortGroup:OptionROEHoldFirePossible() then
self.EscortMenuROEHoldFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Hold Fire", self.EscortMenuROE, ESCORT._ROE, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROEHoldFire(), ParamMessage = "Holding weapons!" } )
self.EscortMenuROEHoldFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Hold Fire", self.EscortMenuROE, ESCORT._ROE, self, self.EscortGroup:OptionROEHoldFire(), "Holding weapons!" )
end
if self.EscortGroup:OptionROEReturnFirePossible() then
self.EscortMenuROEReturnFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Return Fire", self.EscortMenuROE, ESCORT._ROE, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROEReturnFire(), ParamMessage = "Returning fire!" } )
self.EscortMenuROEReturnFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Return Fire", self.EscortMenuROE, ESCORT._ROE, self, self.EscortGroup:OptionROEReturnFire(), "Returning fire!" )
end
if self.EscortGroup:OptionROEOpenFirePossible() then
self.EscortMenuROEOpenFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Open Fire", self.EscortMenuROE, ESCORT._ROE, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROEOpenFire(), ParamMessage = "Opening fire on designated targets!!" } )
self.EscortMenuROEOpenFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Open Fire", self.EscortMenuROE, ESCORT._ROE, self, self.EscortGroup:OptionROEOpenFire(), "Opening fire on designated targets!!" )
end
if self.EscortGroup:OptionROEWeaponFreePossible() then
self.EscortMenuROEWeaponFree = MENU_CLIENT_COMMAND:New( self.EscortClient, "Weapon Free", self.EscortMenuROE, ESCORT._ROE, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROEWeaponFree(), ParamMessage = "Opening fire on targets of opportunity!" } )
self.EscortMenuROEWeaponFree = MENU_CLIENT_COMMAND:New( self.EscortClient, "Weapon Free", self.EscortMenuROE, ESCORT._ROE, self, self.EscortGroup:OptionROEWeaponFree(), "Opening fire on targets of opportunity!" )
end
end
@@ -624,16 +648,16 @@ function ESCORT:MenuEvasion( MenuTextFormat )
-- Reaction to Threats
self.EscortMenuEvasion = MENU_CLIENT:New( self.EscortClient, "Evasion", self.EscortMenu )
if self.EscortGroup:OptionROTNoReactionPossible() then
self.EscortMenuEvasionNoReaction = MENU_CLIENT_COMMAND:New( self.EscortClient, "Fight until death", self.EscortMenuEvasion, ESCORT._ROT, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROTNoReaction(), ParamMessage = "Fighting until death!" } )
self.EscortMenuEvasionNoReaction = MENU_CLIENT_COMMAND:New( self.EscortClient, "Fight until death", self.EscortMenuEvasion, ESCORT._ROT, self, self.EscortGroup:OptionROTNoReaction(), "Fighting until death!" )
end
if self.EscortGroup:OptionROTPassiveDefensePossible() then
self.EscortMenuEvasionPassiveDefense = MENU_CLIENT_COMMAND:New( self.EscortClient, "Use flares, chaff and jammers", self.EscortMenuEvasion, ESCORT._ROT, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROTPassiveDefense(), ParamMessage = "Defending using jammers, chaff and flares!" } )
self.EscortMenuEvasionPassiveDefense = MENU_CLIENT_COMMAND:New( self.EscortClient, "Use flares, chaff and jammers", self.EscortMenuEvasion, ESCORT._ROT, self, self.EscortGroup:OptionROTPassiveDefense(), "Defending using jammers, chaff and flares!" )
end
if self.EscortGroup:OptionROTEvadeFirePossible() then
self.EscortMenuEvasionEvadeFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Evade enemy fire", self.EscortMenuEvasion, ESCORT._ROT, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROTEvadeFire(), ParamMessage = "Evading on enemy fire!" } )
self.EscortMenuEvasionEvadeFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Evade enemy fire", self.EscortMenuEvasion, ESCORT._ROT, self, self.EscortGroup:OptionROTEvadeFire(), "Evading on enemy fire!" )
end
if self.EscortGroup:OptionROTVerticalPossible() then
self.EscortMenuOptionEvasionVertical = MENU_CLIENT_COMMAND:New( self.EscortClient, "Go below radar and evade fire", self.EscortMenuEvasion, ESCORT._ROT, { ParamSelf = self, ParamFunction = self.EscortGroup:OptionROTVertical(), ParamMessage = "Evading on enemy fire with vertical manoeuvres!" } )
self.EscortMenuOptionEvasionVertical = MENU_CLIENT_COMMAND:New( self.EscortClient, "Go below radar and evade fire", self.EscortMenuEvasion, ESCORT._ROT, self, self.EscortGroup:OptionROTVertical(), "Evading on enemy fire with vertical manoeuvres!" )
end
end
end
@@ -658,18 +682,14 @@ end
--- @param #MENUPARAM MenuParam
function ESCORT._HoldPosition( MenuParam )
function ESCORT:_HoldPosition( OrbitGroup, OrbitHeight, OrbitSeconds )
local self = MenuParam.ParamSelf
local EscortGroup = self.EscortGroup
local EscortClient = self.EscortClient
local OrbitGroup = MenuParam.ParamOrbitGroup -- Wrapper.Group#GROUP
local OrbitUnit = OrbitGroup:GetUnit(1) -- Wrapper.Unit#UNIT
local OrbitHeight = MenuParam.ParamHeight
local OrbitSeconds = MenuParam.ParamSeconds -- Not implemented yet
self.FollowScheduler:Stop()
self.FollowScheduler:Stop( self.FollowSchedule )
local PointFrom = {}
local GroupVec3 = EscortGroup:GetUnit(1):GetVec3()
@@ -702,13 +722,12 @@ function ESCORT._HoldPosition( MenuParam )
end
--- @param #MENUPARAM MenuParam
function ESCORT._JoinUpAndFollow( MenuParam )
function ESCORT:_JoinUpAndFollow( Distance )
local self = MenuParam.ParamSelf
local EscortGroup = self.EscortGroup
local EscortClient = self.EscortClient
self.Distance = MenuParam.ParamDistance
self.Distance = Distance
self:JoinUpAndFollow( EscortGroup, EscortClient, self.Distance )
end
@@ -721,7 +740,7 @@ end
function ESCORT:JoinUpAndFollow( EscortGroup, EscortClient, Distance )
self:F( { EscortGroup, EscortClient, Distance } )
self.FollowScheduler:Stop()
self.FollowScheduler:Stop( self.FollowSchedule )
EscortGroup:OptionROEHoldFire()
EscortGroup:OptionROTPassiveDefense()
@@ -730,44 +749,35 @@ function ESCORT:JoinUpAndFollow( EscortGroup, EscortClient, Distance )
self.CT1 = 0
self.GT1 = 0
self.FollowScheduler:Start()
self.FollowScheduler:Start( self.FollowSchedule )
EscortGroup:MessageToClient( "Rejoining and Following at " .. Distance .. "!", 30, EscortClient )
end
--- @param #MENUPARAM MenuParam
function ESCORT._Flare( MenuParam )
function ESCORT:_Flare( Color, Message )
local self = MenuParam.ParamSelf
local EscortGroup = self.EscortGroup
local EscortClient = self.EscortClient
local Color = MenuParam.ParamColor
local Message = MenuParam.ParamMessage
EscortGroup:GetUnit(1):Flare( Color )
EscortGroup:MessageToClient( Message, 10, EscortClient )
end
--- @param #MENUPARAM MenuParam
function ESCORT._Smoke( MenuParam )
function ESCORT:_Smoke( Color, Message )
local self = MenuParam.ParamSelf
local EscortGroup = self.EscortGroup
local EscortClient = self.EscortClient
local Color = MenuParam.ParamColor
local Message = MenuParam.ParamMessage
EscortGroup:GetUnit(1):Smoke( Color )
EscortGroup:MessageToClient( Message, 10, EscortClient )
end
--- @param #MENUPARAM MenuParam
function ESCORT._ReportNearbyTargetsNow( MenuParam )
function ESCORT:_ReportNearbyTargetsNow()
local self = MenuParam.ParamSelf
local EscortGroup = self.EscortGroup
local EscortClient = self.EscortClient
@@ -775,17 +785,16 @@ function ESCORT._ReportNearbyTargetsNow( MenuParam )
end
function ESCORT._SwitchReportNearbyTargets( MenuParam )
function ESCORT:_SwitchReportNearbyTargets( ReportTargets )
local self = MenuParam.ParamSelf
local EscortGroup = self.EscortGroup
local EscortClient = self.EscortClient
self.ReportTargets = MenuParam.ParamReportTargets
self.ReportTargets = ReportTargets
if self.ReportTargets then
if not self.ReportTargetsScheduler then
self.ReportTargetsScheduler = SCHEDULER:New( self, self._ReportTargetsScheduler, {}, 1, 30 )
self.ReportTargetsScheduler:Schedule( self, self._ReportTargetsScheduler, {}, 1, 30 )
end
else
routines.removeFunction( self.ReportTargetsScheduler )
@@ -794,40 +803,31 @@ function ESCORT._SwitchReportNearbyTargets( MenuParam )
end
--- @param #MENUPARAM MenuParam
function ESCORT._ScanTargets( MenuParam )
function ESCORT:_ScanTargets( ScanDuration )
local self = MenuParam.ParamSelf
local EscortGroup = self.EscortGroup
local EscortGroup = self.EscortGroup -- Wrapper.Group#GROUP
local EscortClient = self.EscortClient
local ScanDuration = MenuParam.ParamScanDuration
self.FollowScheduler:Stop()
self.FollowScheduler:Stop( self.FollowSchedule )
if EscortGroup:IsHelicopter() then
SCHEDULER:New( EscortGroup, EscortGroup.PushTask,
{ EscortGroup:TaskControlled(
EscortGroup:TaskOrbitCircle( 200, 20 ),
EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil )
)
},
1
)
EscortGroup:PushTask(
EscortGroup:TaskControlled(
EscortGroup:TaskOrbitCircle( 200, 20 ),
EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil )
), 1 )
elseif EscortGroup:IsAirPlane() then
SCHEDULER:New( EscortGroup, EscortGroup.PushTask,
{ EscortGroup:TaskControlled(
EscortGroup:TaskOrbitCircle( 1000, 500 ),
EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil )
)
},
1
)
EscortGroup:PushTask(
EscortGroup:TaskControlled(
EscortGroup:TaskOrbitCircle( 1000, 500 ),
EscortGroup:TaskCondition( nil, nil, nil, nil, ScanDuration, nil )
), 1 )
end
EscortGroup:MessageToClient( "Scanning targets for " .. ScanDuration .. " seconds.", ScanDuration, EscortClient )
if self.EscortMode == ESCORT.MODE.FOLLOW then
self.FollowScheduler:Start()
self.FollowScheduler:Start( self.FollowSchedule )
end
end
@@ -844,124 +844,157 @@ function _Resume( EscortGroup )
end
--- @param #MENUPARAM MenuParam
function ESCORT._AttackTarget( MenuParam )
--- @param #ESCORT self
-- @param #number DetectedItemID
function ESCORT:_AttackTarget( DetectedItemID )
local self = MenuParam.ParamSelf
local EscortGroup = self.EscortGroup
local EscortGroup = self.EscortGroup -- Wrapper.Group#GROUP
self:E( EscortGroup )
local EscortClient = self.EscortClient
local AttackUnit = MenuParam.ParamUnit -- Wrapper.Unit#UNIT
self.FollowScheduler:Stop()
self:T( AttackUnit )
self.FollowScheduler:Stop( self.FollowSchedule )
if EscortGroup:IsAir() then
EscortGroup:OptionROEOpenFire()
EscortGroup:OptionROTPassiveDefense()
EscortGroup:SetState( EscortGroup, "Escort", self )
SCHEDULER:New( EscortGroup,
EscortGroup.PushTask,
{ EscortGroup:TaskCombo(
{ EscortGroup:TaskAttackUnit( AttackUnit ),
EscortGroup:TaskFunction( 1, 2, "_Resume", { "''" } )
}
)
}, 10
local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID )
local Tasks = {}
DetectedSet:ForEachUnit(
--- @param Wrapper.Unit#UNIT DetectedUnit
function( DetectedUnit, Tasks )
if DetectedUnit:IsAlive() then
Tasks[#Tasks+1] = EscortGroup:TaskAttackUnit( DetectedUnit )
end
end, Tasks
)
Tasks[#Tasks+1] = EscortGroup:TaskFunction( 1, 2, "_Resume", { "''" } )
EscortGroup:SetTask(
EscortGroup:TaskCombo(
Tasks
), 1
)
else
SCHEDULER:New( EscortGroup,
EscortGroup.PushTask,
{ EscortGroup:TaskCombo(
{ EscortGroup:TaskFireAtPoint( AttackUnit:GetVec2(), 50 )
}
)
}, 10
local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID )
local Tasks = {}
DetectedSet:ForEachUnit(
--- @param Wrapper.Unit#UNIT DetectedUnit
function( DetectedUnit, Tasks )
if DetectedUnit:IsAlive() then
Tasks[#Tasks+1] = EscortGroup:TaskFireAtPoint( DetectedUnit:GetVec2(), 50 )
end
end, Tasks
)
EscortGroup:SetTask(
EscortGroup:TaskCombo(
Tasks
), 1
)
end
EscortGroup:MessageToClient( "Engaging Designated Unit!", 10, EscortClient )
end
--- @param #MENUPARAM MenuParam
function ESCORT._AssistTarget( MenuParam )
---
-- @param #number DetectedItemID
function ESCORT:_AssistTarget( EscortGroupAttack, DetectedItemID )
local self = MenuParam.ParamSelf
local EscortGroup = self.EscortGroup
local EscortClient = self.EscortClient
local EscortGroupAttack = MenuParam.ParamEscortGroup
local AttackUnit = MenuParam.ParamUnit -- Wrapper.Unit#UNIT
self.FollowScheduler:Stop()
self:T( AttackUnit )
self.FollowScheduler:Stop( self.FollowSchedule )
if EscortGroupAttack:IsAir() then
EscortGroupAttack:OptionROEOpenFire()
EscortGroupAttack:OptionROTVertical()
SCHDULER:New( EscortGroupAttack,
EscortGroupAttack.PushTask,
{ EscortGroupAttack:TaskCombo(
{ EscortGroupAttack:TaskAttackUnit( AttackUnit ),
EscortGroupAttack:TaskOrbitCircle( 500, 350 )
}
)
}, 10
local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID )
local Tasks = {}
DetectedSet:ForEachUnit(
--- @param Wrapper.Unit#UNIT DetectedUnit
function( DetectedUnit, Tasks )
if DetectedUnit:IsAlive() then
Tasks[#Tasks+1] = EscortGroupAttack:TaskAttackUnit( DetectedUnit )
end
end, Tasks
)
Tasks[#Tasks+1] = EscortGroupAttack:TaskOrbitCircle( 500, 350 )
EscortGroupAttack:SetTask(
EscortGroupAttack:TaskCombo(
Tasks
), 1
)
else
SCHEDULER:New( EscortGroupAttack,
EscortGroupAttack.PushTask,
{ EscortGroupAttack:TaskCombo(
{ EscortGroupAttack:TaskFireAtPoint( AttackUnit:GetVec2(), 50 )
}
)
}, 10
local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID )
local Tasks = {}
DetectedSet:ForEachUnit(
--- @param Wrapper.Unit#UNIT DetectedUnit
function( DetectedUnit, Tasks )
if DetectedUnit:IsAlive() then
Tasks[#Tasks+1] = EscortGroupAttack:TaskFireAtPoint( DetectedUnit:GetVec2(), 50 )
end
end, Tasks
)
EscortGroupAttack:SetTask(
EscortGroupAttack:TaskCombo(
Tasks
), 1
)
end
EscortGroupAttack:MessageToClient( "Assisting with the destroying the enemy unit!", 10, EscortClient )
end
--- @param #MENUPARAM MenuParam
function ESCORT._ROE( MenuParam )
function ESCORT:_ROE( EscortROEFunction, EscortROEMessage )
local self = MenuParam.ParamSelf
local EscortGroup = self.EscortGroup
local EscortClient = self.EscortClient
local EscortROEFunction = MenuParam.ParamFunction
local EscortROEMessage = MenuParam.ParamMessage
pcall( function() EscortROEFunction() end )
EscortGroup:MessageToClient( EscortROEMessage, 10, EscortClient )
end
--- @param #MENUPARAM MenuParam
function ESCORT._ROT( MenuParam )
function ESCORT:_ROT( EscortROTFunction, EscortROTMessage )
local self = MenuParam.ParamSelf
local EscortGroup = self.EscortGroup
local EscortClient = self.EscortClient
local EscortROTFunction = MenuParam.ParamFunction
local EscortROTMessage = MenuParam.ParamMessage
pcall( function() EscortROTFunction() end )
EscortGroup:MessageToClient( EscortROTMessage, 10, EscortClient )
end
--- @param #MENUPARAM MenuParam
function ESCORT._ResumeMission( MenuParam )
function ESCORT:_ResumeMission( WayPoint )
local self = MenuParam.ParamSelf
local EscortGroup = self.EscortGroup
local EscortClient = self.EscortClient
local WayPoint = MenuParam.ParamWayPoint
self.FollowScheduler:Stop()
self.FollowScheduler:Stop( self.FollowSchedule )
local WayPoints = EscortGroup:GetTaskRoute()
self:T( WayPoint, WayPoints )
@@ -1089,7 +1122,7 @@ function ESCORT:_FollowScheduler()
self:T( { "Client Speed, Escort Speed, Speed, FollowDistance, Time:", CS, GS, Speed, FollowDistance, Time } )
-- Now route the escort to the desired point with the desired speed.
self.EscortGroup:TaskRouteToVec3( GDV, Speed / 3.6 ) -- DCS models speed in Mps (Miles per second)
self.EscortGroup:RouteToVec3( GDV, Speed / 3.6 ) -- DCS models speed in Mps (Miles per second)
end
return true
@@ -1105,176 +1138,244 @@ function ESCORT:_ReportTargetsScheduler()
self:F( self.EscortGroup:GetName() )
if self.EscortGroup:IsAlive() and self.EscortClient:IsAlive() then
local EscortGroupName = self.EscortGroup:GetName()
local EscortTargets = self.EscortGroup:GetDetectedTargets()
local ClientEscortTargets = self.EscortClient._EscortGroups[EscortGroupName].Targets
if true then
local EscortTargetMessages = ""
for EscortTargetID, EscortTarget in pairs( EscortTargets ) do
local EscortObject = EscortTarget.object
self:T( EscortObject )
if EscortObject and EscortObject:isExist() and EscortObject.id_ < 50000000 then
local EscortGroupName = self.EscortGroup:GetName()
self.EscortMenuAttackNearbyTargets:RemoveSubMenus()
local EscortTargetUnit = UNIT:Find( EscortObject )
local EscortTargetUnitName = EscortTargetUnit:GetName()
-- local EscortTargetIsDetected,
-- EscortTargetIsVisible,
-- EscortTargetLastTime,
-- EscortTargetKnowType,
-- EscortTargetKnowDistance,
-- EscortTargetLastPos,
-- EscortTargetLastVelocity
-- = self.EscortGroup:IsTargetDetected( EscortObject )
--
-- self:T( { EscortTargetIsDetected,
-- EscortTargetIsVisible,
-- EscortTargetLastTime,
-- EscortTargetKnowType,
-- EscortTargetKnowDistance,
-- EscortTargetLastPos,
-- EscortTargetLastVelocity } )
local EscortTargetUnitVec3 = EscortTargetUnit:GetVec3()
local EscortVec3 = self.EscortGroup:GetVec3()
local Distance = ( ( EscortTargetUnitVec3.x - EscortVec3.x )^2 +
( EscortTargetUnitVec3.y - EscortVec3.y )^2 +
( EscortTargetUnitVec3.z - EscortVec3.z )^2
) ^ 0.5 / 1000
self:T( { self.EscortGroup:GetName(), EscortTargetUnit:GetName(), Distance, EscortTarget } )
if Distance <= 15 then
if not ClientEscortTargets[EscortTargetUnitName] then
ClientEscortTargets[EscortTargetUnitName] = {}
end
ClientEscortTargets[EscortTargetUnitName].AttackUnit = EscortTargetUnit
ClientEscortTargets[EscortTargetUnitName].visible = EscortTarget.visible
ClientEscortTargets[EscortTargetUnitName].type = EscortTarget.type
ClientEscortTargets[EscortTargetUnitName].distance = EscortTarget.distance
else
if ClientEscortTargets[EscortTargetUnitName] then
ClientEscortTargets[EscortTargetUnitName] = nil
end
end
if self.EscortMenuTargetAssistance then
self.EscortMenuTargetAssistance:RemoveSubMenus()
end
end
self:T( { "Sorting Targets Table:", ClientEscortTargets } )
table.sort( ClientEscortTargets, function( a, b ) return a.Distance < b.Distance end )
self:T( { "Sorted Targets Table:", ClientEscortTargets } )
local DetectedItems = self.Detection:GetDetectedItems()
self:E( DetectedItems )
-- Remove the sub menus of the Attack menu of the Escort for the EscortGroup.
self.EscortMenuAttackNearbyTargets:RemoveSubMenus()
local DetectedTargets = false
local DetectedMsgs = {}
for ClientEscortGroupName, EscortGroupData in pairs( self.EscortClient._EscortGroups ) do
if self.EscortMenuTargetAssistance then
self.EscortMenuTargetAssistance:RemoveSubMenus()
end
local ClientEscortTargets = EscortGroupData.Detection
--for MenuIndex = 1, #self.EscortMenuAttackTargets do
-- self:T( { "Remove Menu:", self.EscortMenuAttackTargets[MenuIndex] } )
-- self.EscortMenuAttackTargets[MenuIndex] = self.EscortMenuAttackTargets[MenuIndex]:Remove()
--end
for DetectedItemID, DetectedItem in ipairs( DetectedItems ) do
self:E( { DetectedItemID, DetectedItem } )
-- Remove the sub menus of the Attack menu of the Escort for the EscortGroup.
local DetectedItemReportSummary = self.Detection:DetectedItemReportSummary( DetectedItemID )
if ClientEscortTargets then
for ClientEscortTargetUnitName, ClientEscortTargetData in pairs( ClientEscortTargets ) do
for ClientEscortGroupName, EscortGroupData in pairs( self.EscortClient._EscortGroups ) do
if ClientEscortTargetData and ClientEscortTargetData.AttackUnit:IsAlive() then
local EscortTargetMessage = ""
local EscortTargetCategoryName = ClientEscortTargetData.AttackUnit:GetCategoryName()
local EscortTargetCategoryType = ClientEscortTargetData.AttackUnit:GetTypeName()
if ClientEscortTargetData.type then
EscortTargetMessage = EscortTargetMessage .. EscortTargetCategoryName .. " (" .. EscortTargetCategoryType .. ") at "
else
EscortTargetMessage = EscortTargetMessage .. "Unknown target at "
end
local EscortTargetUnitVec3 = ClientEscortTargetData.AttackUnit:GetVec3()
local EscortVec3 = self.EscortGroup:GetVec3()
local Distance = ( ( EscortTargetUnitVec3.x - EscortVec3.x )^2 +
( EscortTargetUnitVec3.y - EscortVec3.y )^2 +
( EscortTargetUnitVec3.z - EscortVec3.z )^2
) ^ 0.5 / 1000
self:T( { self.EscortGroup:GetName(), ClientEscortTargetData.AttackUnit:GetName(), Distance, ClientEscortTargetData.AttackUnit } )
if ClientEscortTargetData.visible == false then
EscortTargetMessage = EscortTargetMessage .. string.format( "%.2f", Distance ) .. " estimated km"
else
EscortTargetMessage = EscortTargetMessage .. string.format( "%.2f", Distance ) .. " km"
end
if ClientEscortTargetData.visible then
EscortTargetMessage = EscortTargetMessage .. ", visual"
end
if ClientEscortGroupName == EscortGroupName then
MENU_CLIENT_COMMAND:New( self.EscortClient,
EscortTargetMessage,
self.EscortMenuAttackNearbyTargets,
ESCORT._AttackTarget,
{ ParamSelf = self,
ParamUnit = ClientEscortTargetData.AttackUnit
}
)
EscortTargetMessages = EscortTargetMessages .. "\n - " .. EscortTargetMessage
else
if self.EscortMenuTargetAssistance then
local MenuTargetAssistance = MENU_CLIENT:New( self.EscortClient, EscortGroupData.EscortName, self.EscortMenuTargetAssistance )
MENU_CLIENT_COMMAND:New( self.EscortClient,
EscortTargetMessage,
MenuTargetAssistance,
ESCORT._AssistTarget,
{ ParamSelf = self,
ParamEscortGroup = EscortGroupData.EscortGroup,
ParamUnit = ClientEscortTargetData.AttackUnit
}
)
end
end
if ClientEscortGroupName == EscortGroupName then
DetectedMsgs[#DetectedMsgs+1] = DetectedItemReportSummary
MENU_CLIENT_COMMAND:New( self.EscortClient,
DetectedItemReportSummary,
self.EscortMenuAttackNearbyTargets,
ESCORT._AttackTarget,
self,
DetectedItemID
)
else
ClientEscortTargetData = nil
if self.EscortMenuTargetAssistance then
self:T( DetectedItemReportSummary )
local MenuTargetAssistance = MENU_CLIENT:New( self.EscortClient, EscortGroupData.EscortName, self.EscortMenuTargetAssistance )
MENU_CLIENT_COMMAND:New( self.EscortClient,
DetectedItemReportSummary,
MenuTargetAssistance,
ESCORT._AssistTarget,
self,
EscortGroupData.EscortGroup,
DetectedItemID
)
end
end
DetectedTargets = true
end
end
if EscortTargetMessages ~= "" and self.ReportTargets == true then
self.EscortGroup:MessageToClient( "Detected targets within 15 km range:" .. EscortTargetMessages:gsub("\n$",""), 20, self.EscortClient )
self:E( DetectedMsgs )
if DetectedTargets then
self.EscortGroup:MessageToClient( "Detected targets:\n" .. table.concat( DetectedMsgs, "\n" ), 20, self.EscortClient )
else
self.EscortGroup:MessageToClient( "No targets detected!", 20, self.EscortClient )
self.EscortGroup:MessageToClient( "No targets detected.", 10, self.EscortClient )
end
return true
else
-- local EscortGroupName = self.EscortGroup:GetName()
-- local EscortTargets = self.EscortGroup:GetDetectedTargets()
--
-- local ClientEscortTargets = self.EscortClient._EscortGroups[EscortGroupName].Targets
--
-- local EscortTargetMessages = ""
-- for EscortTargetID, EscortTarget in pairs( EscortTargets ) do
-- local EscortObject = EscortTarget.object
-- self:T( EscortObject )
-- if EscortObject and EscortObject:isExist() and EscortObject.id_ < 50000000 then
--
-- local EscortTargetUnit = UNIT:Find( EscortObject )
-- local EscortTargetUnitName = EscortTargetUnit:GetName()
--
--
--
-- -- local EscortTargetIsDetected,
-- -- EscortTargetIsVisible,
-- -- EscortTargetLastTime,
-- -- EscortTargetKnowType,
-- -- EscortTargetKnowDistance,
-- -- EscortTargetLastPos,
-- -- EscortTargetLastVelocity
-- -- = self.EscortGroup:IsTargetDetected( EscortObject )
-- --
-- -- self:T( { EscortTargetIsDetected,
-- -- EscortTargetIsVisible,
-- -- EscortTargetLastTime,
-- -- EscortTargetKnowType,
-- -- EscortTargetKnowDistance,
-- -- EscortTargetLastPos,
-- -- EscortTargetLastVelocity } )
--
--
-- local EscortTargetUnitVec3 = EscortTargetUnit:GetVec3()
-- local EscortVec3 = self.EscortGroup:GetVec3()
-- local Distance = ( ( EscortTargetUnitVec3.x - EscortVec3.x )^2 +
-- ( EscortTargetUnitVec3.y - EscortVec3.y )^2 +
-- ( EscortTargetUnitVec3.z - EscortVec3.z )^2
-- ) ^ 0.5 / 1000
--
-- self:T( { self.EscortGroup:GetName(), EscortTargetUnit:GetName(), Distance, EscortTarget } )
--
-- if Distance <= 15 then
--
-- if not ClientEscortTargets[EscortTargetUnitName] then
-- ClientEscortTargets[EscortTargetUnitName] = {}
-- end
-- ClientEscortTargets[EscortTargetUnitName].AttackUnit = EscortTargetUnit
-- ClientEscortTargets[EscortTargetUnitName].visible = EscortTarget.visible
-- ClientEscortTargets[EscortTargetUnitName].type = EscortTarget.type
-- ClientEscortTargets[EscortTargetUnitName].distance = EscortTarget.distance
-- else
-- if ClientEscortTargets[EscortTargetUnitName] then
-- ClientEscortTargets[EscortTargetUnitName] = nil
-- end
-- end
-- end
-- end
--
-- self:T( { "Sorting Targets Table:", ClientEscortTargets } )
-- table.sort( ClientEscortTargets, function( a, b ) return a.Distance < b.Distance end )
-- self:T( { "Sorted Targets Table:", ClientEscortTargets } )
--
-- -- Remove the sub menus of the Attack menu of the Escort for the EscortGroup.
-- self.EscortMenuAttackNearbyTargets:RemoveSubMenus()
--
-- if self.EscortMenuTargetAssistance then
-- self.EscortMenuTargetAssistance:RemoveSubMenus()
-- end
--
-- --for MenuIndex = 1, #self.EscortMenuAttackTargets do
-- -- self:T( { "Remove Menu:", self.EscortMenuAttackTargets[MenuIndex] } )
-- -- self.EscortMenuAttackTargets[MenuIndex] = self.EscortMenuAttackTargets[MenuIndex]:Remove()
-- --end
--
--
-- if ClientEscortTargets then
-- for ClientEscortTargetUnitName, ClientEscortTargetData in pairs( ClientEscortTargets ) do
--
-- for ClientEscortGroupName, EscortGroupData in pairs( self.EscortClient._EscortGroups ) do
--
-- if ClientEscortTargetData and ClientEscortTargetData.AttackUnit:IsAlive() then
--
-- local EscortTargetMessage = ""
-- local EscortTargetCategoryName = ClientEscortTargetData.AttackUnit:GetCategoryName()
-- local EscortTargetCategoryType = ClientEscortTargetData.AttackUnit:GetTypeName()
-- if ClientEscortTargetData.type then
-- EscortTargetMessage = EscortTargetMessage .. EscortTargetCategoryName .. " (" .. EscortTargetCategoryType .. ") at "
-- else
-- EscortTargetMessage = EscortTargetMessage .. "Unknown target at "
-- end
--
-- local EscortTargetUnitVec3 = ClientEscortTargetData.AttackUnit:GetVec3()
-- local EscortVec3 = self.EscortGroup:GetVec3()
-- local Distance = ( ( EscortTargetUnitVec3.x - EscortVec3.x )^2 +
-- ( EscortTargetUnitVec3.y - EscortVec3.y )^2 +
-- ( EscortTargetUnitVec3.z - EscortVec3.z )^2
-- ) ^ 0.5 / 1000
--
-- self:T( { self.EscortGroup:GetName(), ClientEscortTargetData.AttackUnit:GetName(), Distance, ClientEscortTargetData.AttackUnit } )
-- if ClientEscortTargetData.visible == false then
-- EscortTargetMessage = EscortTargetMessage .. string.format( "%.2f", Distance ) .. " estimated km"
-- else
-- EscortTargetMessage = EscortTargetMessage .. string.format( "%.2f", Distance ) .. " km"
-- end
--
-- if ClientEscortTargetData.visible then
-- EscortTargetMessage = EscortTargetMessage .. ", visual"
-- end
--
-- if ClientEscortGroupName == EscortGroupName then
--
-- MENU_CLIENT_COMMAND:New( self.EscortClient,
-- EscortTargetMessage,
-- self.EscortMenuAttackNearbyTargets,
-- ESCORT._AttackTarget,
-- { ParamSelf = self,
-- ParamUnit = ClientEscortTargetData.AttackUnit
-- }
-- )
-- EscortTargetMessages = EscortTargetMessages .. "\n - " .. EscortTargetMessage
-- else
-- if self.EscortMenuTargetAssistance then
-- local MenuTargetAssistance = MENU_CLIENT:New( self.EscortClient, EscortGroupData.EscortName, self.EscortMenuTargetAssistance )
-- MENU_CLIENT_COMMAND:New( self.EscortClient,
-- EscortTargetMessage,
-- MenuTargetAssistance,
-- ESCORT._AssistTarget,
-- self,
-- EscortGroupData.EscortGroup,
-- ClientEscortTargetData.AttackUnit
-- )
-- end
-- end
-- else
-- ClientEscortTargetData = nil
-- end
-- end
-- end
--
-- if EscortTargetMessages ~= "" and self.ReportTargets == true then
-- self.EscortGroup:MessageToClient( "Detected targets within 15 km range:" .. EscortTargetMessages:gsub("\n$",""), 20, self.EscortClient )
-- else
-- self.EscortGroup:MessageToClient( "No targets detected!", 20, self.EscortClient )
-- end
-- end
--
-- if self.EscortMenuResumeMission then
-- self.EscortMenuResumeMission:RemoveSubMenus()
--
-- -- if self.EscortMenuResumeWayPoints then
-- -- for MenuIndex = 1, #self.EscortMenuResumeWayPoints do
-- -- self:T( { "Remove Menu:", self.EscortMenuResumeWayPoints[MenuIndex] } )
-- -- self.EscortMenuResumeWayPoints[MenuIndex] = self.EscortMenuResumeWayPoints[MenuIndex]:Remove()
-- -- end
-- -- end
--
-- local TaskPoints = self:RegisterRoute()
-- for WayPointID, WayPoint in pairs( TaskPoints ) do
-- local EscortVec3 = self.EscortGroup:GetVec3()
-- local Distance = ( ( WayPoint.x - EscortVec3.x )^2 +
-- ( WayPoint.y - EscortVec3.z )^2
-- ) ^ 0.5 / 1000
-- MENU_CLIENT_COMMAND:New( self.EscortClient, "Waypoint " .. WayPointID .. " at " .. string.format( "%.2f", Distance ).. "km", self.EscortMenuResumeMission, ESCORT._ResumeMission, { ParamSelf = self, ParamWayPoint = WayPointID } )
-- end
-- end
--
-- return true
end
if self.EscortMenuResumeMission then
self.EscortMenuResumeMission:RemoveSubMenus()
-- if self.EscortMenuResumeWayPoints then
-- for MenuIndex = 1, #self.EscortMenuResumeWayPoints do
-- self:T( { "Remove Menu:", self.EscortMenuResumeWayPoints[MenuIndex] } )
-- self.EscortMenuResumeWayPoints[MenuIndex] = self.EscortMenuResumeWayPoints[MenuIndex]:Remove()
-- end
-- end
local TaskPoints = self:RegisterRoute()
for WayPointID, WayPoint in pairs( TaskPoints ) do
local EscortVec3 = self.EscortGroup:GetVec3()
local Distance = ( ( WayPoint.x - EscortVec3.x )^2 +
( WayPoint.y - EscortVec3.z )^2
) ^ 0.5 / 1000
MENU_CLIENT_COMMAND:New( self.EscortClient, "Waypoint " .. WayPointID .. " at " .. string.format( "%.2f", Distance ).. "km", self.EscortMenuResumeMission, ESCORT._ResumeMission, { ParamSelf = self, ParamWayPoint = WayPointID } )
end
end
return true
end
return false

View File

@@ -171,7 +171,7 @@ function MISSILETRAINER:New( Distance, Briefing )
self.Distance = Distance / 1000
_EVENTDISPATCHER:OnShot( self._EventShot, self )
self:HandleEvent( EVENTS.Shot )
self.DBClients = SET_CLIENT:New():FilterStart()
@@ -442,21 +442,21 @@ function MISSILETRAINER._MenuMessages( MenuParameters )
if MenuParameters.Distance ~= nil then
self.Distance = MenuParameters.Distance
MESSAGE:New( "Hit detection distance set to " .. self.Distance .. " meters", 15, "Menu" ):ToAll()
MESSAGE:New( "Hit detection distance set to " .. self.Distance * 1000 .. " meters", 15, "Menu" ):ToAll()
end
end
--- Detects if an SA site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME.
-- @param #MISSILETRAINER self
-- @param Core.Event#EVENTDATA Event
function MISSILETRAINER:_EventShot( Event )
self:F( { Event } )
-- @param Core.Event#EVENTDATA EventData
function MISSILETRAINER:OnEventShot( EVentData )
self:F( { EVentData } )
local TrainerSourceDCSUnit = Event.IniDCSUnit
local TrainerSourceDCSUnitName = Event.IniDCSUnitName
local TrainerWeapon = Event.Weapon -- Identify the weapon fired
local TrainerWeaponName = Event.WeaponName -- return weapon type
local TrainerSourceDCSUnit = EVentData.IniDCSUnit
local TrainerSourceDCSUnitName = EVentData.IniDCSUnitName
local TrainerWeapon = EVentData.Weapon -- Identify the weapon fired
local TrainerWeaponName = EVentData.WeaponName -- return weapon type
self:T( "Missile Launched = " .. TrainerWeaponName )

View File

@@ -3,10 +3,11 @@
-- Performance: If in a DCSRTE there are a lot of moving GROUND units, then in a multi player mission, this WILL create lag if
-- the main DCS execution core of your CPU is fully utilized. So, this class will limit the amount of simultaneous moving GROUND units
-- on defined intervals (currently every minute).
-- @module MOVEMENT
-- @module Movement
--- the MOVEMENT class
-- @type
-- @type MOVEMENT
-- @extends Core.Base#BASE
MOVEMENT = {
ClassName = "MOVEMENT",
}
@@ -20,7 +21,7 @@ MOVEMENT = {
-- Movement_US_Platoons = MOVEMENT:New( { 'US Tank Platoon Left', 'US Tank Platoon Middle', 'US Tank Platoon Right', 'US CH-47D Troops' }, 15 )
function MOVEMENT:New( MovePrefixes, MoveMaximum )
local self = BASE:Inherit( self, BASE:New() )
local self = BASE:Inherit( self, BASE:New() ) -- #MOVEMENT
self:F( { MovePrefixes, MoveMaximum } )
if type( MovePrefixes ) == 'table' then
@@ -33,7 +34,7 @@ function MOVEMENT:New( MovePrefixes, MoveMaximum )
self.AliveUnits = 0 -- Contains the counter how many units are currently alive
self.MoveUnits = {} -- Reflects if the Moving for this MovePrefixes is going to be scheduled or not.
_EVENTDISPATCHER:OnBirth( self.OnBirth, self )
self:HandleEvent( EVENTS.Birth )
-- self:AddEvent( world.event.S_EVENT_BIRTH, self.OnBirth )
--
@@ -60,24 +61,26 @@ end
--- Captures the birth events when new Units were spawned.
-- @todo This method should become obsolete. The new @{DATABASE} class will handle the collection administration.
function MOVEMENT:OnBirth( Event )
self:F( { Event } )
-- @param #MOVEMENT self
-- @param Core.Event#EVENTDATA self
function MOVEMENT:OnEventBirth( EventData )
self:F( { EventData } )
if timer.getTime0() < timer.getAbsTime() then -- dont need to add units spawned in at the start of the mission if mist is loaded in init line
if Event.IniDCSUnit then
self:T( "Birth object : " .. Event.IniDCSUnitName )
if Event.IniDCSGroup and Event.IniDCSGroup:isExist() then
if EventData.IniDCSUnit then
self:T( "Birth object : " .. EventData.IniDCSUnitName )
if EventData.IniDCSGroup and EventData.IniDCSGroup:isExist() then
for MovePrefixID, MovePrefix in pairs( self.MovePrefixes ) do
if string.find( Event.IniDCSUnitName, MovePrefix, 1, true ) then
if string.find( EventData.IniDCSUnitName, MovePrefix, 1, true ) then
self.AliveUnits = self.AliveUnits + 1
self.MoveUnits[Event.IniDCSUnitName] = Event.IniDCSGroupName
self.MoveUnits[EventData.IniDCSUnitName] = EventData.IniDCSGroupName
self:T( self.AliveUnits )
end
end
end
end
_EVENTDISPATCHER:OnCrashForUnit( Event.IniDCSUnitName, self.OnDeadOrCrash, self )
_EVENTDISPATCHER:OnDeadForUnit( Event.IniDCSUnitName, self.OnDeadOrCrash, self )
EventData.IniUnit:HandleEvent( EVENTS.DEAD, self.OnDeadOrCrash )
end
end

View File

@@ -5,8 +5,6 @@
--
-- ===
--
-- # 1) @{Scoring#SCORING} class, extends @{Base#BASE}
--
-- The @{#SCORING} class administers the scoring of player achievements,
-- and creates a CSV file logging the scoring events and results for use at team or squadron websites.
--
@@ -57,6 +55,8 @@
-- Use the radio menu F10 to consult the scores while running the mission.
-- Scores can be reported for your user, or an overall score can be reported of all players currently active in the mission.
--
-- # 1) @{Scoring#SCORING} class, extends @{Base#BASE}
--
-- ## 1.1) Set the destroy score or penalty scale
--
-- Score scales can be set for scores granted when enemies or friendlies are destroyed.
@@ -86,8 +86,6 @@
-- For example, this can be done as follows:
--
-- Scoring:RemoveUnitScore( UNIT:FindByName( "Unit #001" ) )
--
--
--
-- ## 1.3) Define destruction zones that will give extra scores.
--
@@ -546,6 +544,7 @@ function SCORING:_AddPlayerFromUnit( UnitData )
local UnitCategory = UnitDesc.category
local UnitCoalition = UnitData:GetCoalition()
local UnitTypeName = UnitData:GetTypeName()
local UnitThreatLevel, UnitThreatType = UnitData:GetThreatLevel()
self:T( { PlayerName, UnitName, UnitCategory, UnitCoalition, UnitTypeName } )
@@ -577,7 +576,7 @@ function SCORING:_AddPlayerFromUnit( UnitData )
"(changed " .. self.Players[PlayerName].PenaltyCoalition .. " times the coalition). 50 Penalty points added.",
2
):ToAll()
self:ScoreCSV( PlayerName, "COALITION_PENALTY", 1, -50, self.Players[PlayerName].UnitName, _SCORINGCoalition[self.Players[PlayerName].UnitCoalition], _SCORINGCategory[self.Players[PlayerName].UnitCategory], self.Players[PlayerName].UnitType,
self:ScoreCSV( PlayerName, "", "COALITION_PENALTY", 1, -50, self.Players[PlayerName].UnitName, _SCORINGCoalition[self.Players[PlayerName].UnitCoalition], _SCORINGCategory[self.Players[PlayerName].UnitCategory], self.Players[PlayerName].UnitType,
UnitName, _SCORINGCoalition[UnitCoalition], _SCORINGCategory[UnitCategory], UnitData:GetTypeName() )
end
end
@@ -586,6 +585,8 @@ function SCORING:_AddPlayerFromUnit( UnitData )
self.Players[PlayerName].UnitCategory = UnitCategory
self.Players[PlayerName].UnitType = UnitTypeName
self.Players[PlayerName].UNIT = UnitData
self.Players[PlayerName].ThreatLevel = UnitThreatLevel
self.Players[PlayerName].ThreatType = UnitThreatType
if self.Players[PlayerName].Penalty > self.Fratricide * 0.50 then
if self.Players[PlayerName].PenaltyWarning < 1 then
@@ -597,7 +598,7 @@ function SCORING:_AddPlayerFromUnit( UnitData )
end
if self.Players[PlayerName].Penalty > self.Fratricide then
UnitData:Destroy()
--UnitData:Destroy()
MESSAGE:New( "Player '" .. PlayerName .. "' committed FRATRICIDE, he will be COURT MARTIALED and is DISMISSED from this mission!",
10
):ToAll()
@@ -633,7 +634,7 @@ function SCORING:AddGoalScore( PlayerUnit, GoalTag, Text, Score )
MESSAGE:New( Text, 30 ):ToAll()
self:ScoreCSV( PlayerName, "GOAL_" .. string.upper( GoalTag ), 1, Score, PlayerUnit:GetName() )
self:ScoreCSV( PlayerName, "", "GOAL_" .. string.upper( GoalTag ), 1, Score, PlayerUnit:GetName() )
end
end
@@ -671,7 +672,7 @@ function SCORING:_AddMissionTaskScore( Mission, PlayerUnit, Text, Score )
Score .. " task score!",
30 ):ToAll()
self:ScoreCSV( PlayerName, "TASK_" .. MissionName:gsub( ' ', '_' ), 1, Score, PlayerUnit:GetName() )
self:ScoreCSV( PlayerName, "", "TASK_" .. MissionName:gsub( ' ', '_' ), 1, Score, PlayerUnit:GetName() )
end
end
@@ -701,7 +702,7 @@ function SCORING:_AddMissionScore( Mission, Text, Score )
Score .. " mission score!",
60 ):ToAll()
self:ScoreCSV( PlayerName, "MISSION_" .. MissionName:gsub( ' ', '_' ), 1, Score )
self:ScoreCSV( PlayerName, "", "MISSION_" .. MissionName:gsub( ' ', '_' ), 1, Score )
end
end
end
@@ -728,7 +729,8 @@ function SCORING:OnEventPlayerLeaveUnit( Event )
if Event.IniUnit then
local Menu = self:GetState( Event.IniUnit, "ScoringMenu" ) -- Core.Menu#MENU_GROUP
if Menu then
Menu:Remove()
-- TODO: Check if this fixes #281.
--Menu:Remove()
end
end
end
@@ -844,6 +846,7 @@ function SCORING:_EventOnHit( Event )
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit or 0
PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0
PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT
PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel()
-- Only grant hit scores if there was more than one second between the last hit.
if timer.getTime() - PlayerHit.TimeStamp > 1 then
@@ -882,7 +885,7 @@ function SCORING:_EventOnHit( Event )
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
end
self:ScoreCSV( InitPlayerName, "HIT_PENALTY", 1, -25, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
self:ScoreCSV( InitPlayerName, TargetPlayerName, "HIT_PENALTY", 1, -10, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
else
Player.Score = Player.Score + 1
PlayerHit.Score = PlayerHit.Score + 1
@@ -906,7 +909,7 @@ function SCORING:_EventOnHit( Event )
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
end
self:ScoreCSV( InitPlayerName, "HIT_SCORE", 1, 1, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
self:ScoreCSV( InitPlayerName, TargetPlayerName, "HIT_SCORE", 1, 1, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
end
else -- A scenery object was hit.
MESSAGE
@@ -915,7 +918,7 @@ function SCORING:_EventOnHit( Event )
)
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
self:ScoreCSV( InitPlayerName, "HIT_SCORE", 1, 1, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, "", "Scenery", TargetUnitType )
self:ScoreCSV( InitPlayerName, "", "HIT_SCORE", 1, 0, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, "", "Scenery", TargetUnitType )
end
end
end
@@ -923,6 +926,89 @@ function SCORING:_EventOnHit( Event )
elseif InitPlayerName == nil then -- It is an AI hitting a player???
end
-- It is a weapon initiated by a player, that is hitting something
-- This seems to occur only with scenery and static objects.
if Event.WeaponPlayerName ~= nil then
self:_AddPlayerFromUnit( Event.WeaponUNIT )
if self.Players[Event.WeaponPlayerName] then -- This should normally not happen, but i'll test it anyway.
if TargetPlayerName ~= nil then -- It is a player hitting another player ...
self:_AddPlayerFromUnit( TargetUNIT )
end
self:T( "Hitting Scenery" )
-- What is he hitting?
if TargetCategory then
-- A scenery or static got hit, score it.
-- Player contains the score data from self.Players[WeaponPlayerName]
local Player = self.Players[Event.WeaponPlayerName]
-- Ensure there is a hit table per TargetCategory and TargetUnitName.
Player.Hit[TargetCategory] = Player.Hit[TargetCategory] or {}
Player.Hit[TargetCategory][TargetUnitName] = Player.Hit[TargetCategory][TargetUnitName] or {}
-- PlayerHit contains the score counters and data per unit that was hit.
local PlayerHit = Player.Hit[TargetCategory][TargetUnitName]
PlayerHit.Score = PlayerHit.Score or 0
PlayerHit.Penalty = PlayerHit.Penalty or 0
PlayerHit.ScoreHit = PlayerHit.ScoreHit or 0
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit or 0
PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0
PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT
PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel()
-- Only grant hit scores if there was more than one second between the last hit.
if timer.getTime() - PlayerHit.TimeStamp > 1 then
PlayerHit.TimeStamp = timer.getTime()
local Score = 0
if InitCoalition then -- A coalition object was hit, probably a static.
if InitCoalition == TargetCoalition then
-- TODO: Penalty according scale
Player.Penalty = Player.Penalty + 10
PlayerHit.Penalty = PlayerHit.Penalty + 10
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1
MESSAGE
:New( "Player '" .. Event.WeaponPlayerName .. "' hit a friendly target " ..
TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " ..
"Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty,
2
)
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
self:ScoreCSV( Event.WeaponPlayerName, TargetPlayerName, "HIT_PENALTY", 1, -10, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
else
Player.Score = Player.Score + 1
PlayerHit.Score = PlayerHit.Score + 1
PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1
MESSAGE
:New( "Player '" .. Event.WeaponPlayerName .. "' hit an enemy target " ..
TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " ..
"Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty,
2
)
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
self:ScoreCSV( Event.WeaponPlayerName, TargetPlayerName, "HIT_SCORE", 1, 1, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
end
else -- A scenery object was hit.
MESSAGE
:New( "Player '" .. Event.WeaponPlayerName .. "' hit a scenery object.",
2
)
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
self:ScoreCSV( Event.WeaponPlayerName, "", "HIT_SCORE", 1, 0, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, "", "Scenery", TargetUnitType )
end
end
end
end
end
end
--- Track DEAD or CRASH events for the scoring.
@@ -979,8 +1065,13 @@ function SCORING:_EventOnDeadOrCrash( Event )
self:T( { InitUnitName, InitUnitType, InitUnitCoalition, InitCoalition, InitUnitCategory, InitCategory } )
local Destroyed = false
-- What is the player destroying?
if Player and Player.Hit and Player.Hit[TargetCategory] and Player.Hit[TargetCategory][TargetUnitName] then -- Was there a hit for this unit for this player before registered???
if Player and Player.Hit and Player.Hit[TargetCategory] and Player.Hit[TargetCategory][TargetUnitName] and Player.Hit[TargetCategory][TargetUnitName].TimeStamp ~= 0 then -- Was there a hit for this unit for this player before registered???
local TargetThreatLevel = Player.Hit[TargetCategory][TargetUnitName].ThreatLevel
local TargetThreatType = Player.Hit[TargetCategory][TargetUnitName].ThreatType
Player.Destroy[TargetCategory] = Player.Destroy[TargetCategory] or {}
Player.Destroy[TargetCategory][TargetType] = Player.Destroy[TargetCategory][TargetType] or {}
@@ -994,8 +1085,9 @@ function SCORING:_EventOnDeadOrCrash( Event )
if TargetCoalition then
if InitCoalition == TargetCoalition then
local ThreatLevelTarget, ThreatTypeTarget = TargetUnit:GetThreatLevel()
local ThreatLevelPlayer = Player.UNIT:GetThreatLevel() / 10 + 1
local ThreatLevelTarget = TargetThreatLevel
local ThreatTypeTarget = TargetThreatType
local ThreatLevelPlayer = Player.ThreatLevel / 10 + 1
local ThreatPenalty = math.ceil( ( ThreatLevelTarget / ThreatLevelPlayer ) * self.ScaleDestroyPenalty / 10 )
self:E( { ThreatLevel = ThreatPenalty, ThreatLevelTarget = ThreatLevelTarget, ThreatTypeTarget = ThreatTypeTarget, ThreatLevelPlayer = ThreatLevelPlayer } )
@@ -1022,11 +1114,14 @@ function SCORING:_EventOnDeadOrCrash( Event )
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
end
self:ScoreCSV( PlayerName, "DESTROY_PENALTY", 1, ThreatPenalty, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
Destroyed = true
self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_PENALTY", 1, ThreatPenalty, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
else
local ThreatLevelTarget, ThreatTypeTarget = TargetUnit:GetThreatLevel()
local ThreatLevelPlayer = Player.UNIT:GetThreatLevel() / 10 + 1
local ThreatLevelTarget = TargetThreatLevel
local ThreatTypeTarget = TargetThreatType
local ThreatLevelPlayer = Player.ThreatLevel / 10 + 1
local ThreatScore = math.ceil( ( ThreatLevelTarget / ThreatLevelPlayer ) * self.ScaleDestroyScore / 10 )
self:E( { ThreatLevel = ThreatScore, ThreatLevelTarget = ThreatLevelTarget, ThreatTypeTarget = ThreatTypeTarget, ThreatLevelPlayer = ThreatLevelPlayer } )
@@ -1053,7 +1148,8 @@ function SCORING:_EventOnDeadOrCrash( Event )
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
end
self:ScoreCSV( PlayerName, "DESTROY_SCORE", 1, ThreatScore, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
Destroyed = true
self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_SCORE", 1, ThreatScore, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
local UnitName = TargetUnit:GetName()
local Score = self.ScoringObjects[UnitName]
@@ -1067,7 +1163,8 @@ function SCORING:_EventOnDeadOrCrash( Event )
)
:ToAllIf( self:IfMessagesScore() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesScore() and self:IfMessagesToCoalition() )
self:ScoreCSV( PlayerName, "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
Destroyed = true
end
-- Check if there are Zones where the destruction happened.
@@ -1085,7 +1182,8 @@ function SCORING:_EventOnDeadOrCrash( Event )
15 )
:ToAllIf( self:IfMessagesZone() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesZone() and self:IfMessagesToCoalition() )
self:ScoreCSV( PlayerName, "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
Destroyed = true
end
end
@@ -1107,10 +1205,18 @@ function SCORING:_EventOnDeadOrCrash( Event )
)
:ToAllIf( self:IfMessagesZone() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesZone() and self:IfMessagesToCoalition() )
self:ScoreCSV( PlayerName, "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, "", "Scenery", TargetUnitType )
Destroyed = true
self:ScoreCSV( PlayerName, "", "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, "", "Scenery", TargetUnitType )
end
end
end
-- Delete now the hit cache if the target was destroyed.
-- Otherwise points will be granted every time a target gets killed by the players that hit that target.
-- This is only relevant for player to player destroys.
if Destroyed then
Player.Hit[TargetCategory][TargetUnitName].TimeStamp = 0
end
end
end
end
@@ -1529,7 +1635,7 @@ function SCORING:OpenCSV( ScoringCSV )
error( "Error: Cannot open CSV file in " .. lfs.writedir() )
end
self.CSVFile:write( '"GameName","RunTime","Time","PlayerName","ScoreType","PlayerUnitCoaltion","PlayerUnitCategory","PlayerUnitType","PlayerUnitName","TargetUnitCoalition","TargetUnitCategory","TargetUnitType","TargetUnitName","Times","Score"\n' )
self.CSVFile:write( '"GameName","RunTime","Time","PlayerName","TargetPlayerName","ScoreType","PlayerUnitCoaltion","PlayerUnitCategory","PlayerUnitType","PlayerUnitName","TargetUnitCoalition","TargetUnitCategory","TargetUnitType","TargetUnitName","Times","Score"\n' )
self.RunTime = os.date("%y-%m-%d_%H-%M-%S")
else
@@ -1545,6 +1651,7 @@ end
--- Registers a score for a player.
-- @param #SCORING self
-- @param #string PlayerName The name of the player.
-- @param #string TargetPlayerName The name of the target player.
-- @param #string ScoreType The type of the score.
-- @param #string ScoreTimes The amount of scores achieved.
-- @param #string ScoreAmount The score given.
@@ -1557,10 +1664,13 @@ end
-- @param #string TargetUnitCategory The category of the target unit.
-- @param #string TargetUnitType The type of the target unit.
-- @return #SCORING self
function SCORING:ScoreCSV( PlayerName, ScoreType, ScoreTimes, ScoreAmount, PlayerUnitName, PlayerUnitCoalition, PlayerUnitCategory, PlayerUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
function SCORING:ScoreCSV( PlayerName, TargetPlayerName, ScoreType, ScoreTimes, ScoreAmount, PlayerUnitName, PlayerUnitCoalition, PlayerUnitCategory, PlayerUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
--write statistic information to file
local ScoreTime = self:SecondsToClock( timer.getTime() )
PlayerName = PlayerName:gsub( '"', '_' )
TargetPlayerName = TargetPlayerName or ""
TargetPlayerName = TargetPlayerName:gsub( '"', '_' )
if PlayerUnitName and PlayerUnitName ~= '' then
local PlayerUnit = Unit.getByName( PlayerUnitName )
@@ -1602,6 +1712,7 @@ function SCORING:ScoreCSV( PlayerName, ScoreType, ScoreTimes, ScoreAmount, Playe
'"' .. self.RunTime .. '"' .. ',' ..
'' .. ScoreTime .. '' .. ',' ..
'"' .. PlayerName .. '"' .. ',' ..
'"' .. TargetPlayerName .. '"' .. ',' ..
'"' .. ScoreType .. '"' .. ',' ..
'"' .. PlayerUnitCoalition .. '"' .. ',' ..
'"' .. PlayerUnitCategory .. '"' .. ',' ..

View File

@@ -36,25 +36,28 @@ function SEAD:New( SEADGroupPrefixes )
else
self.SEADGroupNames[SEADGroupPrefixes] = SEADGroupPrefixes
end
_EVENTDISPATCHER:OnShot( self.EventShot, self )
self:HandleEvent( EVENTS.Shot )
return self
end
--- Detects if an SA site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME.
-- @see SEAD
function SEAD:EventShot( Event )
self:F( { Event } )
-- @param #SEAD
-- @param Core.Event#EVENTDATA EventData
function SEAD:OnEventShot( EventData )
self:F( { EventData } )
local SEADUnit = Event.IniDCSUnit
local SEADUnitName = Event.IniDCSUnitName
local SEADWeapon = Event.Weapon -- Identify the weapon fired
local SEADWeaponName = Event.WeaponName -- return weapon type
local SEADUnit = EventData.IniDCSUnit
local SEADUnitName = EventData.IniDCSUnitName
local SEADWeapon = EventData.Weapon -- Identify the weapon fired
local SEADWeaponName = EventData.WeaponName -- return weapon type
-- Start of the 2nd loop
self:T( "Missile Launched = " .. SEADWeaponName )
if SEADWeaponName == "KH-58" or SEADWeaponName == "KH-25MPU" or SEADWeaponName == "AGM-88" or SEADWeaponName == "KH-31A" or SEADWeaponName == "KH-31P" then -- Check if the missile is a SEAD
local _evade = math.random (1,100) -- random number for chance of evading action
local _targetMim = Event.Weapon:getTarget() -- Identify target
local _targetMim = EventData.Weapon:getTarget() -- Identify target
local _targetMimname = Unit.getName(_targetMim)
local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon))
local _targetMimgroupName = _targetMimgroup:getName()

View File

@@ -1,157 +1,26 @@
--- Single-Player:**Yes** / Multi-Player:**Yes** / AI:**Yes** / Human:**No** / Types:**All** --
-- **Spawn groups of units dynamically in your missions.**
--- **Functional** -- Spawn dynamically new GROUPs in your missions.
--
-- ![Banner Image](..\Presentations\SPAWN\SPAWN.JPG)
--
-- ===
-- ====
--
-- # 1) @{#SPAWN} class, extends @{Base#BASE}
--
-- The @{#SPAWN} class allows to spawn dynamically new groups, based on pre-defined initialization settings, modifying the behaviour when groups are spawned.
-- For each group to be spawned, within the mission editor, a group has to be created with the "late activation flag" set. We call this group the *"Spawn Template"* of the SPAWN object.
-- A reference to this Spawn Template needs to be provided when constructing the SPAWN object, by indicating the name of the group within the mission editor in the constructor methods.
--
-- Within the SPAWN object, there is an internal index that keeps track of which group from the internal group list was spawned.
-- When new groups get spawned by using the SPAWN methods (see below), it will be validated whether the Limits (@{#SPAWN.Limit}) of the SPAWN object are not reached.
-- When all is valid, a new group will be created by the spawning methods, and the internal index will be increased with 1.
--
-- Regarding the name of new spawned groups, a _SpawnPrefix_ will be assigned for each new group created.
-- If you want to have the Spawn Template name to be used as the _SpawnPrefix_ name, use the @{#SPAWN.New} constructor.
-- However, when the @{#SPAWN.NewWithAlias} constructor was used, the Alias name will define the _SpawnPrefix_ name.
-- Groups will follow the following naming structure when spawned at run-time:
--
-- 1. Spawned groups will have the name _SpawnPrefix_#ggg, where ggg is a counter from 0 to 999.
-- 2. Spawned units will have the name _SpawnPrefix_#ggg-uu, where uu is a counter from 0 to 99 for each new spawned unit belonging to the group.
--
-- Some additional notes that need to be remembered:
--
-- * Templates are actually groups defined within the mission editor, with the flag "Late Activation" set. As such, these groups are never used within the mission, but are used by the @{#SPAWN} module.
-- * It is important to defined BEFORE you spawn new groups, a proper initialization of the SPAWN instance is done with the options you want to use.
-- * When designing a mission, NEVER name groups using a "#" within the name of the group Spawn Template(s), or the SPAWN module logic won't work anymore.
--
-- ## 1.1) SPAWN construction methods
--
-- Create a new SPAWN object with the @{#SPAWN.New}() or the @{#SPAWN.NewWithAlias}() methods:
--
-- * @{#SPAWN.New}(): Creates a new SPAWN object taking the name of the group that represents the GROUP Template (definition).
-- * @{#SPAWN.NewWithAlias}(): Creates a new SPAWN object taking the name of the group that represents the GROUP Template (definition), and gives each spawned @{Group} an different name.
--
-- It is important to understand how the SPAWN class works internally. The SPAWN object created will contain internally a list of groups that will be spawned and that are already spawned.
-- The initialization methods will modify this list of groups so that when a group gets spawned, ALL information is already prepared when spawning. This is done for performance reasons.
-- So in principle, the group list will contain all parameters and configurations after initialization, and when groups get actually spawned, this spawning can be done quickly and efficient.
--
-- ## 1.2) SPAWN initialization methods
--
-- A spawn object will behave differently based on the usage of **initialization** methods, which all start with the **Init** prefix:
--
-- * @{#SPAWN.InitLimit}(): Limits the amount of groups that can be alive at the same time and that can be dynamically spawned.
-- * @{#SPAWN.InitRandomizeRoute}(): Randomize the routes of spawned groups, and for air groups also optionally the height.
-- * @{#SPAWN.InitRandomizeTemplate}(): Randomize the group templates so that when a new group is spawned, a random group template is selected from one of the templates defined.
-- * @{#SPAWN.InitUnControlled}(): Spawn plane groups uncontrolled.
-- * @{#SPAWN.InitArray}(): Make groups visible before they are actually activated, and order these groups like a batallion in an array.
-- * @{#SPAWN.InitRepeat}(): Re-spawn groups when they land at the home base. Similar methods are @{#SPAWN.InitRepeatOnLanding} and @{#SPAWN.InitRepeatOnEngineShutDown}.
-- * @{#SPAWN.InitRandomizeUnits}(): Randomizes the @{Unit}s in the @{Group} that is spawned within a **radius band**, given an Outer and Inner radius.
-- * @{#SPAWN.InitRandomizeZones}(): Randomizes the spawning between a predefined list of @{Zone}s that are declared using this function. Each zone can be given a probability factor.
-- * @{#SPAWN.InitAIOn}(): Turns the AI On when spawning the new @{Group} object.
-- * @{#SPAWN.InitAIOff}(): Turns the AI Off when spawning the new @{Group} object.
-- * @{#SPAWN.InitAIOnOff}(): Turns the AI On or Off when spawning the new @{Group} object.
--
-- ## 1.3) SPAWN spawning methods
--
-- Groups can be spawned at different times and methods:
--
-- * @{#SPAWN.Spawn}(): Spawn one new group based on the last spawned index.
-- * @{#SPAWN.ReSpawn}(): Re-spawn a group based on a given index.
-- * @{#SPAWN.SpawnScheduled}(): Spawn groups at scheduled but randomized intervals. You can use @{#SPAWN.SpawnScheduleStart}() and @{#SPAWN.SpawnScheduleStop}() to start and stop the schedule respectively.
-- * @{#SPAWN.SpawnFromVec3}(): Spawn a new group from a Vec3 coordinate. (The group will can be spawned at a point in the air).
-- * @{#SPAWN.SpawnFromVec2}(): Spawn a new group from a Vec2 coordinate. (The group will be spawned at land height ).
-- * @{#SPAWN.SpawnFromStatic}(): Spawn a new group from a structure, taking the position of a @{Static}.
-- * @{#SPAWN.SpawnFromUnit}(): Spawn a new group taking the position of a @{Unit}.
-- * @{#SPAWN.SpawnInZone}(): Spawn a new group in a @{Zone}.
--
-- Note that @{#SPAWN.Spawn} and @{#SPAWN.ReSpawn} return a @{GROUP#GROUP.New} object, that contains a reference to the DCSGroup object.
-- You can use the @{GROUP} object to do further actions with the DCSGroup.
--
-- ## 1.4) Retrieve alive GROUPs spawned by the SPAWN object
--
-- The SPAWN class administers which GROUPS it has reserved (in stock) or has created during mission execution.
-- Every time a SPAWN object spawns a new GROUP object, a reference to the GROUP object is added to an internal table of GROUPS.
-- SPAWN provides methods to iterate through that internal GROUP object reference table:
--
-- * @{#SPAWN.GetFirstAliveGroup}(): Will find the first alive GROUP it has spawned, and return the alive GROUP object and the first Index where the first alive GROUP object has been found.
-- * @{#SPAWN.GetNextAliveGroup}(): Will find the next alive GROUP object from a given Index, and return a reference to the alive GROUP object and the next Index where the alive GROUP has been found.
-- * @{#SPAWN.GetLastAliveGroup}(): Will find the last alive GROUP object, and will return a reference to the last live GROUP object and the last Index where the last alive GROUP object has been found.
--
-- You can use the methods @{#SPAWN.GetFirstAliveGroup}() and sequently @{#SPAWN.GetNextAliveGroup}() to iterate through the alive GROUPS within the SPAWN object, and to actions... See the respective methods for an example.
-- The method @{#SPAWN.GetGroupFromIndex}() will return the GROUP object reference from the given Index, dead or alive...
--
-- ## 1.5) SPAWN object cleaning
--
-- Sometimes, it will occur during a mission run-time, that ground or especially air objects get damaged, and will while being damged stop their activities, while remaining alive.
-- In such cases, the SPAWN object will just sit there and wait until that group gets destroyed, but most of the time it won't,
-- and it may occur that no new groups are or can be spawned as limits are reached.
-- To prevent this, a @{#SPAWN.InitCleanUp}() initialization method has been defined that will silently monitor the status of each spawned group.
-- Once a group has a velocity = 0, and has been waiting for a defined interval, that group will be cleaned or removed from run-time.
-- There is a catch however :-) If a damaged group has returned to an airbase within the coalition, that group will not be considered as "lost"...
-- In such a case, when the inactive group is cleaned, a new group will Re-spawned automatically.
-- This models AI that has succesfully returned to their airbase, to restart their combat activities.
-- Check the @{#SPAWN.InitCleanUp}() for further info.
--
-- ## 1.6) Catch the @{Group} spawn event in a callback function!
--
-- When using the SpawnScheduled method, new @{Group}s are created following the schedule timing parameters.
-- When a new @{Group} is spawned, you maybe want to execute actions with that group spawned at the spawn event.
-- To SPAWN class supports this functionality through the @{#SPAWN.OnSpawnGroup}( **function( SpawnedGroup ) end ** ) method, which takes a function as a parameter that you can define locally.
-- Whenever a new @{Group} is spawned, the given function is called, and the @{Group} that was just spawned, is given as a parameter.
-- As a result, your spawn event handling function requires one parameter to be declared, which will contain the spawned @{Group} object.
-- A coding example is provided at the description of the @{#SPAWN.OnSpawnGroup}( **function( SpawnedGroup ) end ** ) method.
-- The documentation of the SPAWN class can be found further in this document.
--
-- ====
--
-- # **API CHANGE HISTORY**
-- # Demo Missions
--
-- The underlying change log documents the API changes. Please read this carefully. The following notation is used:
-- ### [SPAWN Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/SPA%20-%20Spawning)
--
-- * **Added** parts are expressed in bold type face.
-- * _Removed_ parts are expressed in italic type face.
-- ### [SPAWN Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SPA%20-%20Spawning)
--
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
--
-- Hereby the change log:
-- ====
--
-- 2017-02-04: SPAWN:InitUnControlled( **UnControlled** ) replaces SPAWN:InitUnControlled().
-- # YouTube Channel
--
-- 2017-01-24: SPAWN:**InitAIOnOff( AIOnOff )** added.
--
-- 2017-01-24: SPAWN:**InitAIOn()** added.
--
-- 2017-01-24: SPAWN:**InitAIOff()** added.
--
-- 2016-08-15: SPAWN:**InitCleanUp**( SpawnCleanUpInterval ) replaces SPAWN:_CleanUp_( SpawnCleanUpInterval ).
--
-- 2016-08-15: SPAWN:**InitRandomizeZones( SpawnZones )** added.
--
-- 2016-08-14: SPAWN:**OnSpawnGroup**( SpawnCallBackFunction, ... ) replaces SPAWN:_SpawnFunction_( SpawnCallBackFunction, ... ).
--
-- 2016-08-14: SPAWN.SpawnInZone( Zone, __RandomizeGroup__, SpawnIndex ) replaces SpawnInZone( Zone, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ).
--
-- 2016-08-14: SPAWN.SpawnFromVec3( Vec3, SpawnIndex ) replaces SpawnFromVec3( Vec3, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ):
--
-- 2016-08-14: SPAWN.SpawnFromVec2( Vec2, SpawnIndex ) replaces SpawnFromVec2( Vec2, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ):
--
-- 2016-08-14: SPAWN.SpawnFromUnit( SpawnUnit, SpawnIndex ) replaces SpawnFromUnit( SpawnUnit, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ):
--
-- 2016-08-14: SPAWN.SpawnFromUnit( SpawnUnit, SpawnIndex ) replaces SpawnFromStatic( SpawnStatic, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ):
--
-- 2016-08-14: SPAWN.**InitRandomizeUnits( RandomizeUnits, OuterRadius, InnerRadius )** added:
--
-- 2016-08-14: SPAWN.**Init**Limit( SpawnMaxUnitsAlive, SpawnMaxGroups ) replaces SPAWN._Limit_( SpawnMaxUnitsAlive, SpawnMaxGroups ):
--
-- 2016-08-14: SPAWN.**Init**Array( SpawnAngle, SpawnWidth, SpawnDeltaX, SpawnDeltaY ) replaces SPAWN._Array_( SpawnAngle, SpawnWidth, SpawnDeltaX, SpawnDeltaY ).
--
-- 2016-08-14: SPAWN.**Init**RandomizeRoute( SpawnStartPoint, SpawnEndPoint, SpawnRadius, SpawnHeight ) replaces SPAWN._RandomizeRoute_( SpawnStartPoint, SpawnEndPoint, SpawnRadius, SpawnHeight ).
--
-- 2016-08-14: SPAWN.**Init**RandomizeTemplate( SpawnTemplatePrefixTable ) replaces SPAWN._RandomizeTemplate_( SpawnTemplatePrefixTable ).
--
-- 2016-08-14: SPAWN.**Init**UnControlled() replaces SPAWN._UnControlled_().
-- ### [SPAWN YouTube Channel](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl1jirWIo4t4YxqN-HxjqRkL)
--
-- ===
--
@@ -172,7 +41,6 @@
--- SPAWN Class
-- @type SPAWN
-- @extends Core.Base#BASE
-- @field ClassName
-- @field #string SpawnTemplatePrefix
-- @field #string SpawnAliasPrefix
@@ -181,6 +49,214 @@
-- @field #number SpawnIndex
-- @field #number MaxAliveGroups
-- @field #SPAWN.SpawnZoneTable SpawnZoneTable
-- @extends Core.Base#BASE
--- # SPAWN class, extends @{Base#BASE}
--
-- The SPAWN class allows to spawn dynamically new groups.
-- Each SPAWN object needs to be have a related **template group** setup in the Mission Editor (ME),
-- which is a normal group with the **Late Activation** flag set.
-- This template group will never be activated in your mission.
-- SPAWN uses that **template group** to reference to all the characteristics
-- (air, ground, livery, unit composition, formation, skill level etc) of each new group to be spawned.
--
-- Therefore, when creating a SPAWN object, the @{#SPAWN.New} and @{#SPAWN.NewWithAlias} require
-- **the name of the template group** to be given as a string to those constructor methods.
--
-- Initialization settings can be applied on the SPAWN object,
-- which modify the behaviour or the way groups are spawned.
-- These initialization methods have the prefix **Init**.
-- There are also spawn methods with the prefix **Spawn** and will spawn new groups in various ways.
--
-- ### IMPORTANT! The methods with prefix **Init** must be used before any methods with prefix **Spawn** method are used, or unexpected results may appear!!!
--
-- Because SPAWN can spawn multiple groups of a template group,
-- SPAWN has an **internal index** that keeps track
-- which was the latest group that was spawned.
--
-- **Limits** can be set on how many groups can be spawn in each SPAWN object,
-- using the method @{#SPAWN.InitLimit}. SPAWN has 2 kind of limits:
--
-- * The maximum amount of @{Unit}s that can be **alive** at the same time...
-- * The maximum amount of @{Group}s that can be **spawned**... This is more of a **resource**-type of limit.
--
-- When new groups get spawned using the **Spawn** methods,
-- it will be evaluated whether any limits have been reached.
-- When no spawn limit is reached, a new group will be created by the spawning methods,
-- and the internal index will be increased with 1.
--
-- These limits ensure that your mission does not accidentally get flooded with spawned groups.
-- Additionally, it also guarantees that independent of the group composition,
-- at any time, the most optimal amount of groups are alive in your mission.
-- For example, if your template group has a group composition of 10 units, and you specify a limit of 100 units alive at the same time,
-- with unlimited resources = :InitLimit( 100, 0 ) and 10 groups are alive, but two groups have only one unit alive in the group,
-- then a sequent Spawn(Scheduled) will allow a new group to be spawned!!!
--
-- ### IMPORTANT!! If a limit has been reached, it is possible that a **Spawn** method returns **nil**, meaning, no @{Group} had been spawned!!!
--
-- Spawned groups get **the same name** as the name of the template group.
-- Spawned units in those groups keep _by default_ **the same name** as the name of the template group.
-- However, because multiple groups and units are created from the template group,
-- a suffix is added to each spawned group and unit.
--
-- Newly spawned groups will get the following naming structure at run-time:
--
-- 1. Spawned groups will have the name _GroupName_#_nnn_, where _GroupName_ is the name of the **template group**,
-- and _nnn_ is a **counter from 0 to 999**.
-- 2. Spawned units will have the name _GroupName_#_nnn_-_uu_,
-- where _uu_ is a **counter from 0 to 99** for each new spawned unit belonging to the group.
--
-- That being said, there is a way to keep the same unit names!
-- The method @{#SPAWN.InitKeepUnitNames}() will keep the same unit names as defined within the template group, thus:
--
-- 3. Spawned units will have the name _UnitName_#_nnn_-_uu_,
-- where _UnitName_ is the **unit name as defined in the template group*,
-- and _uu_ is a **counter from 0 to 99** for each new spawned unit belonging to the group.
--
-- Some **additional notes that need to be considered!!**:
--
-- * templates are actually groups defined within the mission editor, with the flag "Late Activation" set.
-- As such, these groups are never used within the mission, but are used by the @{#SPAWN} module.
-- * It is important to defined BEFORE you spawn new groups,
-- a proper initialization of the SPAWN instance is done with the options you want to use.
-- * When designing a mission, NEVER name groups using a "#" within the name of the group Spawn template(s),
-- or the SPAWN module logic won't work anymore.
--
-- ## SPAWN construction methods
--
-- Create a new SPAWN object with the @{#SPAWN.New}() or the @{#SPAWN.NewWithAlias}() methods:
--
-- * @{#SPAWN.New}(): Creates a new SPAWN object taking the name of the group that represents the GROUP template (definition).
-- * @{#SPAWN.NewWithAlias}(): Creates a new SPAWN object taking the name of the group that represents the GROUP template (definition), and gives each spawned @{Group} an different name.
--
-- It is important to understand how the SPAWN class works internally. The SPAWN object created will contain internally a list of groups that will be spawned and that are already spawned.
-- The initialization methods will modify this list of groups so that when a group gets spawned, ALL information is already prepared when spawning. This is done for performance reasons.
-- So in principle, the group list will contain all parameters and configurations after initialization, and when groups get actually spawned, this spawning can be done quickly and efficient.
--
-- ## SPAWN **Init**ialization methods
--
-- A spawn object will behave differently based on the usage of **initialization** methods, which all start with the **Init** prefix:
--
-- ### Unit Names
--
-- * @{#SPAWN.InitKeepUnitNames}(): Keeps the unit names as defined within the mission editor, but note that anything after a # mark is ignored, and any spaces before and after the resulting name are removed. IMPORTANT! This method MUST be the first used after :New !!!
--
-- ### Route randomization
--
-- * @{#SPAWN.InitRandomizeRoute}(): Randomize the routes of spawned groups, and for air groups also optionally the height.
--
-- ### Group composition randomization
--
-- * @{#SPAWN.InitRandomizeTemplate}(): Randomize the group templates so that when a new group is spawned, a random group template is selected from one of the templates defined.
--
-- ### Uncontrolled
--
-- * @{#SPAWN.InitUnControlled}(): Spawn plane groups uncontrolled.
--
-- ### Array formation
--
-- * @{#SPAWN.InitArray}(): Make groups visible before they are actually activated, and order these groups like a batallion in an array.
--
-- ### Position randomization
--
-- * @{#SPAWN.InitRandomizePosition}(): Randomizes the position of @{Group}s that are spawned within a **radius band**, given an Outer and Inner radius, from the point that the spawn happens.
-- * @{#SPAWN.InitRandomizeUnits}(): Randomizes the @{Unit}s in the @{Group} that is spawned within a **radius band**, given an Outer and Inner radius.
-- * @{#SPAWN.InitRandomizeZones}(): Randomizes the spawning between a predefined list of @{Zone}s that are declared using this function. Each zone can be given a probability factor.
--
-- ### Enable / Disable AI when spawning a new @{Group}
--
-- * @{#SPAWN.InitAIOn}(): Turns the AI On when spawning the new @{Group} object.
-- * @{#SPAWN.InitAIOff}(): Turns the AI Off when spawning the new @{Group} object.
-- * @{#SPAWN.InitAIOnOff}(): Turns the AI On or Off when spawning the new @{Group} object.
--
-- ### Limit scheduled spawning
--
-- * @{#SPAWN.InitLimit}(): Limits the amount of groups that can be alive at the same time and that can be dynamically spawned.
--
-- ### Delay initial scheduled spawn
--
-- * @{#SPAWN.InitDelayOnOff}(): Turns the inital delay On/Off when scheduled spawning the first @{Group} object.
-- * @{#SPAWN.InitDelayOn}(): Turns the inital delay On when scheduled spawning the first @{Group} object.
-- * @{#SPAWN.InitDelayOff}(): Turns the inital delay Off when scheduled spawning the first @{Group} object.
--
-- ### Repeat spawned @{Group}s upon landing
--
-- * @{#SPAWN.InitRepeat}() or @{#SPAWN.InitRepeatOnLanding}(): This method is used to re-spawn automatically the same group after it has landed.
-- * @{#SPAWN.InitRepeatOnEngineShutDown}(): This method is used to re-spawn automatically the same group after it has landed and it shuts down the engines at the ramp.
--
--
-- ## SPAWN **Spawn** methods
--
-- Groups can be spawned at different times and methods:
--
-- ### **Single** spawning methods
--
-- * @{#SPAWN.Spawn}(): Spawn one new group based on the last spawned index.
-- * @{#SPAWN.ReSpawn}(): Re-spawn a group based on a given index.
-- * @{#SPAWN.SpawnFromVec3}(): Spawn a new group from a Vec3 coordinate. (The group will can be spawned at a point in the air).
-- * @{#SPAWN.SpawnFromVec2}(): Spawn a new group from a Vec2 coordinate. (The group will be spawned at land height ).
-- * @{#SPAWN.SpawnFromStatic}(): Spawn a new group from a structure, taking the position of a @{Static}.
-- * @{#SPAWN.SpawnFromUnit}(): Spawn a new group taking the position of a @{Unit}.
-- * @{#SPAWN.SpawnInZone}(): Spawn a new group in a @{Zone}.
--
-- Note that @{#SPAWN.Spawn} and @{#SPAWN.ReSpawn} return a @{GROUP#GROUP.New} object, that contains a reference to the DCSGroup object.
-- You can use the @{GROUP} object to do further actions with the DCSGroup.
--
-- ### **Scheduled** spawning methods
--
-- * @{#SPAWN.SpawnScheduled}(): Spawn groups at scheduled but randomized intervals.
-- * @{#SPAWN.SpawnScheduledStart}(): Start or continue to spawn groups at scheduled time intervals.
-- * @{#SPAWN.SpawnScheduledStop}(): Stop the spawning of groups at scheduled time intervals.
--
--
--
-- ## Retrieve alive GROUPs spawned by the SPAWN object
--
-- The SPAWN class administers which GROUPS it has reserved (in stock) or has created during mission execution.
-- Every time a SPAWN object spawns a new GROUP object, a reference to the GROUP object is added to an internal table of GROUPS.
-- SPAWN provides methods to iterate through that internal GROUP object reference table:
--
-- * @{#SPAWN.GetFirstAliveGroup}(): Will find the first alive GROUP it has spawned, and return the alive GROUP object and the first Index where the first alive GROUP object has been found.
-- * @{#SPAWN.GetNextAliveGroup}(): Will find the next alive GROUP object from a given Index, and return a reference to the alive GROUP object and the next Index where the alive GROUP has been found.
-- * @{#SPAWN.GetLastAliveGroup}(): Will find the last alive GROUP object, and will return a reference to the last live GROUP object and the last Index where the last alive GROUP object has been found.
--
-- You can use the methods @{#SPAWN.GetFirstAliveGroup}() and sequently @{#SPAWN.GetNextAliveGroup}() to iterate through the alive GROUPS within the SPAWN object, and to actions... See the respective methods for an example.
-- The method @{#SPAWN.GetGroupFromIndex}() will return the GROUP object reference from the given Index, dead or alive...
--
-- ## Spawned cleaning of inactive groups
--
-- Sometimes, it will occur during a mission run-time, that ground or especially air objects get damaged, and will while being damged stop their activities, while remaining alive.
-- In such cases, the SPAWN object will just sit there and wait until that group gets destroyed, but most of the time it won't,
-- and it may occur that no new groups are or can be spawned as limits are reached.
-- To prevent this, a @{#SPAWN.InitCleanUp}() initialization method has been defined that will silently monitor the status of each spawned group.
-- Once a group has a velocity = 0, and has been waiting for a defined interval, that group will be cleaned or removed from run-time.
-- There is a catch however :-) If a damaged group has returned to an airbase within the coalition, that group will not be considered as "lost"...
-- In such a case, when the inactive group is cleaned, a new group will Re-spawned automatically.
-- This models AI that has succesfully returned to their airbase, to restart their combat activities.
-- Check the @{#SPAWN.InitCleanUp}() for further info.
--
-- ## Catch the @{Group} Spawn Event in a callback function!
--
-- When using the @{#SPAWN.SpawnScheduled)() method, new @{Group}s are created following the spawn time interval parameters.
-- When a new @{Group} is spawned, you maybe want to execute actions with that group spawned at the spawn event.
-- The SPAWN class supports this functionality through the method @{#SPAWN.OnSpawnGroup}( **function( SpawnedGroup ) end ** ),
-- which takes a function as a parameter that you can define locally.
-- Whenever a new @{Group} is spawned, the given function is called, and the @{Group} that was just spawned, is given as a parameter.
-- As a result, your spawn event handling function requires one parameter to be declared, which will contain the spawned @{Group} object.
-- A coding example is provided at the description of the @{#SPAWN.OnSpawnGroup}( **function( SpawnedGroup ) end ** ) method.
--
-- ## Delay the initial spawning
--
-- When using the @{#SPAWN.SpawnScheduled)() method, the default behaviour of this method will be that it will spawn the initial (first) @{Group}
-- immediately when :SpawnScheduled() is initiated. The methods @{#SPAWN.InitDelayOnOff}() and @{#SPAWN.InitDelayOn}() can be used to
-- activate a delay before the first @{Group} is spawned. For completeness, a method @{#SPAWN.InitDelayOff}() is also available, that
-- can be used to switch off the initial delay. Because there is no delay by default, this method would only be used when a
-- @{#SPAWN.SpawnScheduledStop}() ; @{#SPAWN.SpawnScheduledStart}() sequence would have been used.
--
--
-- @field #SPAWN SPAWN
--
SPAWN = {
ClassName = "SPAWN",
SpawnTemplatePrefix = nil,
@@ -214,18 +290,23 @@ function SPAWN:New( SpawnTemplatePrefix )
self.SpawnTemplate = self._GetTemplate( self, SpawnTemplatePrefix ) -- Contains the template structure for a Group Spawn from the Mission Editor. Note that this group must have lateActivation always on!!!
self.Repeat = false -- Don't repeat the group from Take-Off till Landing and back Take-Off by ReSpawning.
self.UnControlled = false -- When working in UnControlled mode, all planes are Spawned in UnControlled mode before the scheduler starts.
self.SpawnInitLimit = false -- By default, no InitLimit
self.SpawnMaxUnitsAlive = 0 -- The maximum amount of groups that can be alive of SpawnTemplatePrefix at the same time.
self.SpawnMaxGroups = 0 -- The maximum amount of groups that can be spawned.
self.SpawnRandomize = false -- Sets the randomization flag of new Spawned units to false.
self.SpawnVisible = false -- Flag that indicates if all the Groups of the SpawnGroup need to be visible when Spawned.
self.AIOnOff = true -- The AI is on by default when spawning a group.
self.SpawnUnControlled = false
self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name.
self.DelayOnOff = false -- No intial delay when spawning the first group.
self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned.
else
error( "SPAWN:New: There is no group declared in the mission editor with SpawnTemplatePrefix = '" .. SpawnTemplatePrefix .. "'" )
end
self:SetEventPriority( 5 )
return self
end
@@ -253,18 +334,23 @@ function SPAWN:NewWithAlias( SpawnTemplatePrefix, SpawnAliasPrefix )
self.SpawnTemplate = self._GetTemplate( self, SpawnTemplatePrefix ) -- Contains the template structure for a Group Spawn from the Mission Editor. Note that this group must have lateActivation always on!!!
self.Repeat = false -- Don't repeat the group from Take-Off till Landing and back Take-Off by ReSpawning.
self.UnControlled = false -- When working in UnControlled mode, all planes are Spawned in UnControlled mode before the scheduler starts.
self.SpawnInitLimit = false -- By default, no InitLimit
self.SpawnMaxUnitsAlive = 0 -- The maximum amount of groups that can be alive of SpawnTemplatePrefix at the same time.
self.SpawnMaxGroups = 0 -- The maximum amount of groups that can be spawned.
self.SpawnRandomize = false -- Sets the randomization flag of new Spawned units to false.
self.SpawnVisible = false -- Flag that indicates if all the Groups of the SpawnGroup need to be visible when Spawned.
self.AIOnOff = true -- The AI is on by default when spawning a group.
self.SpawnUnControlled = false
self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name.
self.DelayOnOff = false -- No intial delay when spawning the first group.
self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned.
else
error( "SPAWN:New: There is no group declared in the mission editor with SpawnTemplatePrefix = '" .. SpawnTemplatePrefix .. "'" )
end
self:SetEventPriority( 5 )
return self
end
@@ -287,6 +373,7 @@ end
function SPAWN:InitLimit( SpawnMaxUnitsAlive, SpawnMaxGroups )
self:F( { self.SpawnTemplatePrefix, SpawnMaxUnitsAlive, SpawnMaxGroups } )
self.SpawnInitLimit = true
self.SpawnMaxUnitsAlive = SpawnMaxUnitsAlive -- The maximum amount of groups that can be alive of SpawnTemplatePrefix at the same time.
self.SpawnMaxGroups = SpawnMaxGroups -- The maximum amount of groups that can be spawned.
@@ -297,6 +384,20 @@ function SPAWN:InitLimit( SpawnMaxUnitsAlive, SpawnMaxGroups )
return self
end
--- Keeps the unit names as defined within the mission editor,
-- but note that anything after a # mark is ignored,
-- and any spaces before and after the resulting name are removed.
-- IMPORTANT! This method MUST be the first used after :New !!!
-- @param #SPAWN self
-- @return #SPAWN self
function SPAWN:InitKeepUnitNames()
self:F( )
self.SpawnInitKeepUnitNames = true
return self
end
--- Randomizes the defined route of the SpawnTemplatePrefix group in the ME. This is very useful to define extra variation of the behaviour of groups.
-- @param #SPAWN self
@@ -329,6 +430,27 @@ function SPAWN:InitRandomizeRoute( SpawnStartPoint, SpawnEndPoint, SpawnRadius,
return self
end
--- Randomizes the position of @{Group}s that are spawned within a **radius band**, given an Outer and Inner radius, from the point that the spawn happens.
-- @param #SPAWN self
-- @param #boolean RandomizePosition If true, SPAWN will perform the randomization of the @{Group}s position between a given outer and inner radius.
-- @param Dcs.DCSTypes#Distance OuterRadius (optional) The outer radius in meters where the new group will be spawned.
-- @param Dcs.DCSTypes#Distance InnerRadius (optional) The inner radius in meters where the new group will NOT be spawned.
-- @return #SPAWN
function SPAWN:InitRandomizePosition( RandomizePosition, OuterRadius, InnerRadius )
self:F( { self.SpawnTemplatePrefix, RandomizePosition, OuterRadius, InnerRadius } )
self.SpawnRandomizePosition = RandomizePosition or false
self.SpawnRandomizePositionOuterRadius = OuterRadius or 0
self.SpawnRandomizePositionInnerRadius = InnerRadius or 0
for GroupID = 1, self.SpawnMaxGroups do
self:_RandomizeRoute( GroupID )
end
return self
end
--- Randomizes the UNITs that are spawned within a radius band given an Outer and Inner radius.
-- @param #SPAWN self
-- @param #boolean RandomizeUnits If true, SPAWN will perform the randomization of the @{UNIT}s position within the group between a given outer and inner radius.
@@ -528,16 +650,15 @@ function SPAWN:InitArray( SpawnAngle, SpawnWidth, SpawnDeltaX, SpawnDeltaY )
self.SpawnGroups[SpawnGroupID].Visible = true
_EVENTDISPATCHER:OnBirthForTemplate( self.SpawnGroups[SpawnGroupID].SpawnTemplate, self._OnBirth, self )
_EVENTDISPATCHER:OnCrashForTemplate( self.SpawnGroups[SpawnGroupID].SpawnTemplate, self._OnDeadOrCrash, self )
_EVENTDISPATCHER:OnDeadForTemplate( self.SpawnGroups[SpawnGroupID].SpawnTemplate, self._OnDeadOrCrash, self )
self:HandleEvent( EVENTS.Birth, self._OnBirth )
self:HandleEvent( EVENTS.Dead, self._OnDeadOrCrash )
self:HandleEvent( EVENTS.Crash, self._OnDeadOrCrash )
if self.Repeat then
_EVENTDISPATCHER:OnTakeOffForTemplate( self.SpawnGroups[SpawnGroupID].SpawnTemplate, self._OnTakeOff, self )
_EVENTDISPATCHER:OnLandForTemplate( self.SpawnGroups[SpawnGroupID].SpawnTemplate, self._OnLand, self )
self:HandleEvent( EVENTS.Takeoff, self._OnTakeOff )
self:HandleEvent( EVENTS.Land, self._OnLand )
end
if self.RepeatOnEngineShutDown then
_EVENTDISPATCHER:OnEngineShutDownForTemplate( self.SpawnGroups[SpawnGroupID].SpawnTemplate, self._OnEngineShutDown, self )
self:HandleEvent( EVENTS.EngineShutdown, self._OnEngineShutDown )
end
self.SpawnGroups[SpawnGroupID].Group = _DATABASE:Spawn( self.SpawnGroups[SpawnGroupID].SpawnTemplate )
@@ -578,6 +699,36 @@ do -- AI methods
end -- AI methods
do -- Delay methods
--- Turns the Delay On or Off for the first @{Group} scheduled spawning.
-- The default value is that for scheduled spawning, there is an initial delay when spawning the first @{Group}.
-- @param #SPAWN self
-- @param #boolean DelayOnOff A value of true sets the Delay On, a value of false sets the Delay Off.
-- @return #SPAWN The SPAWN object
function SPAWN:InitDelayOnOff( DelayOnOff )
self.DelayOnOff = DelayOnOff
return self
end
--- Turns the Delay On for the @{Group} when spawning.
-- @param #SPAWN self
-- @return #SPAWN The SPAWN object
function SPAWN:InitDelayOn()
return self:InitDelayOnOff( true )
end
--- Turns the Delay Off for the @{Group} when spawning.
-- @param #SPAWN self
-- @return #SPAWN The SPAWN object
function SPAWN:InitDelayOff()
return self:InitDelayOnOff( false )
end
end -- Delay methods
--- Will spawn a group based on the internal index.
-- Note: Uses @{DATABASE} module defined in MOOSE.
-- @param #SPAWN self
@@ -621,6 +772,8 @@ function SPAWN:ReSpawn( SpawnIndex )
SpawnGroup:ReSpawnFunction()
end
SpawnGroup:ResetEvents()
return SpawnGroup
end
@@ -645,6 +798,20 @@ function SPAWN:SpawnWithIndex( SpawnIndex )
local PointVec3 = POINT_VEC3:New( SpawnTemplate.route.points[1].x, SpawnTemplate.route.points[1].alt, SpawnTemplate.route.points[1].y )
self:T( { "Current point of ", self.SpawnTemplatePrefix, PointVec3 } )
-- If RandomizePosition, then Randomize the formation in the zone band, keeping the template.
if self.SpawnRandomizePosition then
local RandomVec2 = PointVec3:GetRandomVec2InRadius( self.SpawnRandomizePositionOuterRadius, self.SpawnRandomizePositionInnerRadius )
local CurrentX = SpawnTemplate.units[1].x
local CurrentY = SpawnTemplate.units[1].y
SpawnTemplate.x = RandomVec2.x
SpawnTemplate.y = RandomVec2.y
for UnitID = 1, #SpawnTemplate.units do
SpawnTemplate.units[UnitID].x = SpawnTemplate.units[UnitID].x + ( RandomVec2.x - CurrentX )
SpawnTemplate.units[UnitID].y = SpawnTemplate.units[UnitID].y + ( RandomVec2.y - CurrentY )
self:T( 'SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y )
end
end
-- If RandomizeUnits, then Randomize the formation at the start point.
if self.SpawnRandomizeUnits then
@@ -663,18 +830,16 @@ function SPAWN:SpawnWithIndex( SpawnIndex )
end
end
_EVENTDISPATCHER:OnBirthForTemplate( SpawnTemplate, self._OnBirth, self )
_EVENTDISPATCHER:OnCrashForTemplate( SpawnTemplate, self._OnDeadOrCrash, self )
_EVENTDISPATCHER:OnDeadForTemplate( SpawnTemplate, self._OnDeadOrCrash, self )
self:HandleEvent( EVENTS.Birth, self._OnBirth )
self:HandleEvent( EVENTS.Dead, self._OnDeadOrCrash )
self:HandleEvent( EVENTS.Crash, self._OnDeadOrCrash )
if self.Repeat then
_EVENTDISPATCHER:OnTakeOffForTemplate( SpawnTemplate, self._OnTakeOff, self )
_EVENTDISPATCHER:OnLandForTemplate( SpawnTemplate, self._OnLand, self )
self:HandleEvent( EVENTS.Takeoff, self._OnTakeOff )
self:HandleEvent( EVENTS.Land, self._OnLand )
end
if self.RepeatOnEngineShutDown then
_EVENTDISPATCHER:OnEngineShutDownForTemplate( SpawnTemplate, self._OnEngineShutDown, self )
self:HandleEvent( EVENTS.EngineShutdown, self._OnEngineShutDown )
end
self:T3( SpawnTemplate.name )
self.SpawnGroups[self.SpawnIndex].Group = _DATABASE:Spawn( SpawnTemplate )
@@ -685,6 +850,8 @@ function SPAWN:SpawnWithIndex( SpawnIndex )
SpawnGroup:SetAIOnOff( self.AIOnOff )
end
self:T3( SpawnTemplate.name )
-- If there is a SpawnFunction hook defined, call it.
if self.SpawnFunctionHook then
@@ -725,7 +892,11 @@ function SPAWN:SpawnScheduled( SpawnTime, SpawnTimeVariation )
self:F( { SpawnTime, SpawnTimeVariation } )
if SpawnTime ~= nil and SpawnTimeVariation ~= nil then
self.SpawnScheduler = SCHEDULER:New( self, self._Scheduler, {}, 1, SpawnTime, SpawnTimeVariation )
local InitialDelay = 0
if self.DelayOnOff == true then
InitialDelay = math.random( SpawnTime - SpawnTime * SpawnTimeVariation, SpawnTime + SpawnTime * SpawnTimeVariation )
end
self.SpawnScheduler = SCHEDULER:New( self, self._Scheduler, {}, InitialDelay, SpawnTime, SpawnTimeVariation )
end
return self
@@ -733,17 +904,23 @@ end
--- Will re-start the spawning scheduler.
-- Note: This method is only required to be called when the schedule was stopped.
-- @param #SPAWN self
-- @return #SPAWN
function SPAWN:SpawnScheduleStart()
self:F( { self.SpawnTemplatePrefix } )
self.SpawnScheduler:Start()
return self
end
--- Will stop the scheduled spawning scheduler.
-- @param #SPAWN self
-- @return #SPAWN
function SPAWN:SpawnScheduleStop()
self:F( { self.SpawnTemplatePrefix } )
self.SpawnScheduler:Stop()
return self
end
@@ -864,7 +1041,7 @@ end
function SPAWN:SpawnFromUnit( HostUnit, SpawnIndex )
self:F( { self.SpawnTemplatePrefix, HostUnit, SpawnIndex } )
if HostUnit and HostUnit:IsAlive() then -- and HostUnit:getUnit(1):inAir() == false then
if HostUnit and HostUnit:IsAlive() ~= nil then -- and HostUnit:getUnit(1):inAir() == false then
return self:SpawnFromVec3( HostUnit:GetVec3(), SpawnIndex )
end
@@ -1056,27 +1233,6 @@ function SPAWN:GetGroupFromIndex( SpawnIndex )
end
end
--- Get the group index from a DCSUnit.
-- The method will search for a #-mark, and will return the index behind the #-mark of the DCSUnit.
-- It will return nil of no prefix was found.
-- @param #SPAWN self
-- @param Dcs.DCSWrapper.Unit#Unit DCSUnit The @{DCSUnit} to be searched.
-- @return #string The prefix
-- @return #nil Nothing found
function SPAWN:_GetGroupIndexFromDCSUnit( DCSUnit )
self:F3( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, DCSUnit } )
local SpawnUnitName = ( DCSUnit and DCSUnit:getName() ) or nil
if SpawnUnitName then
local IndexString = string.match( SpawnUnitName, "#.*-" ):sub( 2, -2 )
if IndexString then
local Index = tonumber( IndexString )
return Index
end
end
return nil
end
--- Return the prefix of a SpawnUnit.
-- The method will search for a #-mark, and will return the text before the #-mark.
@@ -1085,48 +1241,28 @@ end
-- @param Dcs.DCSWrapper.Unit#UNIT DCSUnit The @{DCSUnit} to be searched.
-- @return #string The prefix
-- @return #nil Nothing found
function SPAWN:_GetPrefixFromDCSUnit( DCSUnit )
self:F3( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, DCSUnit } )
function SPAWN:_GetPrefixFromGroup( SpawnGroup )
self:F3( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnGroup } )
local DCSUnitName = ( DCSUnit and DCSUnit:getName() ) or nil
if DCSUnitName then
local SpawnPrefix = string.match( DCSUnitName, ".*#" )
if SpawnPrefix then
SpawnPrefix = SpawnPrefix:sub( 1, -2 )
end
return SpawnPrefix
end
return nil
end
--- Return the group within the SpawnGroups collection with input a DCSUnit.
-- @param #SPAWN self
-- @param Dcs.DCSWrapper.Unit#Unit DCSUnit The @{DCSUnit} to be searched.
-- @return Wrapper.Group#GROUP The Group
-- @return #nil Nothing found
function SPAWN:_GetGroupFromDCSUnit( DCSUnit )
self:F3( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, DCSUnit } )
local SpawnPrefix = self:_GetPrefixFromDCSUnit( DCSUnit )
if self.SpawnTemplatePrefix == SpawnPrefix or ( self.SpawnAliasPrefix and self.SpawnAliasPrefix == SpawnPrefix ) then
local SpawnGroupIndex = self:_GetGroupIndexFromDCSUnit( DCSUnit )
local SpawnGroup = self.SpawnGroups[SpawnGroupIndex].Group
self:T( SpawnGroup )
return SpawnGroup
end
return nil
local GroupName = SpawnGroup:GetName()
if GroupName then
local SpawnPrefix = string.match( GroupName, ".*#" )
if SpawnPrefix then
SpawnPrefix = SpawnPrefix:sub( 1, -2 )
end
return SpawnPrefix
end
return nil
end
--- Get the index from a given group.
-- The function will search the name of the group for a #, and will return the number behind the #-mark.
function SPAWN:GetSpawnIndexFromGroup( SpawnGroup )
self:F3( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnGroup } )
self:F( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnGroup } )
local IndexString = string.match( SpawnGroup:GetName(), "#.*$" ):sub( 2 )
local IndexString = string.match( SpawnGroup:GetName(), "#(%d*)$" ):sub( 2 )
local Index = tonumber( IndexString )
self:T3( IndexString, Index )
@@ -1142,6 +1278,7 @@ function SPAWN:_GetLastIndex()
end
--- Initalize the SpawnGroups collection.
-- @param #SPAWN self
function SPAWN:_InitializeSpawnGroups( SpawnIndex )
self:F3( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnIndex } )
@@ -1245,11 +1382,20 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex )
SpawnTemplate.visible = false
end
for UnitID = 1, #SpawnTemplate.units do
SpawnTemplate.units[UnitID].name = string.format( SpawnTemplate.name .. '-%02d', UnitID )
SpawnTemplate.units[UnitID].unitId = nil
end
if self.SpawnInitKeepUnitNames == false then
for UnitID = 1, #SpawnTemplate.units do
SpawnTemplate.units[UnitID].name = string.format( SpawnTemplate.name .. '-%02d', UnitID )
SpawnTemplate.units[UnitID].unitId = nil
end
else
for UnitID = 1, #SpawnTemplate.units do
local UnitPrefix, Rest = string.match( SpawnTemplate.units[UnitID].name, "^([^#]+)#?" ):gsub( "^%s*(.-)%s*$", "%1" )
self:T( { UnitPrefix, Rest } )
SpawnTemplate.units[UnitID].name = string.format( '%s#%03d-%02d', UnitPrefix, SpawnIndex, UnitID )
SpawnTemplate.units[UnitID].unitId = nil
end
end
self:T3( { "Template:", SpawnTemplate } )
return SpawnTemplate
@@ -1441,18 +1587,21 @@ end
-- TODO Need to delete this... _DATABASE does this now ...
--- @param #SPAWN self
-- @param Core.Event#EVENTDATA Event
function SPAWN:_OnBirth( Event )
-- @param Core.Event#EVENTDATA EventData
function SPAWN:_OnBirth( EventData )
self:F( self.SpawnTemplatePrefix )
if timer.getTime0() < timer.getAbsTime() then
if Event.IniDCSUnit then
local EventPrefix = self:_GetPrefixFromDCSUnit( Event.IniDCSUnit )
self:T( { "Birth Event:", EventPrefix, self.SpawnTemplatePrefix } )
if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then
self.AliveUnits = self.AliveUnits + 1
self:T( "Alive Units: " .. self.AliveUnits )
end
end
local SpawnGroup = EventData.IniGroup
if SpawnGroup then
local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup )
if EventPrefix then -- EventPrefix can be nil if no # is found, which means, no spawnable group!
self:T( { "Birth Event:", EventPrefix, self.SpawnTemplatePrefix } )
if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then
self.AliveUnits = self.AliveUnits + 1
self:T( "Alive Units: " .. self.AliveUnits )
end
end
end
end
@@ -1461,55 +1610,66 @@ end
-- @todo Need to delete this... _DATABASE does this now ...
--- @param #SPAWN self
-- @param Core.Event#EVENTDATA Event
function SPAWN:_OnDeadOrCrash( Event )
self:F( self.SpawnTemplatePrefix, Event )
-- @param Core.Event#EVENTDATA EventData
function SPAWN:_OnDeadOrCrash( EventData )
self:F( self.SpawnTemplatePrefix )
if Event.IniDCSUnit then
local EventPrefix = self:_GetPrefixFromDCSUnit( Event.IniDCSUnit )
self:T( { "Dead event: " .. EventPrefix, self.SpawnTemplatePrefix } )
if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then
self.AliveUnits = self.AliveUnits - 1
self:T( "Alive Units: " .. self.AliveUnits )
end
local SpawnGroup = EventData.IniGroup
if SpawnGroup then
local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup )
if EventPrefix then -- EventPrefix can be nil if no # is found, which means, no spawnable group!
self:T( { "Dead event: " .. EventPrefix } )
if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then
self.AliveUnits = self.AliveUnits - 1
self:T( "Alive Units: " .. self.AliveUnits )
end
end
end
end
--- Will detect AIR Units taking off... When the event takes place, the spawned Group is registered as airborne...
-- This is needed to ensure that Re-SPAWNing only is done for landed AIR Groups.
-- @todo Need to test for AIR Groups only...
function SPAWN:_OnTakeOff( event )
self:F( self.SpawnTemplatePrefix, event )
-- @param #SPAWN self
-- @param Core.Event#EVENTDATA EventData
function SPAWN:_OnTakeOff( EventData )
self:F( self.SpawnTemplatePrefix )
if event.initiator and event.initiator:getName() then
local SpawnGroup = self:_GetGroupFromDCSUnit( event.initiator )
if SpawnGroup then
self:T( { "TakeOff event: " .. event.initiator:getName(), event } )
self:T( "self.Landed = false" )
self.Landed = false
end
local SpawnGroup = EventData.IniGroup
if SpawnGroup then
local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup )
if EventPrefix then -- EventPrefix can be nil if no # is found, which means, no spawnable group!
self:T( { "TakeOff event: " .. EventPrefix } )
if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then
self:T( "self.Landed = false" )
SpawnGroup:SetState( SpawnGroup, "Spawn_Landed", false )
end
end
end
end
--- Will detect AIR Units landing... When the event takes place, the spawned Group is registered as landed.
-- This is needed to ensure that Re-SPAWNing is only done for landed AIR Groups.
-- @todo Need to test for AIR Groups only...
function SPAWN:_OnLand( event )
self:F( self.SpawnTemplatePrefix, event )
-- @param #SPAWN self
-- @param Core.Event#EVENTDATA EventData
function SPAWN:_OnLand( EventData )
self:F( self.SpawnTemplatePrefix )
local SpawnUnit = event.initiator
if SpawnUnit and SpawnUnit:isExist() and Object.getCategory(SpawnUnit) == Object.Category.UNIT then
local SpawnGroup = self:_GetGroupFromDCSUnit( SpawnUnit )
if SpawnGroup then
self:T( { "Landed event:" .. SpawnUnit:getName(), event } )
self.Landed = true
self:T( "self.Landed = true" )
if self.Landed and self.RepeatOnLanding then
local SpawnGroupIndex = self:GetSpawnIndexFromGroup( SpawnGroup )
self:T( { "Landed:", "ReSpawn:", SpawnGroup:GetName(), SpawnGroupIndex } )
self:ReSpawn( SpawnGroupIndex )
end
end
local SpawnGroup = EventData.IniGroup
if SpawnGroup then
local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup )
if EventPrefix then -- EventPrefix can be nil if no # is found, which means, no spawnable group!
self:T( { "Land event: " .. EventPrefix } )
if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then
-- TODO: Check if this is the last unit of the group that lands.
SpawnGroup:SetState( SpawnGroup, "Spawn_Landed", true )
if self.RepeatOnLanding then
local SpawnGroupIndex = self:GetSpawnIndexFromGroup( SpawnGroup )
self:T( { "Landed:", "ReSpawn:", SpawnGroup:GetName(), SpawnGroupIndex } )
self:ReSpawn( SpawnGroupIndex )
end
end
end
end
end
@@ -1517,23 +1677,25 @@ end
-- When the event takes place, and the method @{RepeatOnEngineShutDown} was called, the spawned Group will Re-SPAWN.
-- But only when the Unit was registered to have landed.
-- @param #SPAWN self
-- @see _OnTakeOff
-- @see _OnLand
-- @todo Need to test for AIR Groups only...
function SPAWN:_OnEngineShutDown( event )
self:F( self.SpawnTemplatePrefix, event )
-- @param Core.Event#EVENTDATA EventData
function SPAWN:_OnEngineShutDown( EventData )
self:F( self.SpawnTemplatePrefix )
local SpawnUnit = event.initiator
if SpawnUnit and SpawnUnit:isExist() and Object.getCategory(SpawnUnit) == Object.Category.UNIT then
local SpawnGroup = self:_GetGroupFromDCSUnit( SpawnUnit )
if SpawnGroup then
self:T( { "EngineShutDown event: " .. SpawnUnit:getName(), event } )
if self.Landed and self.RepeatOnEngineShutDown then
local SpawnGroupIndex = self:GetSpawnIndexFromGroup( SpawnGroup )
self:T( { "EngineShutDown: ", "ReSpawn:", SpawnGroup:GetName(), SpawnGroupIndex } )
self:ReSpawn( SpawnGroupIndex )
end
end
local SpawnGroup = EventData.IniGroup
if SpawnGroup then
local EventPrefix = self:_GetPrefixFromGroup( SpawnGroup )
if EventPrefix then -- EventPrefix can be nil if no # is found, which means, no spawnable group!
self:T( { "EngineShutdown event: " .. EventPrefix } )
if EventPrefix == self.SpawnTemplatePrefix or ( self.SpawnAliasPrefix and EventPrefix == self.SpawnAliasPrefix ) then
-- todo: test if on the runway
local Landed = SpawnGroup:GetState( SpawnGroup, "Spawn_Landed" )
if Landed and self.RepeatOnEngineShutDown then
local SpawnGroupIndex = self:GetSpawnIndexFromGroup( SpawnGroup )
self:T( { "EngineShutDown: ", "ReSpawn:", SpawnGroup:GetName(), SpawnGroupIndex } )
self:ReSpawn( SpawnGroupIndex )
end
end
end
end
end

View File

@@ -1,67 +1,3 @@
--- The main include file for the MOOSE system.
--- Core Routines
Include.File( "Utilities/Routines" )
Include.File( "Utilities/Utils" )
--- Core Classes
Include.File( "Core/Base" )
Include.File( "Core/Scheduler" )
Include.File( "Core/ScheduleDispatcher")
Include.File( "Core/Event" )
Include.File( "Core/Menu" )
Include.File( "Core/Zone" )
Include.File( "Core/Database" )
Include.File( "Core/Set" )
Include.File( "Core/Point" )
Include.File( "Core/Message" )
Include.File( "Core/Fsm" )
--- Wrapper Classes
Include.File( "Wrapper/Object" )
Include.File( "Wrapper/Identifiable" )
Include.File( "Wrapper/Positionable" )
Include.File( "Wrapper/Controllable" )
Include.File( "Wrapper/Group" )
Include.File( "Wrapper/Unit" )
Include.File( "Wrapper/Client" )
Include.File( "Wrapper/Static" )
Include.File( "Wrapper/Airbase" )
Include.File( "Wrapper/Scenery" )
--- Functional Classes
Include.File( "Functional/Scoring" )
Include.File( "Functional/CleanUp" )
Include.File( "Functional/Spawn" )
Include.File( "Functional/Movement" )
Include.File( "Functional/Sead" )
Include.File( "Functional/Escort" )
Include.File( "Functional/MissileTrainer" )
Include.File( "Functional/AirbasePolice" )
Include.File( "Functional/Detection" )
--- AI Classes
Include.File( "AI/AI_Balancer" )
Include.File( "AI/AI_Patrol" )
Include.File( "AI/AI_Cap" )
Include.File( "AI/AI_Cas" )
Include.File( "AI/AI_Cargo" )
--- Actions
Include.File( "Actions/Act_Assign" )
Include.File( "Actions/Act_Route" )
Include.File( "Actions/Act_Account" )
Include.File( "Actions/Act_Assist" )
--- Task Handling Classes
Include.File( "Tasking/CommandCenter" )
Include.File( "Tasking/Mission" )
Include.File( "Tasking/Task" )
Include.File( "Tasking/DetectionManager" )
Include.File( "Tasking/Task_SEAD" )
Include.File( "Tasking/Task_A2G" )
-- The order of the declarations is important here. Don't touch it.
--- Declare the event dispatcher based on the EVENT class
@@ -73,5 +9,4 @@ _SCHEDULEDISPATCHER = SCHEDULEDISPATCHER:New() -- Core.Timer#SCHEDULEDISPATCHER
--- Declare the main database object, which is used internally by the MOOSE classes.
_DATABASE = DATABASE:New() -- Database#DATABASE
--COORDINATE:CoordinateMenu()

View File

@@ -9,6 +9,7 @@
-- @extends Core.Base#BASE
REPORT = {
ClassName = "REPORT",
Title = "",
}
--- Create a new REPORT.
@@ -17,25 +18,63 @@ REPORT = {
-- @return #REPORT
function REPORT:New( Title )
local self = BASE:Inherit( self, BASE:New() )
local self = BASE:Inherit( self, BASE:New() ) -- #REPORT
self.Report = {}
self.Report[#self.Report+1] = Title
Title = Title or ""
if Title then
self.Title = Title
end
return self
end
--- Has the REPORT Text?
-- @param #REPORT self
-- @return #boolean
function REPORT:HasText() --R2.1
return #self.Report > 0
end
--- Set indent of a REPORT.
-- @param #REPORT self
-- @param #number Indent
-- @return #REPORT
function REPORT:SetIndent( Indent ) --R2.1
self.Indent = Indent
return self
end
--- Add a new line to a REPORT.
-- @param #REPORT self
-- @param #string Text
-- @return #REPORT
function REPORT:Add( Text )
self.Report[#self.Report+1] = Text
return self.Report[#self.Report+1]
return self
end
function REPORT:Text()
return table.concat( self.Report, "\n" )
--- Add a new line to a REPORT.
-- @param #REPORT self
-- @param #string Text
-- @return #REPORT
function REPORT:AddIndent( Text ) --R2.1
self.Report[#self.Report+1] = string.rep(" ", self.Indent ) .. Text:gsub("\n","\n"..string.rep( " ", self.Indent ) )
return self
end
--- Produces the text of the report, taking into account an optional delimeter, which is \n by default.
-- @param #REPORT self
-- @param #string Delimiter (optional) A delimiter text.
-- @return #string The report text.
function REPORT:Text( Delimiter )
Delimiter = Delimiter or "\n"
local ReportText = ( self.Title ~= "" and self.Title .. Delimiter or self.Title ) .. table.concat( self.Report, Delimiter ) or ""
return ReportText
end
--- The COMMANDCENTER class
@@ -68,22 +107,23 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName )
self:HandleEvent( EVENTS.Birth,
--- @param #COMMANDCENTER self
--- @param Core.Event#EVENTDATA EventData
-- @param Core.Event#EVENTDATA EventData
function( self, EventData )
self:E( { EventData } )
local EventGroup = GROUP:Find( EventData.IniDCSGroup )
if EventGroup and self:HasGroup( EventGroup ) then
local MenuReporting = MENU_GROUP:New( EventGroup, "Reporting", self.CommandCenterMenu )
local MenuMissionsSummary = MENU_GROUP_COMMAND:New( EventGroup, "Missions Summary Report", MenuReporting, self.ReportSummary, self, EventGroup )
local MenuMissionsDetails = MENU_GROUP_COMMAND:New( EventGroup, "Missions Details Report", MenuReporting, self.ReportDetails, self, EventGroup )
self:ReportSummary( EventGroup )
end
local PlayerUnit = EventData.IniUnit
for MissionID, Mission in pairs( self:GetMissions() ) do
local Mission = Mission -- Tasking.Mission#MISSION
local PlayerGroup = EventData.IniGroup -- The GROUP object should be filled!
Mission:JoinUnit( PlayerUnit, PlayerGroup )
Mission:ReportDetails()
if EventData.IniObjectCategory == 1 then
local EventGroup = GROUP:Find( EventData.IniDCSGroup )
if EventGroup and self:HasGroup( EventGroup ) then
local MenuReporting = MENU_GROUP:New( EventGroup, "Missions Reports", self.CommandCenterMenu )
local MenuMissionsSummary = MENU_GROUP_COMMAND:New( EventGroup, "Missions Status Report", MenuReporting, self.ReportMissionsStatus, self, EventGroup )
local MenuMissionsDetails = MENU_GROUP_COMMAND:New( EventGroup, "Missions Players Report", MenuReporting, self.ReportMissionsPlayers, self, EventGroup )
self:ReportSummary( EventGroup )
end
local PlayerUnit = EventData.IniUnit
for MissionID, Mission in pairs( self:GetMissions() ) do
local Mission = Mission -- Tasking.Mission#MISSION
local PlayerGroup = EventData.IniGroup -- The GROUP object should be filled!
Mission:JoinUnit( PlayerUnit, PlayerGroup )
Mission:ReportDetails()
end
end
end
@@ -108,6 +148,21 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName )
end
)
-- Handle when a player leaves a slot and goes back to spectators ...
-- The PlayerUnit will be UnAssigned from the Task.
-- When there is no Unit left running the Task, the Task goes into Abort...
self:HandleEvent( EVENTS.MissionEnd,
--- @param #TASK self
-- @param Core.Event#EVENTDATA EventData
function( self, EventData )
local PlayerUnit = EventData.IniUnit
for MissionID, Mission in pairs( self:GetMissions() ) do
local Mission = Mission -- Tasking.Mission#MISSION
Mission:Stop()
end
end
)
-- Handle when a player leaves a slot and goes back to spectators ...
-- The PlayerUnit will be UnAssigned from the Task.
-- When there is no Unit left running the Task, the Task goes into Abort...
@@ -118,7 +173,9 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName )
local PlayerUnit = EventData.IniUnit
for MissionID, Mission in pairs( self:GetMissions() ) do
local Mission = Mission -- Tasking.Mission#MISSION
Mission:AbortUnit( PlayerUnit )
if Mission:IsENGAGED() then
Mission:AbortUnit( PlayerUnit )
end
end
end
)
@@ -132,10 +189,15 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName )
function( self, EventData )
local PlayerUnit = EventData.IniUnit
for MissionID, Mission in pairs( self:GetMissions() ) do
Mission:CrashUnit( PlayerUnit )
local Mission = Mission -- Tasking.Mission#MISSION
if Mission:IsENGAGED() then
Mission:CrashUnit( PlayerUnit )
end
end
end
)
self:SetMenu()
return self
end
@@ -193,17 +255,25 @@ function COMMANDCENTER:SetMenu()
self.CommandCenterMenu = self.CommandCenterMenu or MENU_COALITION:New( self.CommandCenterCoalition, "Command Center (" .. self:GetName() .. ")" )
for MissionID, Mission in pairs( self:GetMissions() ) do
local MenuTime = timer.getTime()
for MissionID, Mission in pairs( self:GetMissions() or {} ) do
local Mission = Mission -- Tasking.Mission#MISSION
Mission:RemoveMenu()
Mission:SetMenu( MenuTime )
end
for MissionID, Mission in pairs( self:GetMissions() or {} ) do
local Mission = Mission -- Tasking.Mission#MISSION
Mission:RemoveMenu( MenuTime )
end
for MissionID, Mission in pairs( self:GetMissions() ) do
local Mission = Mission -- Tasking.Mission#MISSION
Mission:SetMenu()
end
end
--- Gets the commandcenter menu structure governed by the HQ command center.
-- @param #COMMANDCENTER self
-- @return Core.Menu#MENU_COALITION
function COMMANDCENTER:GetMenu()
return self.CommandCenterMenu
end
--- Checks of the COMMANDCENTER has a GROUP.
-- @param #COMMANDCENTER self
@@ -224,16 +294,22 @@ function COMMANDCENTER:HasGroup( MissionGroup )
return Has
end
--- Send a CC message to the coalition of the CC.
-- @param #COMMANDCENTER self
function COMMANDCENTER:MessageToAll( Message )
self:GetPositionable():MessageToAll( Message, 20, self:GetName() )
end
--- Send a CC message to a GROUP.
-- @param #COMMANDCENTER self
-- @param #string Message
-- @param Wrapper.Group#GROUP TaskGroup
-- @param #sring Name (optional) The name of the Group used as a prefix for the message to the Group. If not provided, there will be nothing shown.
function COMMANDCENTER:MessageToGroup( Message, TaskGroup, Name )
function COMMANDCENTER:MessageToGroup( Message, TaskGroup )
local Prefix = Name and "@ Group (" .. Name .. "): " or ''
Message = Prefix .. Message
self:GetPositionable():MessageToGroup( Message , 20, TaskGroup, self:GetName() )
self:GetPositionable():MessageToGroup( Message , 15, TaskGroup, self:GetName() )
end
@@ -243,25 +319,46 @@ function COMMANDCENTER:MessageToCoalition( Message )
local CCCoalition = self:GetPositionable():GetCoalition()
--TODO: Fix coalition bug!
self:GetPositionable():MessageToCoalition( Message, 20, CCCoalition, self:GetName() )
self:GetPositionable():MessageToCoalition( Message, 15, CCCoalition )
end
--- Report the status of all MISSIONs to a GROUP.
-- Each Mission is listed, with an indication how many Tasks are still to be completed.
-- @param #COMMANDCENTER self
function COMMANDCENTER:ReportSummary( ReportGroup )
function COMMANDCENTER:ReportMissionsStatus( ReportGroup )
self:E( ReportGroup )
local Report = REPORT:New()
Report:Add( "Status report of all missions." )
for MissionID, Mission in pairs( self.Missions ) do
local Mission = Mission -- Tasking.Mission#MISSION
Report:Add( " - " .. Mission:ReportStatus() )
end
self:MessageToGroup( Report:Text(), ReportGroup )
end
--- Report the players of all MISSIONs to a GROUP.
-- Each Mission is listed, with an indication how many Tasks are still to be completed.
-- @param #COMMANDCENTER self
function COMMANDCENTER:ReportMissionsPlayers( ReportGroup )
self:E( ReportGroup )
local Report = REPORT:New()
Report:Add( "Players active in all missions." )
for MissionID, Mission in pairs( self.Missions ) do
local Mission = Mission -- Tasking.Mission#MISSION
Report:Add( " - " .. Mission:ReportOverview() )
Report:Add( " - " .. Mission:ReportPlayers() )
end
self:GetPositionable():MessageToGroup( Report:Text(), 30, ReportGroup )
self:MessageToGroup( Report:Text(), ReportGroup )
end
--- Report the status of a Task to a Group.
@@ -277,6 +374,6 @@ function COMMANDCENTER:ReportDetails( ReportGroup, Task )
Report:Add( " - " .. Mission:ReportDetails() )
end
self:GetPositionable():MessageToGroup( Report:Text(), 30, ReportGroup )
self:MessageToGroup( Report:Text(), ReportGroup )
end

View File

@@ -2,7 +2,7 @@
--
-- ===
--
-- 1) @{DetectionManager#DETECTION_MANAGER} class, extends @{Base#BASE}
-- 1) @{DetectionManager#DETECTION_MANAGER} class, extends @{Fsm#FSM}
-- ====================================================================
-- The @{DetectionManager#DETECTION_MANAGER} class defines the core functions to report detected objects to groups.
-- Reportings can be done in several manners, and it is up to the derived classes if DETECTION_MANAGER to model the reporting behaviour.
@@ -34,23 +34,6 @@
-- -------------------------------
-- The @{DetectionManager#DETECTION_REPORTING.New}() method creates a new DETECTION_REPORTING instance.
--
-- ===
--
-- 3) @{#DETECTION_DISPATCHER} class, extends @{#DETECTION_MANAGER}
-- ================================================================
-- The @{#DETECTION_DISPATCHER} class implements the dynamic dispatching of tasks upon groups of detected units determined a @{Set} of FAC (groups).
-- The FAC will detect units, will group them, and will dispatch @{Task}s to groups. Depending on the type of target detected, different tasks will be dispatched.
-- Find a summary below describing for which situation a task type is created:
--
-- * **CAS Task**: Is created when there are enemy ground units within range of the FAC, while there are friendly units in the FAC perimeter.
-- * **BAI Task**: Is created when there are enemy ground units within range of the FAC, while there are NO other friendly units within the FAC perimeter.
-- * **SEAD Task**: Is created when there are enemy ground units wihtin range of the FAC, with air search radars.
--
-- Other task types will follow...
--
-- 3.1) DETECTION_DISPATCHER constructor:
-- --------------------------------------
-- The @{#DETECTION_DISPATCHER.New}() method creates a new DETECTION_DISPATCHER instance.
--
-- ===
--
@@ -65,7 +48,7 @@ do -- DETECTION MANAGER
-- @type DETECTION_MANAGER
-- @field Set#SET_GROUP SetGroup The groups to which the FAC will report to.
-- @field Functional.Detection#DETECTION_BASE Detection The DETECTION_BASE object that is used to report the detected objects.
-- @extends Base#BASE
-- @extends Core.Fsm#FSM
DETECTION_MANAGER = {
ClassName = "DETECTION_MANAGER",
SetGroup = nil,
@@ -80,17 +63,38 @@ do -- DETECTION MANAGER
function DETECTION_MANAGER:New( SetGroup, Detection )
-- Inherits from BASE
local self = BASE:Inherit( self, BASE:New() ) -- Functional.Detection#DETECTION_MANAGER
local self = BASE:Inherit( self, FSM:New() ) -- #DETECTION_MANAGER
self.SetGroup = SetGroup
self.Detection = Detection
self:SetStartState( "Stopped" )
self:AddTransition( "Stopped", "Start", "Started" )
self:AddTransition( "Started", "Stop", "Stopped" )
self:AddTransition( "Started", "Report", "Started" )
self:SetReportInterval( 30 )
self:SetReportDisplayTime( 25 )
self:E( { Detection = Detection } )
Detection:__Start( 1 )
return self
end
function DETECTION_MANAGER:onafterStart( From, Event, To )
self:Report()
end
function DETECTION_MANAGER:onafterReport( From, Event, To )
self:E( "onafterReport" )
self:__Report( -self._ReportInterval )
self:ProcessDetected( self.Detection )
end
--- Set the reporting time interval.
-- @param #DETECTION_MANAGER self
-- @param #number ReportInterval The interval in seconds when a report needs to be done.
@@ -121,51 +125,14 @@ do -- DETECTION MANAGER
return self._ReportDisplayTime
end
--- Reports the detected items to the @{Set#SET_GROUP}.
-- @param #DETECTION_MANAGER self
-- @param Functional.Detection#DETECTION_BASE Detection
-- @return #DETECTION_MANAGER self
function DETECTION_MANAGER:ReportDetected( Detection )
self:F2()
function DETECTION_MANAGER:ProcessDetected( Detection )
self:E()
end
--- Schedule the FAC reporting.
-- @param #DETECTION_MANAGER self
-- @param #number DelayTime The delay in seconds to wait the reporting.
-- @param #number ReportInterval The repeat interval in seconds for the reporting to happen repeatedly.
-- @return #DETECTION_MANAGER self
function DETECTION_MANAGER:Schedule( DelayTime, ReportInterval )
self:F2()
self._ScheduleDelayTime = DelayTime
self:SetReportInterval( ReportInterval )
self.FacScheduler = SCHEDULER:New(self, self._FacScheduler, { self, "DetectionManager" }, self._ScheduleDelayTime, self._ReportInterval )
return self
end
--- Report the detected @{Unit#UNIT}s detected within the @{Detection#DETECTION_BASE} object to the @{Set#SET_GROUP}s.
-- @param #DETECTION_MANAGER self
function DETECTION_MANAGER:_FacScheduler( SchedulerName )
self:F2( { SchedulerName } )
return self:ProcessDetected( self.Detection )
-- self.SetGroup:ForEachGroup(
-- --- @param Wrapper.Group#GROUP Group
-- function( Group )
-- if Group:IsAlive() then
-- return self:ProcessDetected( self.Detection )
-- end
-- end
-- )
-- return true
end
end
@@ -250,259 +217,3 @@ do -- DETECTION_REPORTING
end
do -- DETECTION_DISPATCHER
--- DETECTION_DISPATCHER class.
-- @type DETECTION_DISPATCHER
-- @field Set#SET_GROUP SetGroup The groups to which the FAC will report to.
-- @field Functional.Detection#DETECTION_BASE Detection The DETECTION_BASE object that is used to report the detected objects.
-- @field Tasking.Mission#MISSION Mission
-- @field Wrapper.Group#GROUP CommandCenter
-- @extends Tasking.DetectionManager#DETECTION_MANAGER
DETECTION_DISPATCHER = {
ClassName = "DETECTION_DISPATCHER",
Mission = nil,
CommandCenter = nil,
Detection = nil,
}
--- DETECTION_DISPATCHER constructor.
-- @param #DETECTION_DISPATCHER self
-- @param Set#SET_GROUP SetGroup
-- @param Functional.Detection#DETECTION_BASE Detection
-- @return #DETECTION_DISPATCHER self
function DETECTION_DISPATCHER:New( Mission, CommandCenter, SetGroup, Detection )
-- Inherits from DETECTION_MANAGER
local self = BASE:Inherit( self, DETECTION_MANAGER:New( SetGroup, Detection ) ) -- #DETECTION_DISPATCHER
self.Detection = Detection
self.CommandCenter = CommandCenter
self.Mission = Mission
self:Schedule( 30 )
return self
end
--- Creates a SEAD task when there are targets for it.
-- @param #DETECTION_DISPATCHER self
-- @param Functional.Detection#DETECTION_AREAS.DetectedArea DetectedArea
-- @return Set#SET_UNIT TargetSetUnit: The target set of units.
-- @return #nil If there are no targets to be set.
function DETECTION_DISPATCHER:EvaluateSEAD( DetectedArea )
self:F( { DetectedArea.AreaID } )
local DetectedSet = DetectedArea.Set
local DetectedZone = DetectedArea.Zone
-- Determine if the set has radar targets. If it does, construct a SEAD task.
local RadarCount = DetectedSet:HasSEAD()
if RadarCount > 0 then
-- Here we're doing something advanced... We're copying the DetectedSet, but making a new Set only with SEADable Radar units in it.
local TargetSetUnit = SET_UNIT:New()
TargetSetUnit:SetDatabase( DetectedSet )
TargetSetUnit:FilterHasSEAD()
TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection.
return TargetSetUnit
end
return nil
end
--- Creates a CAS task when there are targets for it.
-- @param #DETECTION_DISPATCHER self
-- @param Functional.Detection#DETECTION_AREAS.DetectedArea DetectedArea
-- @return Tasking.Task#TASK
function DETECTION_DISPATCHER:EvaluateCAS( DetectedArea )
self:F( { DetectedArea.AreaID } )
local DetectedSet = DetectedArea.Set
local DetectedZone = DetectedArea.Zone
-- Determine if the set has radar targets. If it does, construct a SEAD task.
local GroundUnitCount = DetectedSet:HasGroundUnits()
local FriendliesNearBy = self.Detection:IsFriendliesNearBy( DetectedArea )
if GroundUnitCount > 0 and FriendliesNearBy == true then
-- Copy the Set
local TargetSetUnit = SET_UNIT:New()
TargetSetUnit:SetDatabase( DetectedSet )
TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection.
return TargetSetUnit
end
return nil
end
--- Creates a BAI task when there are targets for it.
-- @param #DETECTION_DISPATCHER self
-- @param Functional.Detection#DETECTION_AREAS.DetectedArea DetectedArea
-- @return Tasking.Task#TASK
function DETECTION_DISPATCHER:EvaluateBAI( DetectedArea, FriendlyCoalition )
self:F( { DetectedArea.AreaID } )
local DetectedSet = DetectedArea.Set
local DetectedZone = DetectedArea.Zone
-- Determine if the set has radar targets. If it does, construct a SEAD task.
local GroundUnitCount = DetectedSet:HasGroundUnits()
local FriendliesNearBy = self.Detection:IsFriendliesNearBy( DetectedArea )
if GroundUnitCount > 0 and FriendliesNearBy == false then
-- Copy the Set
local TargetSetUnit = SET_UNIT:New()
TargetSetUnit:SetDatabase( DetectedSet )
TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection.
return TargetSetUnit
end
return nil
end
--- Evaluates the removal of the Task from the Mission.
-- Can only occur when the DetectedArea is Changed AND the state of the Task is "Planned".
-- @param #DETECTION_DISPATCHER self
-- @param Tasking.Mission#MISSION Mission
-- @param Tasking.Task#TASK Task
-- @param Functional.Detection#DETECTION_AREAS.DetectedArea DetectedArea
-- @return Tasking.Task#TASK
function DETECTION_DISPATCHER:EvaluateRemoveTask( Mission, Task, DetectedArea )
if Task then
if Task:IsStatePlanned() and DetectedArea.Changed == true then
self:E( "Removing Tasking: " .. Task:GetTaskName() )
Task = Mission:RemoveTask( Task )
end
end
return Task
end
--- Assigns tasks in relation to the detected items to the @{Set#SET_GROUP}.
-- @param #DETECTION_DISPATCHER self
-- @param Functional.Detection#DETECTION_AREAS Detection The detection created by the @{Detection#DETECTION_AREAS} object.
-- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop.
function DETECTION_DISPATCHER:ProcessDetected( Detection )
self:F2()
local AreaMsg = {}
local TaskMsg = {}
local ChangeMsg = {}
local Mission = self.Mission
--- First we need to the detected targets.
for DetectedAreaID, DetectedAreaData in ipairs( Detection:GetDetectedAreas() ) do
local DetectedArea = DetectedAreaData -- Functional.Detection#DETECTION_AREAS.DetectedArea
local DetectedSet = DetectedArea.Set
local DetectedZone = DetectedArea.Zone
self:E( { "Targets in DetectedArea", DetectedArea.AreaID, DetectedSet:Count(), tostring( DetectedArea ) } )
DetectedSet:Flush()
local AreaID = DetectedArea.AreaID
-- Evaluate SEAD Tasking
local SEADTask = Mission:GetTask( "SEAD." .. AreaID )
SEADTask = self:EvaluateRemoveTask( Mission, SEADTask, DetectedArea )
if not SEADTask then
local TargetSetUnit = self:EvaluateSEAD( DetectedArea ) -- Returns a SetUnit if there are targets to be SEADed...
if TargetSetUnit then
SEADTask = Mission:AddTask( TASK_SEAD:New( Mission, self.SetGroup, "SEAD." .. AreaID, TargetSetUnit , DetectedZone ) )
end
end
if SEADTask and SEADTask:IsStatePlanned() then
self:E( "Planned" )
--SEADTask:SetPlannedMenu()
TaskMsg[#TaskMsg+1] = " - " .. SEADTask:GetStateString() .. " SEAD " .. AreaID .. " - " .. SEADTask.TargetSetUnit:GetUnitTypesText()
end
-- Evaluate CAS Tasking
local CASTask = Mission:GetTask( "CAS." .. AreaID )
CASTask = self:EvaluateRemoveTask( Mission, CASTask, DetectedArea )
if not CASTask then
local TargetSetUnit = self:EvaluateCAS( DetectedArea ) -- Returns a SetUnit if there are targets to be SEADed...
if TargetSetUnit then
CASTask = Mission:AddTask( TASK_A2G:New( Mission, self.SetGroup, "CAS." .. AreaID, "CAS", TargetSetUnit , DetectedZone, DetectedArea.NearestFAC ) )
end
end
if CASTask and CASTask:IsStatePlanned() then
--CASTask:SetPlannedMenu()
TaskMsg[#TaskMsg+1] = " - " .. CASTask:GetStateString() .. " CAS " .. AreaID .. " - " .. CASTask.TargetSetUnit:GetUnitTypesText()
end
-- Evaluate BAI Tasking
local BAITask = Mission:GetTask( "BAI." .. AreaID )
BAITask = self:EvaluateRemoveTask( Mission, BAITask, DetectedArea )
if not BAITask then
local TargetSetUnit = self:EvaluateBAI( DetectedArea, self.CommandCenter:GetCoalition() ) -- Returns a SetUnit if there are targets to be SEADed...
if TargetSetUnit then
BAITask = Mission:AddTask( TASK_A2G:New( Mission, self.SetGroup, "BAI." .. AreaID, "BAI", TargetSetUnit , DetectedZone, DetectedArea.NearestFAC ) )
end
end
if BAITask and BAITask:IsStatePlanned() then
--BAITask:SetPlannedMenu()
TaskMsg[#TaskMsg+1] = " - " .. BAITask:GetStateString() .. " BAI " .. AreaID .. " - " .. BAITask.TargetSetUnit:GetUnitTypesText()
end
if #TaskMsg > 0 then
local ThreatLevel = Detection:GetTreatLevelA2G( DetectedArea )
local DetectedAreaVec3 = DetectedZone:GetVec3()
local DetectedAreaPointVec3 = POINT_VEC3:New( DetectedAreaVec3.x, DetectedAreaVec3.y, DetectedAreaVec3.z )
local DetectedAreaPointLL = DetectedAreaPointVec3:ToStringLL( 3, true )
AreaMsg[#AreaMsg+1] = string.format( " - Area #%d - %s - Threat Level [%s] (%2d)",
DetectedAreaID,
DetectedAreaPointLL,
string.rep( "", ThreatLevel ),
ThreatLevel
)
-- Loop through the changes ...
local ChangeText = Detection:GetChangeText( DetectedArea )
if ChangeText ~= "" then
ChangeMsg[#ChangeMsg+1] = string.gsub( string.gsub( ChangeText, "\n", "%1 - " ), "^.", " - %1" )
end
end
-- OK, so the tasking has been done, now delete the changes reported for the area.
Detection:AcceptChanges( DetectedArea )
end
-- TODO set menus using the HQ coordinator
Mission:GetCommandCenter():SetMenu()
if #AreaMsg > 0 then
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
if not TaskGroup:GetState( TaskGroup, "Assigned" ) then
self.CommandCenter:MessageToGroup(
string.format( "HQ Reporting - Target areas for mission '%s':\nAreas:\n%s\n\nTasks:\n%s\n\nChanges:\n%s ",
self.Mission:GetName(),
table.concat( AreaMsg, "\n" ),
table.concat( TaskMsg, "\n" ),
table.concat( ChangeMsg, "\n" )
), self:GetReportDisplayTime(), TaskGroup
)
end
end
end
return true
end
end

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,28 +1,76 @@
--- (AI) (SP) (MP) Tasking for Air to Ground Processes.
--- **Tasking** - The TASK_A2G models tasks for players in Air to Ground engagements.
--
-- 1) @{#TASK_A2G} class, extends @{Task#TASK}
-- =================================================
-- The @{#TASK_A2G} class defines a CAS or BAI task of a @{Set} of Target Units,
-- located at a Target Zone, based on the tasking capabilities defined in @{Task#TASK}.
-- ![Banner Image](..\Presentations\TASK_A2G\Dia1.JPG)
--
--
-- # 1) @{Task_A2G#TASK_A2G} class, extends @{Task#TASK}
--
-- The @{#TASK_A2G} class defines Air To Ground tasks for a @{Set} of Target Units,
-- based on the tasking capabilities defined in @{Task#TASK}.
-- The TASK_A2G is implemented using a @{Statemachine#FSM_TASK}, and has the following statuses:
--
-- * **None**: Start of the process
-- * **Planned**: The SEAD task is planned. Upon Planned, the sub-process @{Process_Fsm.Assign#ACT_ASSIGN_ACCEPT} is started to accept the task.
-- * **Assigned**: The SEAD task is assigned to a @{Group#GROUP}. Upon Assigned, the sub-process @{Process_Fsm.Route#ACT_ROUTE} is started to route the active Units in the Group to the attack zone.
-- * **Success**: The SEAD task is successfully completed. Upon Success, the sub-process @{Process_SEAD#PROCESS_SEAD} is started to follow-up successful SEADing of the targets assigned in the task.
-- * **Failed**: The SEAD task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ.
-- * **Planned**: The A2G task is planned.
-- * **Assigned**: The A2G task is assigned to a @{Group#GROUP}.
-- * **Success**: The A2G task is successfully completed.
-- * **Failed**: The A2G task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ.
--
-- # 1.1) Set the scoring of achievements in an A2G attack.
--
-- Scoring or penalties can be given in the following circumstances:
--
-- * @{#TASK_A2G.SetScoreOnDestroy}(): Set a score when a target in scope of the A2G attack, has been destroyed.
-- * @{#TASK_A2G.SetScoreOnSuccess}(): Set a score when all the targets in scope of the A2G attack, have been destroyed.
-- * @{#TASK_A2G.SetPenaltyOnFailed}(): Set a penalty when the A2G attack has failed.
--
-- # 2) @{Task_A2G#TASK_SEAD} class, extends @{Task_A2G#TASK_A2G}
--
-- The @{#TASK_SEAD} class defines a SEAD task for a @{Set} of Target Units.
--
-- ===
--
-- ### Authors: FlightControl - Design and Programming
-- # 3) @{Task_A2G#TASK_CAS} class, extends @{Task_A2G#TASK_A2G}
--
-- The @{#TASK_CAS} class defines a CAS task for a @{Set} of Target Units.
--
-- ===
--
-- # 4) @{Task_A2G#TASK_BAI} class, extends @{Task_A2G#TASK_A2G}
--
-- The @{#TASK_BAI} class defines a BAI task for a @{Set} of Target Units.
--
-- ====
--
-- # **API CHANGE HISTORY**
--
-- The underlying change log documents the API changes. Please read this carefully. The following notation is used:
--
-- * **Added** parts are expressed in bold type face.
-- * _Removed_ parts are expressed in italic type face.
--
-- Hereby the change log:
--
-- 2017-03-09: Revised version.
--
-- ===
--
-- # **AUTHORS and CONTRIBUTIONS**
--
-- ### Contributions:
--
-- * **[WingThor]**: Concept, Advice & Testing.
--
-- ### Authors:
--
-- * **FlightControl**: Concept, Design & Programming.
--
-- @module Task_A2G
do -- TASK_A2G
--- The TASK_A2G class
-- @type TASK_A2G
-- @field Set#SET_UNIT TargetSetUnit
-- @extends Tasking.Task#TASK
TASK_A2G = {
ClassName = "TASK_A2G",
@@ -33,52 +81,393 @@ do -- TASK_A2G
-- @param Tasking.Mission#MISSION Mission
-- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned.
-- @param #string TaskName The name of the Task.
-- @param #string TaskType BAI or CAS
-- @param Set#SET_UNIT UnitSetTargets
-- @param Core.Zone#ZONE_BASE TargetZone
-- @param #number TargetDistance The distance to Target when the Player is considered to have "arrived" at the engagement range.
-- @param Core.Zone#ZONE_BASE TargetZone The target zone, if known.
-- If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be.
-- @return #TASK_A2G self
function TASK_A2G:New( Mission, SetGroup, TaskName, TaskType, TargetSetUnit, TargetZone, FACUnit )
local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, TaskType ) )
function TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskType, TaskBriefing )
local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, TaskType, TaskBriefing ) ) -- Tasking.Task#TASK_A2G
self:F()
self.TargetSetUnit = TargetSetUnit
self.TargetZone = TargetZone
self.FACUnit = FACUnit
local A2GUnitProcess = self:GetUnitProcess()
self.TaskType = TaskType
A2GUnitProcess:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( "Attack the Area" ), { Assigned = "Route", Rejected = "Eject" } )
A2GUnitProcess:AddProcess ( "Assigned", "Route", ACT_ROUTE_ZONE:New( self.TargetZone ), { Arrived = "Update" } )
A2GUnitProcess:AddTransition( "Rejected", "Eject", "Planned" )
A2GUnitProcess:AddTransition( "Arrived", "Update", "Updated" )
A2GUnitProcess:AddProcess ( "Updated", "Account", ACT_ACCOUNT_DEADS:New( self.TargetSetUnit, "Attack" ), { Accounted = "Success" } )
A2GUnitProcess:AddProcess ( "Updated", "Smoke", ACT_ASSIST_SMOKE_TARGETS_ZONE:New( self.TargetSetUnit, self.TargetZone ) )
--Fsm:AddProcess ( "Updated", "JTAC", PROCESS_JTAC:New( self, TaskUnit, self.TargetSetUnit, self.FACUnit ) )
A2GUnitProcess:AddTransition( "Accounted", "Success", "Success" )
A2GUnitProcess:AddTransition( "Failed", "Fail", "Failed" )
local Fsm = self:GetUnitProcess()
function A2GUnitProcess:onenterUpdated( TaskUnit )
self:E( { self } )
self:Account()
self:Smoke()
Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "RouteToRendezVous", Rejected = "Reject" } )
Fsm:AddTransition( "Assigned", "RouteToRendezVous", "RoutingToRendezVous" )
Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtRendezVous" } )
Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtRendezVous" } )
Fsm:AddTransition( { "Arrived", "RoutingToRendezVous" }, "ArriveAtRendezVous", "ArrivedAtRendezVous" )
Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "Engage", "Engaging" )
Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "HoldAtRendezVous", "HoldingAtRendezVous" )
Fsm:AddProcess ( "Engaging", "Account", ACT_ACCOUNT_DEADS:New( self.TargetSetUnit, self.TaskType ), { Accounted = "Success" } )
Fsm:AddTransition( "Engaging", "RouteToTarget", "Engaging" )
Fsm:AddProcess( "Engaging", "RouteToTargetZone", ACT_ROUTE_ZONE:New(), {} )
Fsm:AddProcess( "Engaging", "RouteToTargetPoint", ACT_ROUTE_POINT:New(), {} )
Fsm:AddTransition( "Engaging", "RouteToTargets", "Engaging" )
Fsm:AddTransition( "Accounted", "DestroyedAll", "Accounted" )
Fsm:AddTransition( "Accounted", "Success", "Success" )
Fsm:AddTransition( "Rejected", "Reject", "Aborted" )
Fsm:AddTransition( "Failed", "Fail", "Failed" )
--- Test
-- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_A2G#TASK_A2G Task
function Fsm:onafterRouteToRendezVous( TaskUnit, Task )
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
-- Determine the first Unit from the self.RendezVousSetUnit
if Task:GetRendezVousZone( TaskUnit ) then
self:__RouteToRendezVousZone( 0.1 )
else
if Task:GetRendezVousCoordinate( TaskUnit ) then
self:__RouteToRendezVousPoint( 0.1 )
else
self:__ArriveAtRendezVous( 0.1 )
end
end
end
--- Test
-- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task#TASK_A2G Task
function Fsm:OnAfterArriveAtRendezVous( TaskUnit, Task )
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
-- Determine the first Unit from the self.TargetSetUnit
self:__Engage( 0.1 )
end
--- Test
-- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task#TASK_A2G Task
function Fsm:onafterEngage( TaskUnit, Task )
self:E( { self } )
self:__Account( 0.1 )
self:__RouteToTarget(0.1 )
self:__RouteToTargets( -10 )
end
--- Test
-- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_A2G#TASK_A2G Task
function Fsm:onafterRouteToTarget( TaskUnit, Task )
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
-- Determine the first Unit from the self.TargetSetUnit
if Task:GetTargetZone( TaskUnit ) then
self:__RouteToTargetZone( 0.1 )
else
local TargetUnit = Task.TargetSetUnit:GetFirst() -- Wrapper.Unit#UNIT
if TargetUnit then
local Coordinate = TargetUnit:GetCoordinate()
self:T( { TargetCoordinate = Coordinate, Coordinate:GetX(), Coordinate:GetAlt(), Coordinate:GetZ() } )
Task:SetTargetCoordinate( TargetUnit:GetCoordinate(), TaskUnit )
end
self:__RouteToTargetPoint( 0.1 )
end
end
--- Test
-- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_A2G#TASK_A2G Task
function Fsm:onafterRouteToTargets( TaskUnit, Task )
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
local TargetUnit = Task.TargetSetUnit:GetFirst() -- Wrapper.Unit#UNIT
if TargetUnit then
Task:SetTargetCoordinate( TargetUnit:GetCoordinate(), TaskUnit )
end
self:__RouteToTargets( -10 )
end
--_EVENTDISPATCHER:OnPlayerLeaveUnit( self._EventPlayerLeaveUnit, self )
--_EVENTDISPATCHER:OnDead( self._EventDead, self )
--_EVENTDISPATCHER:OnCrash( self._EventDead, self )
--_EVENTDISPATCHER:OnPilotDead( self._EventDead, self )
return self
end
--- @param #TASK_A2G self
--- @param #TASK_A2G self
function TASK_A2G:GetPlannedMenuText()
return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )"
end
--- @param #TASK_A2G self
-- @param Core.Point#COORDINATE RendezVousCoordinate The Coordinate object referencing to the 2D point where the RendezVous point is located on the map.
-- @param #number RendezVousRange The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point.
-- @param Wrapper.Unit#UNIT TaskUnit
function TASK_A2G:SetRendezVousCoordinate( RendezVousCoordinate, RendezVousRange, TaskUnit )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT
ActRouteRendezVous:SetCoordinate( RendezVousCoordinate )
ActRouteRendezVous:SetRange( RendezVousRange )
end
--- @param #TASK_A2G self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return Core.Point#COORDINATE The Coordinate object referencing to the 2D point where the RendezVous point is located on the map.
-- @return #number The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point.
function TASK_A2G:GetRendezVousCoordinate( TaskUnit )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT
return ActRouteRendezVous:GetCoordinate(), ActRouteRendezVous:GetRange()
end
--- @param #TASK_A2G self
-- @param Core.Zone#ZONE_BASE RendezVousZone The Zone object where the RendezVous is located on the map.
-- @param Wrapper.Unit#UNIT TaskUnit
function TASK_A2G:SetRendezVousZone( RendezVousZone, TaskUnit )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
ActRouteRendezVous:SetZone( RendezVousZone )
end
--- @param #TASK_A2G self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return Core.Zone#ZONE_BASE The Zone object where the RendezVous is located on the map.
function TASK_A2G:GetRendezVousZone( TaskUnit )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
local ActRouteRendezVous = ProcessUnit:GetProcess( "RoutingToRendezVous", "RouteToRendezVousZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
return ActRouteRendezVous:GetZone()
end
--- @param #TASK_A2G self
-- @param Core.Point#COORDINATE TargetCoordinate The Coordinate object where the Target is located on the map.
-- @param Wrapper.Unit#UNIT TaskUnit
function TASK_A2G:SetTargetCoordinate( TargetCoordinate, TaskUnit )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT
ActRouteTarget:SetCoordinate( TargetCoordinate )
end
--- @param #TASK_A2G self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return Core.Point#COORDINATE The Coordinate object where the Target is located on the map.
function TASK_A2G:GetTargetCoordinate( TaskUnit )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT
return ActRouteTarget:GetCoordinate()
end
--- @param #TASK_A2G self
-- @param Core.Zone#ZONE_BASE TargetZone The Zone object where the Target is located on the map.
-- @param Wrapper.Unit#UNIT TaskUnit
function TASK_A2G:SetTargetZone( TargetZone, TaskUnit )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
ActRouteTarget:SetZone( TargetZone )
end
--- @param #TASK_A2G self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return Core.Zone#ZONE_BASE The Zone object where the Target is located on the map.
function TASK_A2G:GetTargetZone( TaskUnit )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
return ActRouteTarget:GetZone()
end
--- Set a score when a target in scope of the A2G attack, has been destroyed .
-- @param #TASK_A2G self
-- @param #string Text The text to display to the player, when the target has been destroyed.
-- @param #number Score The score in points.
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return #TASK_A2G
function TASK_A2G:SetScoreOnDestroy( Text, Score, TaskUnit )
self:F( { Text, Score, TaskUnit } )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScoreProcess( "Engaging", "Account", "Account", Text, Score )
return self
end
--- Set a score when all the targets in scope of the A2G attack, have been destroyed.
-- @param #TASK_A2G self
-- @param #string Text The text to display to the player, when all targets hav been destroyed.
-- @param #number Score The score in points.
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return #TASK_A2G
function TASK_A2G:SetScoreOnSuccess( Text, Score, TaskUnit )
self:F( { Text, Score, TaskUnit } )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScore( "Success", Text, Score )
return self
end
--- Set a penalty when the A2G attack has failed.
-- @param #TASK_A2G self
-- @param #string Text The text to display to the player, when the A2G attack has failed.
-- @param #number Penalty The penalty in points.
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return #TASK_A2G
function TASK_A2G:SetPenaltyOnFailed( Text, Penalty, TaskUnit )
self:F( { Text, Score, TaskUnit } )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScore( "Failed", Text, Penalty )
return self
end
end
do -- TASK_SEAD
--- The TASK_SEAD class
-- @type TASK_SEAD
-- @field Set#SET_UNIT TargetSetUnit
-- @extends Tasking.Task#TASK
TASK_SEAD = {
ClassName = "TASK_SEAD",
}
--- Instantiates a new TASK_SEAD.
-- @param #TASK_SEAD self
-- @param Tasking.Mission#MISSION Mission
-- @param Core.Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned.
-- @param #string TaskName The name of the Task.
-- @param Core.Set#SET_UNIT TargetSetUnit
-- @param #string TaskBriefing The briefing of the task.
-- @return #TASK_SEAD self
function TASK_SEAD:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing )
local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "SEAD", TaskBriefing ) ) -- #TASK_SEAD
self:F()
Mission:AddTask( self )
local TargetCoord = TargetSetUnit:GetFirst():GetCoordinate()
local TargetPositionText = TargetCoord:ToString()
local TargetThreatLevel = TargetSetUnit:CalculateThreatLevelA2G()
self:SetBriefing(
TaskBriefing or
"Execute a Suppression of Enemy Air Defenses.\n" ..
"Initial Coordinates: " .. TargetPositionText .. "\n" ..
"Threat Level: [" .. string.rep( "", TargetThreatLevel ) .. "]"
)
return self
end
end
do -- TASK_BAI
--- The TASK_BAI class
-- @type TASK_BAI
-- @field Set#SET_UNIT TargetSetUnit
-- @extends Tasking.Task#TASK
TASK_BAI = {
ClassName = "TASK_BAI",
}
--- Instantiates a new TASK_BAI.
-- @param #TASK_BAI self
-- @param Tasking.Mission#MISSION Mission
-- @param Core.Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned.
-- @param #string TaskName The name of the Task.
-- @param Core.Set#SET_UNIT TargetSetUnit
-- @param #string TaskBriefing The briefing of the task.
-- @return #TASK_BAI self
function TASK_BAI:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing )
local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "BAI", TaskBriefing ) ) -- #TASK_BAI
self:F()
Mission:AddTask( self )
local TargetCoord = TargetSetUnit:GetFirst():GetCoordinate()
local TargetPositionText = TargetCoord:ToString()
local TargetThreatLevel = TargetSetUnit:CalculateThreatLevelA2G()
self:SetBriefing(
TaskBriefing or
"Execute a Battlefield Air Interdiction of a group of enemy targets.\n" ..
"Initial Coordinates: " .. TargetPositionText .. "\n" ..
"Threat Level: [" .. string.rep( "", TargetThreatLevel ) .. "]"
)
return self
end
end
do -- TASK_CAS
--- The TASK_CAS class
-- @type TASK_CAS
-- @field Set#SET_UNIT TargetSetUnit
-- @extends Tasking.Task#TASK
TASK_CAS = {
ClassName = "TASK_CAS",
}
--- Instantiates a new TASK_CAS.
-- @param #TASK_CAS self
-- @param Tasking.Mission#MISSION Mission
-- @param Core.Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned.
-- @param #string TaskName The name of the Task.
-- @param Core.Set#SET_UNIT TargetSetUnit
-- @param #string TaskBriefing The briefing of the task.
-- @return #TASK_CAS self
function TASK_CAS:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing )
local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "CAS", TaskBriefing ) ) -- #TASK_CAS
self:F()
Mission:AddTask( self )
local TargetCoord = TargetSetUnit:GetFirst():GetCoordinate()
local TargetPositionText = TargetCoord:ToString()
local TargetThreatLevel = TargetSetUnit:CalculateThreatLevelA2G()
self:SetBriefing(
TaskBriefing or
"Execute a Close Air Support for a group of enemy targets.\n" ..
"Beware of friendlies at the vicinity!\n" ..
"Initial Coordinates: " .. TargetPositionText .. "\n" ..
"Threat Level: [" .. string.rep( "", TargetThreatLevel ) .. "]"
)
return self
end
end

View File

@@ -0,0 +1,294 @@
--- **Tasking** - The TASK_A2G_DISPATCHER creates and manages player TASK_A2G tasks based on detected targets.
--
-- ===
--
-- # 1) @{#TASK_A2G_DISPATCHER} class, extends @{#DETECTION_MANAGER}
--
-- The @{#TASK_A2G_DISPATCHER} class implements the dynamic dispatching of tasks upon groups of detected units determined a @{Set} of FAC (groups).
-- The FAC will detect units, will group them, and will dispatch @{Task}s to groups. Depending on the type of target detected, different tasks will be dispatched.
-- Find a summary below describing for which situation a task type is created:
--
-- * **CAS Task**: Is created when there are enemy ground units within range of the FAC, while there are friendly units in the FAC perimeter.
-- * **BAI Task**: Is created when there are enemy ground units within range of the FAC, while there are NO other friendly units within the FAC perimeter.
-- * **SEAD Task**: Is created when there are enemy ground units wihtin range of the FAC, with air search radars.
--
-- Other task types will follow...
--
-- 3.1) TASK_A2G_DISPATCHER constructor:
-- --------------------------------------
-- The @{#TASK_A2G_DISPATCHER.New}() method creates a new TASK_A2G_DISPATCHER instance.
--
-- ===
--
-- # **API CHANGE HISTORY**
--
-- The underlying change log documents the API changes. Please read this carefully. The following notation is used:
--
-- * **Added** parts are expressed in bold type face.
-- * _Removed_ parts are expressed in italic type face.
--
-- Hereby the change log:
--
-- 2017-03-09: Initial class and API.
--
-- ===
--
-- # **AUTHORS and CONTRIBUTIONS**
--
-- ### Contributions:
--
-- ### Authors:
--
-- * **FlightControl**: Concept, Design & Programming.
--
-- @module Task_A2G_Dispatcher
do -- TASK_A2G_DISPATCHER
--- TASK_A2G_DISPATCHER class.
-- @type TASK_A2G_DISPATCHER
-- @field Set#SET_GROUP SetGroup The groups to which the FAC will report to.
-- @field Functional.Detection#DETECTION_BASE Detection The DETECTION_BASE object that is used to report the detected objects.
-- @field Tasking.Mission#MISSION Mission
-- @extends Tasking.DetectionManager#DETECTION_MANAGER
TASK_A2G_DISPATCHER = {
ClassName = "TASK_A2G_DISPATCHER",
Mission = nil,
Detection = nil,
Tasks = {},
}
--- TASK_A2G_DISPATCHER constructor.
-- @param #TASK_A2G_DISPATCHER self
-- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done.
-- @param Set#SET_GROUP SetGroup The set of groups that can join the tasks within the mission.
-- @param Functional.Detection#DETECTION_BASE Detection The detection results that are used to dynamically assign new tasks to human players.
-- @return #TASK_A2G_DISPATCHER self
function TASK_A2G_DISPATCHER:New( Mission, SetGroup, Detection )
-- Inherits from DETECTION_MANAGER
local self = BASE:Inherit( self, DETECTION_MANAGER:New( SetGroup, Detection ) ) -- #TASK_A2G_DISPATCHER
self.Detection = Detection
self.Mission = Mission
self.Detection:FilterCategories( Unit.Category.GROUND_UNIT, Unit.Category.SHIP )
self:AddTransition( "Started", "Assign", "Started" )
--- OnAfter Transition Handler for Event Assign.
-- @function [parent=#TASK_A2G_DISPATCHER] OnAfterAssign
-- @param #TASK_A2G_DISPATCHER self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param Tasking.Task_A2G#TASK_A2G Task
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param #string PlayerName
self:__Start( 5 )
return self
end
--- Creates a SEAD task when there are targets for it.
-- @param #TASK_A2G_DISPATCHER self
-- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem
-- @return Set#SET_UNIT TargetSetUnit: The target set of units.
-- @return #nil If there are no targets to be set.
function TASK_A2G_DISPATCHER:EvaluateSEAD( DetectedItem )
self:F( { DetectedItem.ItemID } )
local DetectedSet = DetectedItem.Set
local DetectedZone = DetectedItem.Zone
-- Determine if the set has radar targets. If it does, construct a SEAD task.
local RadarCount = DetectedSet:HasSEAD()
if RadarCount > 0 then
-- Here we're doing something advanced... We're copying the DetectedSet, but making a new Set only with SEADable Radar units in it.
local TargetSetUnit = SET_UNIT:New()
TargetSetUnit:SetDatabase( DetectedSet )
TargetSetUnit:FilterHasSEAD()
TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection.
return TargetSetUnit
end
return nil
end
--- Creates a CAS task when there are targets for it.
-- @param #TASK_A2G_DISPATCHER self
-- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem
-- @return Tasking.Task#TASK
function TASK_A2G_DISPATCHER:EvaluateCAS( DetectedItem )
self:F( { DetectedItem.ItemID } )
local DetectedSet = DetectedItem.Set
local DetectedZone = DetectedItem.Zone
-- Determine if the set has radar targets. If it does, construct a SEAD task.
local GroundUnitCount = DetectedSet:HasGroundUnits()
local FriendliesNearBy = self.Detection:IsFriendliesNearBy( DetectedItem )
if GroundUnitCount > 0 and FriendliesNearBy == true then
-- Copy the Set
local TargetSetUnit = SET_UNIT:New()
TargetSetUnit:SetDatabase( DetectedSet )
TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection.
return TargetSetUnit
end
return nil
end
--- Creates a BAI task when there are targets for it.
-- @param #TASK_A2G_DISPATCHER self
-- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem
-- @return Tasking.Task#TASK
function TASK_A2G_DISPATCHER:EvaluateBAI( DetectedItem, FriendlyCoalition )
self:F( { DetectedItem.ItemID } )
local DetectedSet = DetectedItem.Set
local DetectedZone = DetectedItem.Zone
-- Determine if the set has radar targets. If it does, construct a SEAD task.
local GroundUnitCount = DetectedSet:HasGroundUnits()
local FriendliesNearBy = self.Detection:IsFriendliesNearBy( DetectedItem )
if GroundUnitCount > 0 and FriendliesNearBy == false then
-- Copy the Set
local TargetSetUnit = SET_UNIT:New()
TargetSetUnit:SetDatabase( DetectedSet )
TargetSetUnit:FilterOnce() -- Filter but don't do any events!!! Elements are added manually upon each detection.
return TargetSetUnit
end
return nil
end
--- Evaluates the removal of the Task from the Mission.
-- Can only occur when the DetectedItem is Changed AND the state of the Task is "Planned".
-- @param #TASK_A2G_DISPATCHER self
-- @param Tasking.Mission#MISSION Mission
-- @param Tasking.Task#TASK Task
-- @param #boolean DetectedItemID
-- @param #boolean DetectedItemChange
-- @return Tasking.Task#TASK
function TASK_A2G_DISPATCHER:EvaluateRemoveTask( Mission, Task, DetectedItemID, DetectedItemChanged )
if Task then
if Task:IsStatePlanned() and DetectedItemChanged == true then
self:E( "Removing Tasking: " .. Task:GetTaskName() )
Mission:RemoveTask( Task )
self.Tasks[DetectedItemID] = nil
end
end
return Task
end
--- Assigns tasks in relation to the detected items to the @{Set#SET_GROUP}.
-- @param #TASK_A2G_DISPATCHER self
-- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Detection#DETECTION_BASE} derived object.
-- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop.
function TASK_A2G_DISPATCHER:ProcessDetected( Detection )
self:E()
local AreaMsg = {}
local TaskMsg = {}
local ChangeMsg = {}
local Mission = self.Mission
if Mission:IsIDLE() or Mission:IsENGAGED() then
local TaskReport = REPORT:New()
--- First we need to the detected targets.
for DetectedItemID, DetectedItem in pairs( Detection:GetDetectedItems() ) do
local DetectedItem = DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem
local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT
local DetectedZone = DetectedItem.Zone
self:E( { "Targets in DetectedItem", DetectedItem.ItemID, DetectedSet:Count(), tostring( DetectedItem ) } )
DetectedSet:Flush()
local DetectedItemID = DetectedItem.ID
local DetectedItemIndex = DetectedItem.Index
local DetectedItemChanged = DetectedItem.Changed
local Task = self.Tasks[DetectedItemID]
Task = self:EvaluateRemoveTask( Mission, Task, DetectedItemID, DetectedItemChanged ) -- Task will be removed if it is planned and changed.
-- Evaluate SEAD
if not Task then
local TargetSetUnit = self:EvaluateSEAD( DetectedItem ) -- Returns a SetUnit if there are targets to be SEADed...
if TargetSetUnit then
Task = TASK_SEAD:New( Mission, self.SetGroup, string.format( "SEAD.%03d", DetectedItemID ), TargetSetUnit )
end
-- Evaluate CAS
if not Task then
local TargetSetUnit = self:EvaluateCAS( DetectedItem ) -- Returns a SetUnit if there are targets to be CASed...
if TargetSetUnit then
Task = TASK_CAS:New( Mission, self.SetGroup, string.format( "CAS.%03d", DetectedItemID ), TargetSetUnit )
end
-- Evaluate BAI
if not Task then
local TargetSetUnit = self:EvaluateBAI( DetectedItem, self.Mission:GetCommandCenter():GetPositionable():GetCoalition() ) -- Returns a SetUnit if there are targets to be BAIed...
if TargetSetUnit then
Task = TASK_BAI:New( Mission, self.SetGroup, string.format( "BAI.%03d", DetectedItemID ), TargetSetUnit )
end
end
end
if Task then
self.Tasks[DetectedItemID] = Task
Task:SetTargetZone( DetectedZone )
Task:SetDispatcher( self )
Task:SetInfo( "ThreatLevel", DetectedSet:CalculateThreatLevelA2G() )
Task:SetInfo( "Detection", Detection:DetectedItemReportSummary( DetectedItemIndex ) )
Task:SetInfo( "Changes", Detection:GetChangeText( DetectedItem ) )
Mission:AddTask( Task )
else
self:E("This should not happen")
end
end
TaskReport:Add( Task:GetName() )
-- OK, so the tasking has been done, now delete the changes reported for the area.
Detection:AcceptChanges( DetectedItem )
end
-- TODO set menus using the HQ coordinator
Mission:GetCommandCenter():SetMenu()
local TaskText = TaskReport:Text(", ")
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
if ( not Mission:IsGroupAssigned(TaskGroup) ) and TaskText ~= "" then
Mission:GetCommandCenter():MessageToGroup( string.format( "%s has tasks %s. Subscribe to a task using the radio menu.", Mission:GetName(), TaskText ), TaskGroup )
end
end
end
return true
end
end

View File

@@ -0,0 +1,916 @@
--- **Tasking (Release 2.1)** -- The TASK_CARGO models tasks for players to transport @{Cargo}.
--
-- ![Banner Image](..\Presentations\TASK_CARGO\Dia1.JPG)
--
-- ====
--
-- The Moose framework provides various CARGO classes that allow DCS phisical or logical objects to be transported or sling loaded by Carriers.
-- The CARGO_ classes, as part of the moose core, are able to Board, Load, UnBoard and UnLoad cargo between Carrier units.
--
-- This collection of classes in this module define tasks for human players to handle these cargo objects.
-- Cargo can be transported, picked-up, deployed and sling-loaded from and to other places.
--
-- The following classes are important to consider:
--
-- * @{#TASK_CARGO_TRANSPORT}: Defines a task for a human player to transport a set of cargo between various zones.
--
-- ==
--
-- # **API CHANGE HISTORY**
--
-- The underlying change log documents the API changes. Please read this carefully. The following notation is used:
--
-- * **Added** parts are expressed in bold type face.
-- * _Removed_ parts are expressed in italic type face.
--
-- Hereby the change log:
--
-- 2017-03-09: Revised version.
--
-- ===
--
-- # **AUTHORS and CONTRIBUTIONS**
--
-- ### Contributions:
--
-- ### Authors:
--
-- * **FlightControl**: Concept, Design & Programming.
--
-- @module Task_Cargo
do -- TASK_CARGO
--- @type TASK_CARGO
-- @extends Tasking.Task#TASK
---
-- # TASK_CARGO class, extends @{Task#TASK}
--
-- ## A flexible tasking system
--
-- The TASK_CARGO classes provide you with a flexible tasking sytem,
-- that allows you to transport cargo of various types between various locations
-- and various dedicated deployment zones.
--
-- The cargo in scope of the TASK_CARGO classes must be explicitly given, and is of type SET_CARGO.
-- The SET_CARGO contains a collection of CARGO objects that must be handled by the players in the mission.
--
--
-- ## Task execution experience from the player perspective
--
-- A human player can join the battle field in a client airborne slot or a ground vehicle within the CA module (ALT-J).
-- The player needs to accept the task from the task overview list within the mission, using the radio menus.
--
-- Once the TASK_CARGO is assigned to the player and accepted by the player, the player will obtain
-- an extra **Cargo Handling Radio Menu** that contains the CARGO objects that need to be transported.
--
-- Each CARGO object has a certain state:
--
-- * **UnLoaded**: The CARGO is located within the battlefield. It may still need to be transported.
-- * **Loaded**: The CARGO is loaded within a Carrier. This can be your air unit, or another air unit, or even a vehicle.
-- * **Boarding**: The CARGO is running or moving towards your Carrier for loading.
-- * **UnBoarding**: The CARGO is driving or jumping out of your Carrier and moves to a location in the Deployment Zone.
--
-- Cargo must be transported towards different **Deployment @{Zone}s**.
--
-- The Cargo Handling Radio Menu system allows to execute **various actions** to handle the cargo.
-- In the menu, you'll find for each CARGO, that is part of the scope of the task, various actions that can be completed.
-- Depending on the location of your Carrier unit, the menu options will vary.
--
--
-- ## Cargo Pickup and Boarding
--
-- For cargo boarding, a cargo can only execute the boarding actions if it is within the foreseen **Reporting Range**.
-- Therefore, it is important that you steer your Carrier within the Reporting Range,
-- so that boarding actions can be executed on the cargo.
-- To Pickup and Board cargo, the following menu items will be shown in your carrier radio menu:
--
-- ### Board Cargo
--
-- If your Carrier is within the Reporting Range of the cargo, it will allow to pickup the cargo by selecting this menu option.
-- Depending on the Cargo type, the cargo will either move to your Carrier or you will receive instructions how to handle the cargo
-- pickup. If the cargo moves to your carrier, it will indicate the boarding status.
-- Note that multiple units need to board your Carrier, so it is required to await the full boarding process.
-- Once the cargo is fully boarded within your Carrier, you will be notified of this.
--
-- Note that for airborne Carriers, it is required to land first before the Boarding process can be initiated.
-- If during boarding the Carrier gets airborne, the boarding process will be cancelled.
--
-- ## Pickup Cargo
--
-- If your Carrier is not within the Reporting Range of the cargo, the HQ will guide you to its location.
-- Routing information is shown in flight that directs you to the cargo within Reporting Range.
-- Upon arrival, the Cargo will contact you and further instructions will be given.
-- When your Carrier is airborne, you will receive instructions to land your Carrier.
-- The action will not be completed until you've landed your Carrier.
--
--
-- ## Cargo Deploy and UnBoarding
--
-- Various Deployment Zones can be foreseen in the scope of the Cargo transportation. Each deployment zone can be of varying @{Zone} type.
-- The Cargo Handling Radio Menu provides with menu options to execute an action to steer your Carrier to a specific Zone.
--
-- ### UnBoard Cargo
--
-- If your Carrier is already within a Deployment Zone,
-- then the Cargo Handling Radio Menu allows to **UnBoard** a specific cargo that is
-- loaded within your Carrier group into the Deployment Zone.
-- Note that the Unboarding process takes a while, as the cargo units (infantry or vehicles) must unload from your Carrier.
-- Ensure that you stay at the position or stay on the ground while Unboarding.
-- If any unforeseen manoeuvre is done by the Carrier, then the Unboarding will be cancelled.
--
-- ### Deploy Cargo
--
-- If your Carrier is not within a Deployment Zone, you'll need to fly towards one.
-- Fortunately, the Cargo Handling Radio Menu provides you with menu options to select a specific Deployment Zone to fly towards.
-- Once a Deployment Zone has been selected, your Carrier will receive routing information from HQ towards the Deployment Zone center.
-- Upon arrival, the HQ will provide you with further instructions.
-- When your Carrier is airborne, you will receive instructions to land your Carrier.
-- The action will not be completed until you've landed your Carrier!
--
-- ## Handle TASK_CARGO Events ...
--
-- The TASK_CARGO classes define @{Cargo} transport tasks,
-- based on the tasking capabilities defined in @{Task#TASK}.
--
-- ### Specific TASK_CARGO Events
--
-- Specific Cargo Handling event can be captured, that allow to trigger specific actions!
--
-- * **Boarded**: Triggered when the Cargo has been Boarded into your Carrier.
-- * **UnBoarded**: Triggered when the cargo has been Unboarded from your Carrier and has arrived at the Deployment Zone.
--
-- ### Standard TASK_CARGO Events
--
-- The TASK_CARGO is implemented using a @{Statemachine#FSM_TASK}, and has the following standard statuses:
--
-- * **None**: Start of the process.
-- * **Planned**: The cargo task is planned.
-- * **Assigned**: The cargo task is assigned to a @{Group#GROUP}.
-- * **Success**: The cargo task is successfully completed.
-- * **Failed**: The cargo task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ.
--
-- ===
--
-- @field #TASK_CARGO
--
TASK_CARGO = {
ClassName = "TASK_CARGO",
}
--- Instantiates a new TASK_CARGO.
-- @param #TASK_CARGO self
-- @param Tasking.Mission#MISSION Mission
-- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned.
-- @param #string TaskName The name of the Task.
-- @param Core.Set#SET_CARGO SetCargo The scope of the cargo to be transported.
-- @param #string TaskType The type of Cargo task.
-- @param #string TaskBriefing The Cargo Task briefing.
-- @return #TASK_CARGO self
function TASK_CARGO:New( Mission, SetGroup, TaskName, SetCargo, TaskType, TaskBriefing )
local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, TaskType, TaskBriefing ) ) -- #TASK_CARGO
self:F( {Mission, SetGroup, TaskName, SetCargo, TaskType})
self.SetCargo = SetCargo
self.TaskType = TaskType
self.DeployZones = {} -- setmetatable( {}, { __mode = "v" } ) -- weak table on value
local Fsm = self:GetUnitProcess()
Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "SelectAction", Rejected = "Reject" } )
Fsm:AddTransition( { "Assigned", "WaitingForCommand", "ArrivedAtPickup", "ArrivedAtDeploy", "Boarded", "UnBoarded", "Landed", "Boarding" }, "SelectAction", "*" )
Fsm:AddTransition( "*", "RouteToPickup", "RoutingToPickup" )
Fsm:AddProcess ( "RoutingToPickup", "RouteToPickupPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtPickup", Cancelled = "CancelRouteToPickup" } )
Fsm:AddTransition( "Arrived", "ArriveAtPickup", "ArrivedAtPickup" )
Fsm:AddTransition( "Cancelled", "CancelRouteToPickup", "WaitingForCommand" )
Fsm:AddTransition( "*", "RouteToDeploy", "RoutingToDeploy" )
Fsm:AddProcess ( "RoutingToDeploy", "RouteToDeployZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtDeploy", Cancelled = "CancelRouteToDeploy" } )
Fsm:AddTransition( "Arrived", "ArriveAtDeploy", "ArrivedAtDeploy" )
Fsm:AddTransition( "Cancelled", "CancelRouteToDeploy", "WaitingForCommand" )
Fsm:AddTransition( { "ArrivedAtPickup", "ArrivedAtDeploy", "Landing" }, "Land", "Landing" )
Fsm:AddTransition( "Landing", "Landed", "Landed" )
Fsm:AddTransition( "*", "PrepareBoarding", "AwaitBoarding" )
Fsm:AddTransition( "AwaitBoarding", "Board", "Boarding" )
Fsm:AddTransition( "Boarding", "Boarded", "Boarded" )
Fsm:AddTransition( "*", "PrepareUnBoarding", "AwaitUnBoarding" )
Fsm:AddTransition( "AwaitUnBoarding", "UnBoard", "UnBoarding" )
Fsm:AddTransition( "UnBoarding", "UnBoarded", "UnBoarded" )
Fsm:AddTransition( "Deployed", "Success", "Success" )
Fsm:AddTransition( "Rejected", "Reject", "Aborted" )
Fsm:AddTransition( "Failed", "Fail", "Failed" )
---
-- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_CARGO#TASK_CARGO Task
function Fsm:onafterSelectAction( TaskUnit, Task )
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
local MenuTime = timer.getTime()
TaskUnit.Menu = MENU_GROUP:New( TaskUnit:GetGroup(), Task:GetName() .. " @ " .. TaskUnit:GetName() ):SetTime( MenuTime )
Task.SetCargo:ForEachCargo(
--- @param Core.Cargo#CARGO Cargo
function( Cargo )
if Cargo:IsAlive() then
-- if Task:is( "RoutingToPickup" ) then
-- MENU_GROUP_COMMAND:New(
-- TaskUnit:GetGroup(),
-- "Cancel Route " .. Cargo.Name,
-- TaskUnit.Menu,
-- self.MenuRouteToPickupCancel,
-- self,
-- Cargo
-- ):SetTime(MenuTime)
-- end
if Cargo:IsUnLoaded() then
if Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then
MENU_GROUP_COMMAND:New(
TaskUnit:GetGroup(),
"Board cargo " .. Cargo.Name,
TaskUnit.Menu,
self.MenuBoardCargo,
self,
Cargo
):SetTime(MenuTime)
else
MENU_GROUP_COMMAND:New(
TaskUnit:GetGroup(),
"Route to Pickup cargo " .. Cargo.Name,
TaskUnit.Menu,
self.MenuRouteToPickup,
self,
Cargo
):SetTime(MenuTime)
end
end
if Cargo:IsLoaded() then
MENU_GROUP_COMMAND:New(
TaskUnit:GetGroup(),
"Unboard cargo " .. Cargo.Name,
TaskUnit.Menu,
self.MenuUnBoardCargo,
self,
Cargo
):SetTime(MenuTime)
-- Deployzones are optional zones that can be selected to request routing information.
for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do
if not Cargo:IsInZone( DeployZone ) then
MENU_GROUP_COMMAND:New(
TaskUnit:GetGroup(),
"Route to Deploy cargo at " .. DeployZoneName,
TaskUnit.Menu,
self.MenuRouteToDeploy,
self,
DeployZone
):SetTime(MenuTime)
end
end
end
end
end
)
TaskUnit.Menu:Remove( MenuTime )
self:__SelectAction( -15 )
--Task:GetMission():GetCommandCenter():MessageToGroup("Cargo menu is ready ...", TaskUnit:GetGroup() )
end
---
-- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_Cargo#TASK_CARGO Task
function Fsm:OnLeaveWaitingForCommand( TaskUnit, Task )
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
TaskUnit.Menu:Remove()
end
function Fsm:MenuBoardCargo( Cargo )
self:__PrepareBoarding( 1.0, Cargo )
end
function Fsm:MenuUnBoardCargo( Cargo, DeployZone )
self:__PrepareUnBoarding( 1.0, Cargo, DeployZone )
end
function Fsm:MenuRouteToPickup( Cargo )
self:__RouteToPickup( 1.0, Cargo )
end
function Fsm:MenuRouteToDeploy( DeployZone )
self:__RouteToDeploy( 1.0, DeployZone )
end
--- Route to Cargo
-- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_Cargo#TASK_CARGO Task
-- @param From
-- @param Event
-- @param To
-- @param Core.Cargo#CARGO Cargo
function Fsm:onafterRouteToPickup( TaskUnit, Task, From, Event, To, Cargo )
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
if Cargo:IsAlive() then
self.Cargo = Cargo -- Core.Cargo#CARGO
Task:SetCargoPickup( self.Cargo, TaskUnit )
self:__RouteToPickupPoint( -0.1 )
end
end
---
-- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_Cargo#TASK_CARGO Task
function Fsm:onafterArriveAtPickup( TaskUnit, Task )
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
if self.Cargo:IsAlive() then
if TaskUnit:IsAir() then
self.Cargo.CargoObject:GetUnit(1):SmokeRed()
self:__Land( -0.1, "Pickup" )
else
self:__SelectAction( -0.1 )
end
end
end
---
-- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_Cargo#TASK_CARGO Task
function Fsm:onafterCancelRouteToPickup( TaskUnit, Task )
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
self:__SelectAction( -0.1 )
end
--- Route to DeployZone
-- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
function Fsm:onafterRouteToDeploy( TaskUnit, Task, From, Event, To, DeployZone )
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
self:E( DeployZone )
self.DeployZone = DeployZone
Task:SetDeployZone( self.DeployZone, TaskUnit )
self:__RouteToDeployZone( -0.1 )
end
---
-- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_Cargo#TASK_CARGO Task
function Fsm:onafterArriveAtDeploy( TaskUnit, Task )
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
if TaskUnit:IsAir() then
self:__Land( -0.1, "Deploy" )
else
self:__SelectAction( -0.1 )
end
end
---
-- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_Cargo#TASK_CARGO Task
function Fsm:onafterCancelRouteToDeploy( TaskUnit, Task )
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
self:__SelectAction( -0.1 )
end
-- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_Cargo#TASK_CARGO Task
function Fsm:onafterLand( TaskUnit, Task, From, Event, To, Action )
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
if self.Cargo:IsAlive() then
if self.Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then
if TaskUnit:InAir() then
Task:GetMission():GetCommandCenter():MessageToGroup( "Land", TaskUnit:GetGroup() )
self:__Land( -10, Action )
else
Task:GetMission():GetCommandCenter():MessageToGroup( "Landed ...", TaskUnit:GetGroup() )
self:__Landed( -0.1, Action )
end
else
if Action == "Pickup" then
self:__RouteToPickupZone( -0.1 )
else
self:__RouteToDeployZone( -0.1 )
end
end
end
end
---
-- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_Cargo#TASK_CARGO Task
function Fsm:onafterLanded( TaskUnit, Task, From, Event, To, Action )
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
if self.Cargo:IsAlive() then
if self.Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then
if TaskUnit:InAir() then
self:__Land( -0.1, Action )
else
self:__SelectAction( -0.1 )
end
else
if Action == "Pickup" then
self:__RouteToPickupZone( -0.1 )
else
self:__RouteToDeployZone( -0.1 )
end
end
end
end
---
-- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_Cargo#TASK_CARGO Task
function Fsm:onafterPrepareBoarding( TaskUnit, Task, From, Event, To, Cargo )
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
if Cargo and Cargo:IsAlive() then
self.Cargo = Cargo -- Core.Cargo#CARGO_GROUP
self:__Board( -0.1 )
end
end
---
-- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_Cargo#TASK_CARGO Task
function Fsm:onafterBoard( TaskUnit, Task )
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
function self.Cargo:OnEnterLoaded( From, Event, To, TaskUnit, TaskProcess )
self:E({From, Event, To, TaskUnit, TaskProcess })
TaskProcess:__Boarded( 0.1 )
end
if self.Cargo:IsAlive() then
if self.Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then
if TaskUnit:InAir() then
--- ABORT the boarding. Split group if any and go back to select action.
else
self.Cargo:MessageToGroup( "Boarding ...", TaskUnit:GetGroup() )
self.Cargo:Board( TaskUnit, 20, self )
end
else
--self:__ArriveAtCargo( -0.1 )
end
end
end
---
-- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_Cargo#TASK_CARGO Task
function Fsm:onafterBoarded( TaskUnit, Task )
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
self.Cargo:MessageToGroup( "Boarded ...", TaskUnit:GetGroup() )
self:__SelectAction( 1 )
-- TODO:I need to find a more decent solution for this.
Task:E( { CargoPickedUp = Task.CargoPickedUp } )
if self.Cargo:IsAlive() then
if Task.CargoPickedUp then
Task:CargoPickedUp( TaskUnit, self.Cargo )
end
end
end
---
-- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_Cargo#TASK_CARGO Task
-- @param From
-- @param Event
-- @param To
-- @param Cargo
-- @param Core.Zone#ZONE_BASE DeployZone
function Fsm:onafterPrepareUnBoarding( TaskUnit, Task, From, Event, To, Cargo )
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID(), From, Event, To, Cargo } )
self.Cargo = Cargo
self.DeployZone = nil
-- Check if the Cargo is at a deployzone... If it is, provide it as a parameter!
if Cargo:IsAlive() then
for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do
if Cargo:IsInZone( DeployZone ) then
self.DeployZone = DeployZone -- Core.Zone#ZONE_BASE
break
end
end
self:__UnBoard( -0.1, Cargo, self.DeployZone )
end
end
---
-- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_Cargo#TASK_CARGO Task
-- @param From
-- @param Event
-- @param To
-- @param Cargo
-- @param Core.Zone#ZONE_BASE DeployZone
function Fsm:onafterUnBoard( TaskUnit, Task, From, Event, To, Cargo, DeployZone )
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID(), From, Event, To, Cargo, DeployZone } )
function self.Cargo:OnEnterUnLoaded( From, Event, To, DeployZone, TaskProcess )
self:E({From, Event, To, DeployZone, TaskProcess })
TaskProcess:__UnBoarded( -0.1 )
end
if self.Cargo:IsAlive() then
self.Cargo:MessageToGroup( "UnBoarding ...", TaskUnit:GetGroup() )
if DeployZone then
self.Cargo:UnBoard( DeployZone:GetPointVec2(), 400, self )
else
self.Cargo:UnBoard( TaskUnit:GetPointVec2():AddX(60), 400, self )
end
end
end
---
-- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_Cargo#TASK_CARGO Task
function Fsm:onafterUnBoarded( TaskUnit, Task )
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
self.Cargo:MessageToGroup( "UnBoarded ...", TaskUnit:GetGroup() )
-- TODO:I need to find a more decent solution for this.
Task:E( { CargoDeployed = Task.CargoDeployed } )
if self.Cargo:IsAlive() then
if Task.CargoDeployed then
Task:CargoDeployed( TaskUnit, self.Cargo, self.DeployZone )
end
end
self:__SelectAction( 1 )
end
return self
end
--- @param #TASK_CARGO self
function TASK_CARGO:GetPlannedMenuText()
return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )"
end
--- @param #TASK_CARGO self
-- @return Core.Set#SET_CARGO The Cargo Set.
function TASK_CARGO:GetCargoSet()
return self.SetCargo
end
--- @param #TASK_CARGO self
-- @return #list<Core.Zone#ZONE_BASE> The Deployment Zones.
function TASK_CARGO:GetDeployZones()
return self.DeployZones
end
--- @param #TASK_CARGO self
-- @param AI.AI_Cargo#AI_CARGO Cargo The cargo.
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return #TASK_CARGO
function TASK_CARGO:SetCargoPickup( Cargo, TaskUnit )
self:F({Cargo, TaskUnit})
local ProcessUnit = self:GetUnitProcess( TaskUnit )
local ActRouteCargo = ProcessUnit:GetProcess( "RoutingToPickup", "RouteToPickupPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT
ActRouteCargo:Reset()
ActRouteCargo:SetCoordinate( Cargo:GetCoordinate() )
ActRouteCargo:SetRange( Cargo:GetBoardingRange() )
ActRouteCargo:SetMenuCancel( TaskUnit:GetGroup(), "Cancel Routing to Cargo " .. Cargo:GetName(), TaskUnit.Menu )
ActRouteCargo:Start()
return self
end
--- @param #TASK_CARGO self
-- @param Core.Zone#ZONE DeployZone
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return #TASK_CARGO
function TASK_CARGO:SetDeployZone( DeployZone, TaskUnit )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
local ActRouteDeployZone = ProcessUnit:GetProcess( "RoutingToDeploy", "RouteToDeployZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
ActRouteDeployZone:Reset()
ActRouteDeployZone:SetZone( DeployZone )
ActRouteDeployZone:SetMenuCancel( TaskUnit:GetGroup(), "Cancel Routing to Deploy Zone" .. DeployZone:GetName(), TaskUnit.Menu )
ActRouteDeployZone:Start()
return self
end
--- @param #TASK_CARGO self
-- @param Core.Zone#ZONE DeployZone
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return #TASK_CARGO
function TASK_CARGO:AddDeployZone( DeployZone, TaskUnit )
self.DeployZones[DeployZone:GetName()] = DeployZone
return self
end
--- @param #TASK_CARGO self
-- @param Core.Zone#ZONE DeployZone
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return #TASK_CARGO
function TASK_CARGO:RemoveDeployZone( DeployZone, TaskUnit )
self.DeployZones[DeployZone:GetName()] = nil
return self
end
--- @param #TASK_CARGO self
-- @param @list<Core.Zone#ZONE> DeployZones
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return #TASK_CARGO
function TASK_CARGO:SetDeployZones( DeployZones, TaskUnit )
for DeployZoneID, DeployZone in pairs( DeployZones ) do
self.DeployZones[DeployZone:GetName()] = DeployZone
end
return self
end
--- @param #TASK_CARGO self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return Core.Zone#ZONE_BASE The Zone object where the Target is located on the map.
function TASK_CARGO:GetTargetZone( TaskUnit )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
return ActRouteTarget:GetZone()
end
--- Set a score when a target in scope of the A2G attack, has been destroyed .
-- @param #TASK_CARGO self
-- @param #string Text The text to display to the player, when the target has been destroyed.
-- @param #number Score The score in points.
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return #TASK_CARGO
function TASK_CARGO:SetScoreOnDestroy( Text, Score, TaskUnit )
self:F( { Text, Score, TaskUnit } )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScoreProcess( "Engaging", "Account", "Account", Text, Score )
return self
end
--- Set a score when all the targets in scope of the A2G attack, have been destroyed.
-- @param #TASK_CARGO self
-- @param #string Text The text to display to the player, when all targets hav been destroyed.
-- @param #number Score The score in points.
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return #TASK_CARGO
function TASK_CARGO:SetScoreOnSuccess( Text, Score, TaskUnit )
self:F( { Text, Score, TaskUnit } )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScore( "Success", Text, Score )
return self
end
--- Set a penalty when the A2G attack has failed.
-- @param #TASK_CARGO self
-- @param #string Text The text to display to the player, when the A2G attack has failed.
-- @param #number Penalty The penalty in points.
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return #TASK_CARGO
function TASK_CARGO:SetPenaltyOnFailed( Text, Penalty, TaskUnit )
self:F( { Text, Score, TaskUnit } )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScore( "Failed", Text, Penalty )
return self
end
end
do -- TASK_CARGO_TRANSPORT
--- The TASK_CARGO_TRANSPORT class
-- @type TASK_CARGO_TRANSPORT
-- @extends #TASK_CARGO
TASK_CARGO_TRANSPORT = {
ClassName = "TASK_CARGO_TRANSPORT",
}
--- Instantiates a new TASK_CARGO_TRANSPORT.
-- @param #TASK_CARGO_TRANSPORT self
-- @param Tasking.Mission#MISSION Mission
-- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned.
-- @param #string TaskName The name of the Task.
-- @param Core.Set#SET_CARGO SetCargo The scope of the cargo to be transported.
-- @param #string TaskBriefing The Cargo Task briefing.
-- @return #TASK_CARGO_TRANSPORT self
function TASK_CARGO_TRANSPORT:New( Mission, SetGroup, TaskName, SetCargo, TaskBriefing )
local self = BASE:Inherit( self, TASK_CARGO:New( Mission, SetGroup, TaskName, SetCargo, "Transport", TaskBriefing ) ) -- #TASK_CARGO_TRANSPORT
self:F()
Mission:AddTask( self )
-- Events
self:AddTransition( "*", "CargoPickedUp", "*" )
self:AddTransition( "*", "CargoDeployed", "*" )
--- OnBefore Transition Handler for Event CargoPickedUp.
-- @function [parent=#TASK_CARGO_TRANSPORT] OnBeforeCargoPickedUp
-- @param #TASK_CARGO_TRANSPORT self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc.
-- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event CargoPickedUp.
-- @function [parent=#TASK_CARGO_TRANSPORT] OnAfterCargoPickedUp
-- @param #TASK_CARGO_TRANSPORT self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc.
-- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.
--- Synchronous Event Trigger for Event CargoPickedUp.
-- @function [parent=#TASK_CARGO_TRANSPORT] CargoPickedUp
-- @param #TASK_CARGO_TRANSPORT self
-- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc.
-- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.
--- Asynchronous Event Trigger for Event CargoPickedUp.
-- @function [parent=#TASK_CARGO_TRANSPORT] __CargoPickedUp
-- @param #TASK_CARGO_TRANSPORT self
-- @param #number Delay The delay in seconds.
-- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc.
-- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.
--- OnBefore Transition Handler for Event CargoDeployed.
-- @function [parent=#TASK_CARGO_TRANSPORT] OnBeforeCargoDeployed
-- @param #TASK_CARGO_TRANSPORT self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc.
-- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.
-- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event CargoDeployed.
-- @function [parent=#TASK_CARGO_TRANSPORT] OnAfterCargoDeployed
-- @param #TASK_CARGO_TRANSPORT self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc.
-- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.
-- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded.
--- Synchronous Event Trigger for Event CargoDeployed.
-- @function [parent=#TASK_CARGO_TRANSPORT] CargoDeployed
-- @param #TASK_CARGO_TRANSPORT self
-- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc.
-- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.
-- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded.
--- Asynchronous Event Trigger for Event CargoDeployed.
-- @function [parent=#TASK_CARGO_TRANSPORT] __CargoDeployed
-- @param #TASK_CARGO_TRANSPORT self
-- @param #number Delay The delay in seconds.
-- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc.
-- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status.
-- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded.
local Fsm = self:GetUnitProcess()
local CargoReport = REPORT:New( "Transport Cargo. The following cargo needs to be transported including initial positions:")
SetCargo:ForEachCargo(
--- @param Core.Cargo#CARGO Cargo
function( Cargo )
local CargoType = Cargo:GetType()
local CargoName = Cargo:GetName()
local CargoCoordinate = Cargo:GetCoordinate()
CargoReport:Add( string.format( '- "%s" (%s) at %s', CargoName, CargoType, CargoCoordinate:ToString() ) )
end
)
self:SetBriefing(
TaskBriefing or
CargoReport:Text()
)
return self
end
---
-- @param #TASK_CARGO_TRANSPORT self
-- @return #boolean
function TASK_CARGO_TRANSPORT:IsAllCargoTransported()
local CargoSet = self:GetCargoSet()
local Set = CargoSet:GetSet()
local DeployZones = self:GetDeployZones()
local CargoDeployed = true
-- Loop the CargoSet (so evaluate each Cargo in the SET_CARGO ).
for CargoID, CargoData in pairs( Set ) do
local Cargo = CargoData -- Core.Cargo#CARGO
-- Loop the DeployZones set for the TASK_CARGO_TRANSPORT.
for DeployZoneID, DeployZone in pairs( DeployZones ) do
-- If there is a Cargo not in one of DeployZones, then not all Cargo is deployed.
self:T( { Cargo.CargoObject } )
if Cargo:IsInZone( DeployZone ) then
else
CargoDeployed = false
end
end
end
return CargoDeployed
end
end

View File

@@ -41,11 +41,6 @@ do -- TASK_PICKUP
local self = BASE:Inherit( self, TASK:New( Mission, AssignedSetGroup, TaskName, TaskType, "PICKUP" ) )
self:F()
_EVENTDISPATCHER:OnPlayerLeaveUnit( self._EventPlayerLeaveUnit, self )
_EVENTDISPATCHER:OnDead( self._EventDead, self )
_EVENTDISPATCHER:OnCrash( self._EventDead, self )
_EVENTDISPATCHER:OnPilotDead( self._EventDead, self )
return self
end

View File

@@ -1,78 +0,0 @@
--- This module contains the TASK_SEAD classes.
--
-- 1) @{#TASK_SEAD} class, extends @{Task#TASK}
-- =================================================
-- The @{#TASK_SEAD} class defines a SEAD task for a @{Set} of Target Units, located at a Target Zone,
-- based on the tasking capabilities defined in @{Task#TASK}.
-- The TASK_SEAD is implemented using a @{Statemachine#FSM_TASK}, and has the following statuses:
--
-- * **None**: Start of the process
-- * **Planned**: The SEAD task is planned. Upon Planned, the sub-process @{Process_Fsm.Assign#ACT_ASSIGN_ACCEPT} is started to accept the task.
-- * **Assigned**: The SEAD task is assigned to a @{Group#GROUP}. Upon Assigned, the sub-process @{Process_Fsm.Route#ACT_ROUTE} is started to route the active Units in the Group to the attack zone.
-- * **Success**: The SEAD task is successfully completed. Upon Success, the sub-process @{Process_SEAD#PROCESS_SEAD} is started to follow-up successful SEADing of the targets assigned in the task.
-- * **Failed**: The SEAD task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ.
--
-- ===
--
-- ### Authors: FlightControl - Design and Programming
--
-- @module Task_SEAD
do -- TASK_SEAD
--- The TASK_SEAD class
-- @type TASK_SEAD
-- @field Set#SET_UNIT TargetSetUnit
-- @extends Tasking.Task#TASK
TASK_SEAD = {
ClassName = "TASK_SEAD",
}
--- Instantiates a new TASK_SEAD.
-- @param #TASK_SEAD self
-- @param Tasking.Mission#MISSION Mission
-- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned.
-- @param #string TaskName The name of the Task.
-- @param Set#SET_UNIT UnitSetTargets
-- @param Core.Zone#ZONE_BASE TargetZone
-- @return #TASK_SEAD self
function TASK_SEAD:New( Mission, SetGroup, TaskName, TargetSetUnit, TargetZone )
local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, "SEAD" ) ) -- Tasking.Task_SEAD#TASK_SEAD
self:F()
self.TargetSetUnit = TargetSetUnit
self.TargetZone = TargetZone
local Fsm = self:GetUnitProcess()
Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "Route", Rejected = "Eject" } )
Fsm:AddProcess ( "Assigned", "Route", ACT_ROUTE_ZONE:New( self.TargetZone ), { Arrived = "Update" } )
Fsm:AddTransition( "Rejected", "Eject", "Planned" )
Fsm:AddTransition( "Arrived", "Update", "Updated" )
Fsm:AddProcess ( "Updated", "Account", ACT_ACCOUNT_DEADS:New( self.TargetSetUnit, "SEAD" ), { Accounted = "Success" } )
Fsm:AddProcess ( "Updated", "Smoke", ACT_ASSIST_SMOKE_TARGETS_ZONE:New( self.TargetSetUnit, self.TargetZone ) )
Fsm:AddTransition( "Accounted", "Success", "Success" )
Fsm:AddTransition( "Failed", "Fail", "Failed" )
function Fsm:onenterUpdated( TaskUnit )
self:E( { self } )
self:Account()
self:Smoke()
end
-- _EVENTDISPATCHER:OnPlayerLeaveUnit( self._EventPlayerLeaveUnit, self )
-- _EVENTDISPATCHER:OnDead( self._EventDead, self )
-- _EVENTDISPATCHER:OnCrash( self._EventDead, self )
-- _EVENTDISPATCHER:OnPilotDead( self._EventDead, self )
return self
end
--- @param #TASK_SEAD self
function TASK_SEAD:GetPlannedMenuText()
return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )"
end
end

View File

@@ -1,246 +0,0 @@
--- Provides a logging of statistics in a running DCS Mission.
-- @script eStatHandler
--Handler table
local eStatHandler = {}
local _StatRunID
--Neccessary tables for string instead of integers
SETCoalition =
{
[1] = "red",
[2] = "blue",
}
SETGroupCat =
{
[1] = "AIRPLANE",
[2] = "HELICOPTER",
[3] = "GROUND",
[4] = "SHIP",
[5] = "STRUCTURE",
}
SETWeaponCatName =
{
[0] = "SHELL",
[1] = "MISSILE",
[2] = "ROCKET",
[3] = "BOMB",
}
wEvent = {
"S_EVENT_SHOT",
"S_EVENT_HIT",
"S_EVENT_TAKEOFF",
"S_EVENT_LAND",
"S_EVENT_CRASH",
"S_EVENT_EJECTION",
"S_EVENT_REFUELING",
"S_EVENT_DEAD",
"S_EVENT_PILOT_DEAD",
"S_EVENT_BASE_CAPTURED",
"S_EVENT_MISSION_START",
"S_EVENT_MISSION_END",
"S_EVENT_TOOK_CONTROL",
"S_EVENT_REFUELING_STOP",
"S_EVENT_BIRTH",
"S_EVENT_HUMAN_FAILURE",
"S_EVENT_ENGINE_STARTUP",
"S_EVENT_ENGINE_SHUTDOWN",
"S_EVENT_PLAYER_ENTER_UNIT",
"S_EVENT_PLAYER_LEAVE_UNIT",
"S_EVENT_PLAYER_COMMENT",
"S_EVENT_SHOOTING_START",
"S_EVENT_SHOOTING_END",
"S_EVENT_MAX",
}
statEventsTable = {}
function SecondsToClock(sSeconds)
local nSeconds = sSeconds
if nSeconds == 0 then
--return nil;
return "00:00:00";
else
nHours = string.format("%02.f", math.floor(nSeconds/3600));
nMins = string.format("%02.f", math.floor(nSeconds/60 - (nHours*60)));
nSecs = string.format("%02.f", math.floor(nSeconds - nHours*3600 - nMins *60));
return nHours..":"..nMins..":"..nSecs
end
end
function eStatHandler:onEvent(e)
local InitID_ = ""
local InitName = ""
local WorldEvent = wEvent[e.id]
local InitCoa = ""
local InitGroupCat = ""
local InitType = ""
local InitPlayer = ""
local eWeaponCat = ""
local eWeaponName = ""
local TargID_ = ""
local TargName = ""
local TargType = ""
local TargPlayer = ""
local TargCoa = ""
local TargGroupCat = ""
if e.initiator and Object.getCategory(e.initiator) == Object.Category.UNIT then
--Initiator variables
local InitGroup = e.initiator:getGroup()
InitID_ = e.initiator.id_
if e.initiator:getName() then
InitName = e.initiator:getName()
end
if InitGroup:getCoalition() then
InitCoa = SETCoalition[InitGroup:getCoalition()]
end
if InitGroup:getCategory() then
InitGroupCat = SETGroupCat[InitGroup:getCategory() + 1]
end
InitType = e.initiator:getTypeName()
--Get initiator player name or AI if NIL
if e.initiator:getPlayerName() == nil then
InitPlayer = "AI"
else
InitPlayer = e.initiator:getPlayerName()
end
else
if e.initiator then
local InitGroup = e.initiator:getGroup()
InitID_ = e.initiator.id_
if e.initiator:getName() then
InitName = e.initiator:getName()
end
InitCoa = SETCoalition[InitGroup:getCoalition()]
InitGroupCat = SETGroupCat[InitGroup:getCategory() + 1]
InitType = e.initiator:getTypeName()
--Get initiator player name or AI if NIL
if e.initiator:getPlayerName() == nil then
InitPlayer = "AI"
else
InitPlayer = e.initiator:getPlayerName()
end
end
end
--Weapon variables
if e.weapon == nil then
eWeaponCat = ""
eWeaponName = ""
else
local eWeaponDesc = e.weapon:getDesc()
eWeaponCat = SETWeaponCatName[eWeaponDesc.category]
eWeaponName = eWeaponDesc.displayName
end
--Target variables
if e.target == nil then
TargID_ = ""
TargName = ""
TargType = ""
TargPlayer = ""
TargCoa = ""
TargGroupCat = ""
elseif Object.getCategory(e.target) == Object.Category.UNIT then
local TargGroup = e.target:getGroup()
TargID_ = e.target.id_
if e.target:getName() then
TargName = e.target:getName()
end
TargType = e.target:getTypeName()
TargCoa = SETCoalition[TargGroup:getCoalition()]
TargGroupCat = SETGroupCat[TargGroup:getCategory() + 1]
--Get target player name or AI if NIL
if not e.target:getPlayerName() then
TargPlayer = "AI"
else
TargPlayer = e.target:getPlayerName()
end
else
TargType = e.target:getTypeName()
TargID_ = ""
TargName = ""
TargPlayer = ""
TargCoa = ""
TargGroupCat = ""
end
--write events to table
statEventsTable[#statEventsTable + 1] =
{
[1] = _StatRunID,
[2] = SecondsToClock(timer.getTime()),
[3] = WorldEvent,
[4] = InitID_,
[5] = InitName,
[6] = InitCoa,
[7] = InitGroupCat,
[8] = InitType,
[9] = InitPlayer,
[10] = eWeaponCat,
[11] = eWeaponName,
[12] = TargID_,
[13] = TargName,
[14] = TargCoa,
[15] = TargGroupCat,
[16] = TargType,
[17] = TargPlayer,
}
env.info( 'Event: ' .. _StatRunID .. '~ ' .. SecondsToClock(timer.getTime()) .. '~ ' .. WorldEvent .. '~ ' .. InitID_ .. '~ ' .. InitName .. '~ ' .. InitCoa .. '~ ' .. InitGroupCat .. '~ ' .. InitType .. '~ ' .. InitPlayer ..
'~ ' .. eWeaponCat .. '~ ' .. eWeaponName .. '~ ' .. TargID_ .. '~ ' .. TargName .. '~ ' .. TargCoa .. '~ ' .. TargGroupCat .. '~ ' .. TargType .. '~ ' .. TargPlayer )
end
do
local StatFile,err
function StatOpen()
local fdir = lfs.writedir() .. [[Logs\]] .. "Events_" .. os.date( "%Y-%m-%d_%H-%M-%S" ) .. ".csv"
StatFile,err = io.open(fdir,"w+")
if not StatFile then
local errmsg = 'Error: No Logs folder found in the User\\Saved Games\\DCS\\Logs directory...' .. 'Save_stat . sample: C:\\Users\\youname\\Saved Games\\DCS\\Logs'
trigger.action.outText(errmsg, 10)
return print(err)
end
StatFile:write("RunID~Time~Event~Initiator ID~Initiator Name~Initiator Coalition~Initiator Group Category~Initiator Type~Initiator Player~Weapon Category~Weapon Name~Target ID~Target Name~Target Coalition~Target Group Category~Target Type~Target Player\n")
_StatRunID = os.date("%y-%m-%d_%H-%M-%S")
routines.scheduleFunction( StatSave, { }, timer.getTime() + 1, 1)
end
function StatSave()
--write statistic information to file
for Index, eDetails in ipairs(statEventsTable) do
for eInfoName, eInfoData in ipairs(eDetails) do
StatFile:write(eInfoData.."~")
end
StatFile:write("\n")
end
statEventsTable = {}
end
function StatClose()
StatFile:close()
end
end
world.addEventHandler(eStatHandler)
StatOpen()

View File

@@ -276,6 +276,16 @@ UTILS.tostringLL = function( lat, lon, acc, DMS)
end
end
-- acc- the accuracy of each easting/northing. 0, 1, 2, 3, 4, or 5.
UTILS.tostringMGRS = function(MGRS, acc) --R2.1
if acc == 0 then
return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph
else
return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph .. ' ' .. string.format('%0' .. acc .. 'd', UTILS.Round(MGRS.Easting/(10^(5-acc)), 0))
.. ' ' .. string.format('%0' .. acc .. 'd', UTILS.Round(MGRS.Northing/(10^(5-acc)), 0))
end
end
--- From http://lua-users.org/wiki/SimpleRound
-- use negative idp for rounding ahead of decimal place, positive for rounding after decimal place

View File

@@ -73,7 +73,7 @@ CLIENT = {
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*RAMP-Deploy Troops 3' ):Transport() )
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*HOT-Deploy Troops 2' ):Transport() )
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*RAMP-Deploy Troops 4' ):Transport() )
function CLIENT:Find( DCSUnit )
function CLIENT:Find( DCSUnit, Error )
local ClientName = DCSUnit:getName()
local ClientFound = _DATABASE:FindClient( ClientName )
@@ -82,7 +82,9 @@ function CLIENT:Find( DCSUnit )
return ClientFound
end
error( "CLIENT not found for: " .. ClientName )
if not Error then
error( "CLIENT not found for: " .. ClientName )
end
end

View File

@@ -30,7 +30,7 @@
--
-- Find below a list of the **assigned task** methods:
--
-- * @{#CONTROLLABLE.TaskAttackControllable}: (AIR) Attack a Controllable.
-- * @{#CONTROLLABLE.TaskAttackGroup}: (AIR) Attack a Controllable.
-- * @{#CONTROLLABLE.TaskAttackMapObject}: (AIR) Attacking the map object (building, structure, e.t.c).
-- * @{#CONTROLLABLE.TaskAttackUnit}: (AIR) Attack the Unit.
-- * @{#CONTROLLABLE.TaskBombing}: (AIR) Delivering weapon at the point on the ground.
@@ -38,7 +38,7 @@
-- * @{#CONTROLLABLE.TaskEmbarking}: (AIR) Move the controllable to a Vec2 Point, wait for a defined duration and embark a controllable.
-- * @{#CONTROLLABLE.TaskEmbarkToTransport}: (GROUND) Embark to a Transport landed at a location.
-- * @{#CONTROLLABLE.TaskEscort}: (AIR) Escort another airborne controllable.
-- * @{#CONTROLLABLE.TaskFAC_AttackControllable}: (AIR + GROUND) The task makes the controllable/unit a FAC and orders the FAC to control the target (enemy ground controllable) destruction.
-- * @{#CONTROLLABLE.TaskFAC_AttackGroup}: (AIR + GROUND) The task makes the controllable/unit a FAC and orders the FAC to control the target (enemy ground controllable) destruction.
-- * @{#CONTROLLABLE.TaskFireAtPoint}: (GROUND) Fire some or all ammunition at a VEC2 point.
-- * @{#CONTROLLABLE.TaskFollow}: (AIR) Following another airborne controllable.
-- * @{#CONTROLLABLE.TaskHold}: (GROUND) Hold ground controllable from moving.
@@ -222,6 +222,24 @@ end
-- Tasks
--- Clear all tasks from the controllable.
-- @param #CONTROLLABLE self
-- @return #CONTROLLABLE
function CONTROLLABLE:ClearTasks()
self:F2()
local DCSControllable = self:GetDCSObject()
if DCSControllable then
local Controller = self:_GetController()
Controller:resetTask()
return self
end
return nil
end
--- Popping current Task from the controllable.
-- @param #CONTROLLABLE self
-- @return Wrapper.Controllable#CONTROLLABLE self
@@ -439,12 +457,20 @@ function CONTROLLABLE:CommandSwitchWayPoint( FromWayPoint, ToWayPoint )
return CommandSwitchWayPoint
end
--- Perform stop route command
--- Create a stop route command, which returns a string containing the command.
-- Use the result in the method @{#CONTROLLABLE.SetCommand}().
-- A value of true will make the ground group stop, a value of false will make it continue.
-- Note that this can only work on GROUP level, although individual UNITs can be commanded, the whole GROUP will react.
--
-- Example missions:
--
-- * GRP-310
--
-- @param #CONTROLLABLE self
-- @param #boolean StopRoute
-- @param #boolean StopRoute true if the ground unit needs to stop, false if it needs to continue to move.
-- @return Dcs.DCSTasking.Task#Task
function CONTROLLABLE:CommandStopRoute( StopRoute, Index )
self:F2( { StopRoute, Index } )
function CONTROLLABLE:CommandStopRoute( StopRoute )
self:F2( { StopRoute } )
local CommandStopRoute = {
id = 'StopRoute',
@@ -469,13 +495,13 @@ end
-- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.
-- @param Dcs.DCSTypes#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.
-- @param Dcs.DCSTypes#Distance Altitude (optional) Desired attack start altitude. Controllable/aircraft will make its attacks from the altitude. If the altitude is too low or too high to use weapon aircraft/controllable will choose closest altitude to the desired attack start altitude. If the desired altitude is defined controllable/aircraft will not attack from safe altitude.
-- @param #boolean AttackQtyLimit (optional) The flag determines how to interpret attackQty parameter. If the flag is true then attackQty is a limit on maximal attack quantity for "AttackControllable" and "AttackUnit" tasks. If the flag is false then attackQty is a desired attack quantity for "Bombing" and "BombingRunway" tasks.
-- @param #boolean AttackQtyLimit (optional) The flag determines how to interpret attackQty parameter. If the flag is true then attackQty is a limit on maximal attack quantity for "AttackGroup" and "AttackUnit" tasks. If the flag is false then attackQty is a desired attack quantity for "Bombing" and "BombingRunway" tasks.
-- @return Dcs.DCSTasking.Task#Task The DCS task structure.
function CONTROLLABLE:TaskAttackGroup( AttackGroup, WeaponType, WeaponExpend, AttackQty, Direction, Altitude, AttackQtyLimit )
self:F2( { self.ControllableName, AttackGroup, WeaponType, WeaponExpend, AttackQty, Direction, Altitude, AttackQtyLimit } )
-- AttackControllable = {
-- id = 'AttackControllable',
-- AttackGroup = {
-- id = 'AttackGroup',
-- params = {
-- groupId = Group.ID,
-- weaponType = number,
@@ -500,7 +526,7 @@ function CONTROLLABLE:TaskAttackGroup( AttackGroup, WeaponType, WeaponExpend, At
end
local DCSTask
DCSTask = { id = 'AttackControllable',
DCSTask = { id = 'AttackGroup',
params = {
groupId = AttackGroup:GetID(),
weaponType = WeaponType,
@@ -518,47 +544,35 @@ function CONTROLLABLE:TaskAttackGroup( AttackGroup, WeaponType, WeaponExpend, At
return DCSTask
end
--- (AIR) Attack the Unit.
-- @param #CONTROLLABLE self
-- @param Wrapper.Unit#UNIT AttackUnit The unit.
-- @param #number WeaponType (optional) Bitmask of weapon types those allowed to use. If parameter is not defined that means no limits on weapon usage.
-- @param Wrapper.Unit#UNIT AttackUnit The UNIT.
-- @param #boolean GroupAttack (optional) If true, all units in the group will attack the Unit when found.
-- @param Dcs.DCSTypes#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion.
-- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.
-- @param Dcs.DCSTypes#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.
-- @param #boolean AttackQtyLimit (optional) The flag determines how to interpret attackQty parameter. If the flag is true then attackQty is a limit on maximal attack quantity for "AttackControllable" and "AttackUnit" tasks. If the flag is false then attackQty is a desired attack quantity for "Bombing" and "BombingRunway" tasks.
-- @param #boolean ControllableAttack (optional) Flag indicates that the target must be engaged by all aircrafts of the controllable. Has effect only if the task is assigned to a controllable, not to a single aircraft.
-- @param #number Altitude (optional) The altitude from where to attack.
-- @param #boolean Visible (optional) not a clue.
-- @param #number WeaponType (optional) The WeaponType.
-- @return Dcs.DCSTasking.Task#Task The DCS task structure.
function CONTROLLABLE:TaskAttackUnit( AttackUnit, WeaponType, WeaponExpend, AttackQty, Direction, AttackQtyLimit, ControllableAttack )
self:F2( { self.ControllableName, AttackUnit, WeaponType, WeaponExpend, AttackQty, Direction, AttackQtyLimit, ControllableAttack } )
-- AttackUnit = {
-- id = 'AttackUnit',
-- params = {
-- unitId = Unit.ID,
-- weaponType = number,
-- expend = enum AI.Task.WeaponExpend
-- attackQty = number,
-- direction = Azimuth,
-- attackQtyLimit = boolean,
-- controllableAttack = boolean,
-- }
-- }
function CONTROLLABLE:TaskAttackUnit( AttackUnit, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, Visible, WeaponType )
self:F2( { self.ControllableName, AttackUnit, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, Visible, WeaponType } )
local DCSTask
DCSTask = {
id = 'AttackUnit',
params = {
altitudeEnabled = true,
unitId = AttackUnit:GetID(),
attackQtyLimit = AttackQtyLimit or false,
attackQty = AttackQty or 2,
groupAttack = GroupAttack or false,
visible = Visible or false,
expend = WeaponExpend or "Auto",
altitude = 2000,
directionEnabled = true,
groupAttack = true,
--weaponType = WeaponType or 1073741822,
direction = Direction or 0,
directionEnabled = Direction and true or false,
direction = Direction,
altitudeEnabled = Altitude and true or false,
altitude = Altitude or 30,
attackQtyLimit = AttackQty and true or false,
attackQty = AttackQty,
weaponType = WeaponType
}
}
@@ -571,36 +585,64 @@ end
--- (AIR) Delivering weapon at the point on the ground.
-- @param #CONTROLLABLE self
-- @param Dcs.DCSTypes#Vec2 Vec2 2D-coordinates of the point to deliver weapon at.
-- @param #number WeaponType (optional) Bitmask of weapon types those allowed to use. If parameter is not defined that means no limits on weapon usage.
-- @param #boolean GroupAttack (optional) If true, all units in the group will attack the Unit when found.
-- @param Dcs.DCSTypes#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion.
-- @param #number AttackQty (optional) Desired quantity of passes. The parameter is not the same in AttackControllable and AttackUnit tasks.
-- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.
-- @param Dcs.DCSTypes#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.
-- @param #boolean ControllableAttack (optional) Flag indicates that the target must be engaged by all aircrafts of the controllable. Has effect only if the task is assigned to a controllable, not to a single aircraft.
-- @param #number Altitude (optional) The altitude from where to attack.
-- @param #number WeaponType (optional) The WeaponType.
-- @return Dcs.DCSTasking.Task#Task The DCS task structure.
function CONTROLLABLE:TaskBombing( Vec2, WeaponType, WeaponExpend, AttackQty, Direction, ControllableAttack )
self:F2( { self.ControllableName, Vec2, WeaponType, WeaponExpend, AttackQty, Direction, ControllableAttack } )
-- Bombing = {
-- id = 'Bombing',
-- params = {
-- point = Vec2,
-- weaponType = number,
-- expend = enum AI.Task.WeaponExpend,
-- attackQty = number,
-- direction = Azimuth,
-- controllableAttack = boolean,
-- }
-- }
function CONTROLLABLE:TaskBombing( Vec2, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, WeaponType )
self:F2( { self.ControllableName, Vec2, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, WeaponType } )
local DCSTask
DCSTask = { id = 'Bombing',
DCSTask = {
id = 'Bombing',
params = {
point = Vec2,
weaponType = WeaponType,
expend = WeaponExpend,
attackQty = AttackQty,
direction = Direction,
controllableAttack = ControllableAttack,
point = Vec2,
groupAttack = GroupAttack or false,
expend = WeaponExpend or "Auto",
attackQtyLimit = AttackQty and true or false,
attackQty = AttackQty,
directionEnabled = Direction and true or false,
direction = Direction,
altitudeEnabled = Altitude and true or false,
altitude = Altitude or 30,
weaponType = WeaponType,
},
},
self:T3( { DCSTask } )
return DCSTask
end
--- (AIR) Attacking the map object (building, structure, e.t.c).
-- @param #CONTROLLABLE self
-- @param Dcs.DCSTypes#Vec2 Vec2 2D-coordinates of the point to deliver weapon at.
-- @param #boolean GroupAttack (optional) If true, all units in the group will attack the Unit when found.
-- @param Dcs.DCSTypes#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion.
-- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.
-- @param Dcs.DCSTypes#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.
-- @param #number Altitude (optional) The altitude from where to attack.
-- @param #number WeaponType (optional) The WeaponType.
-- @return Dcs.DCSTasking.Task#Task The DCS task structure.
function CONTROLLABLE:TaskAttackMapObject( Vec2, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, WeaponType )
self:F2( { self.ControllableName, Vec2, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, WeaponType } )
local DCSTask
DCSTask = {
id = 'AttackMapObject',
params = {
point = Vec2,
groupAttack = GroupAttack or false,
expend = WeaponExpend or "Auto",
attackQtyLimit = AttackQty and true or false,
attackQty = AttackQty,
directionEnabled = Direction and true or false,
direction = Direction,
altitudeEnabled = Altitude and true or false,
altitude = Altitude or 30,
weaponType = WeaponType,
},
},
@@ -608,6 +650,7 @@ function CONTROLLABLE:TaskBombing( Vec2, WeaponType, WeaponExpend, AttackQty, Di
return DCSTask
end
--- (AIR) Orbit at a specified position at a specified alititude during a specified duration with a specified speed.
-- @param #CONTROLLABLE self
-- @param Dcs.DCSTypes#Vec2 Point The point to hold the position.
@@ -686,45 +729,6 @@ end
--- (AIR) Attacking the map object (building, structure, e.t.c).
-- @param #CONTROLLABLE self
-- @param Dcs.DCSTypes#Vec2 Vec2 2D-coordinates of the point the map object is closest to. The distance between the point and the map object must not be greater than 2000 meters. Object id is not used here because Mission Editor doesn't support map object identificators.
-- @param #number WeaponType (optional) Bitmask of weapon types those allowed to use. If parameter is not defined that means no limits on weapon usage.
-- @param Dcs.DCSTypes#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion.
-- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.
-- @param Dcs.DCSTypes#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.
-- @param #boolean ControllableAttack (optional) Flag indicates that the target must be engaged by all aircrafts of the controllable. Has effect only if the task is assigned to a controllable, not to a single aircraft.
-- @return Dcs.DCSTasking.Task#Task The DCS task structure.
function CONTROLLABLE:TaskAttackMapObject( Vec2, WeaponType, WeaponExpend, AttackQty, Direction, ControllableAttack )
self:F2( { self.ControllableName, Vec2, WeaponType, WeaponExpend, AttackQty, Direction, ControllableAttack } )
-- AttackMapObject = {
-- id = 'AttackMapObject',
-- params = {
-- point = Vec2,
-- weaponType = number,
-- expend = enum AI.Task.WeaponExpend,
-- attackQty = number,
-- direction = Azimuth,
-- controllableAttack = boolean,
-- }
-- }
local DCSTask
DCSTask = { id = 'AttackMapObject',
params = {
point = Vec2,
weaponType = WeaponType,
expend = WeaponExpend,
attackQty = AttackQty,
direction = Direction,
controllableAttack = ControllableAttack,
},
},
self:T3( { DCSTask } )
return DCSTask
end
--- (AIR) Delivering weapon on the runway.
@@ -1018,8 +1022,8 @@ end
function CONTROLLABLE:TaskFAC_AttackGroup( AttackGroup, WeaponType, Designation, Datalink )
self:F2( { self.ControllableName, AttackGroup, WeaponType, Designation, Datalink } )
-- FAC_AttackControllable = {
-- id = 'FAC_AttackControllable',
-- FAC_AttackGroup = {
-- id = 'FAC_AttackGroup',
-- params = {
-- groupId = Group.ID,
-- weaponType = number,
@@ -1029,7 +1033,7 @@ function CONTROLLABLE:TaskFAC_AttackGroup( AttackGroup, WeaponType, Designation,
-- }
local DCSTask
DCSTask = { id = 'FAC_AttackControllable',
DCSTask = { id = 'FAC_AttackGroup',
params = {
groupId = AttackGroup:GetID(),
weaponType = WeaponType,
@@ -1121,7 +1125,7 @@ end
-- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo.
-- @param Dcs.DCSTypes#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction.
-- @param Dcs.DCSTypes#Distance Altitude (optional) Desired attack start altitude. Controllable/aircraft will make its attacks from the altitude. If the altitude is too low or too high to use weapon aircraft/controllable will choose closest altitude to the desired attack start altitude. If the desired altitude is defined controllable/aircraft will not attack from safe altitude.
-- @param #boolean AttackQtyLimit (optional) The flag determines how to interpret attackQty parameter. If the flag is true then attackQty is a limit on maximal attack quantity for "AttackControllable" and "AttackUnit" tasks. If the flag is false then attackQty is a desired attack quantity for "Bombing" and "BombingRunway" tasks.
-- @param #boolean AttackQtyLimit (optional) The flag determines how to interpret attackQty parameter. If the flag is true then attackQty is a limit on maximal attack quantity for "AttackGroup" and "AttackUnit" tasks. If the flag is false then attackQty is a desired attack quantity for "Bombing" and "BombingRunway" tasks.
-- @return Dcs.DCSTasking.Task#Task The DCS task structure.
function CONTROLLABLE:EnRouteTaskEngageGroup( AttackGroup, Priority, WeaponType, WeaponExpend, AttackQty, Direction, Altitude, AttackQtyLimit )
self:F2( { self.ControllableName, AttackGroup, Priority, WeaponType, WeaponExpend, AttackQty, Direction, Altitude, AttackQtyLimit } )
@@ -1173,7 +1177,7 @@ function CONTROLLABLE:EnRouteTaskEngageGroup( AttackGroup, Priority, WeaponType,
end
--- (AIR) Attack the Unit.
--- (AIR) Search and attack the Unit.
-- @param #CONTROLLABLE self
-- @param Wrapper.Unit#UNIT EngageUnit The UNIT.
-- @param #number Priority (optional) All en-route tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first.
@@ -1454,7 +1458,7 @@ end
-- @param Dcs.DCSTypes#Vec3 Point The destination point in Vec3 format.
-- @param #number Speed The speed to travel.
-- @return #CONTROLLABLE self
function CONTROLLABLE:TaskRouteToVec2( Point, Speed )
function CONTROLLABLE:RouteToVec2( Point, Speed )
self:F2( { Point, Speed } )
local ControllablePoint = self:GetUnit( 1 ):GetVec2()
@@ -1505,7 +1509,7 @@ end
-- @param Dcs.DCSTypes#Vec3 Point The destination point in Vec3 format.
-- @param #number Speed The speed to travel.
-- @return #CONTROLLABLE self
function CONTROLLABLE:TaskRouteToVec3( Point, Speed )
function CONTROLLABLE:RouteToVec3( Point, Speed )
self:F2( { Point, Speed } )
local ControllableVec3 = self:GetUnit( 1 ):GetVec3()
@@ -1602,7 +1606,7 @@ function CONTROLLABLE:TaskRouteToZone( Zone, Randomize, Speed, Formation )
PointFrom.x = ControllablePoint.x
PointFrom.y = ControllablePoint.y
PointFrom.type = "Turning Point"
PointFrom.action = "Cone"
PointFrom.action = Formation or "Cone"
PointFrom.speed = 20 / 1.6
@@ -1643,90 +1647,6 @@ function CONTROLLABLE:TaskRouteToZone( Zone, Randomize, Speed, Formation )
return nil
end
--- (AIR) Return the Controllable to an @{Airbase#AIRBASE}
-- A speed can be given in km/h.
-- A given formation can be given.
-- @param #CONTROLLABLE self
-- @param Wrapper.Airbase#AIRBASE ReturnAirbase The @{Airbase#AIRBASE} to return to.
-- @param #number Speed (optional) The speed.
-- @return #string The route
function CONTROLLABLE:RouteReturnToAirbase( ReturnAirbase, Speed )
self:F2( { ReturnAirbase, Speed } )
-- Example
-- [4] =
-- {
-- ["alt"] = 45,
-- ["type"] = "Land",
-- ["action"] = "Landing",
-- ["alt_type"] = "BARO",
-- ["formation_template"] = "",
-- ["properties"] =
-- {
-- ["vnav"] = 1,
-- ["scale"] = 0,
-- ["angle"] = 0,
-- ["vangle"] = 0,
-- ["steer"] = 2,
-- }, -- end of ["properties"]
-- ["ETA"] = 527.81058817743,
-- ["airdromeId"] = 12,
-- ["y"] = 243127.2973737,
-- ["x"] = -5406.2803440839,
-- ["name"] = "DictKey_WptName_53",
-- ["speed"] = 138.88888888889,
-- ["ETA_locked"] = false,
-- ["task"] =
-- {
-- ["id"] = "ComboTask",
-- ["params"] =
-- {
-- ["tasks"] =
-- {
-- }, -- end of ["tasks"]
-- }, -- end of ["params"]
-- }, -- end of ["task"]
-- ["speed_locked"] = true,
-- }, -- end of [4]
local DCSControllable = self:GetDCSObject()
if DCSControllable then
local ControllablePoint = self:GetVec2()
local ControllableVelocity = self:GetMaxVelocity()
local PointFrom = {}
PointFrom.x = ControllablePoint.x
PointFrom.y = ControllablePoint.y
PointFrom.type = "Turning Point"
PointFrom.action = "Turning Point"
PointFrom.speed = ControllableVelocity
local PointTo = {}
local AirbasePoint = ReturnAirbase:GetVec2()
PointTo.x = AirbasePoint.x
PointTo.y = AirbasePoint.y
PointTo.type = "Land"
PointTo.action = "Landing"
PointTo.airdromeId = ReturnAirbase:GetID()-- Airdrome ID
self:T(PointTo.airdromeId)
--PointTo.alt = 0
local Points = { PointFrom, PointTo }
self:T3( Points )
local Route = { points = Points, }
return Route
end
return nil
end
-- Commands
@@ -1767,6 +1687,8 @@ function CONTROLLABLE:GetTaskRoute()
return routines.utils.deepCopy( _DATABASE.Templates.Controllables[self.ControllableName].Template.route.points )
end
--- Return the route of a controllable by using the @{Database#DATABASE} class.
-- @param #CONTROLLABLE self
-- @param #number Begin The route point from where the copy will start. The base route point is 0.
@@ -1842,6 +1764,7 @@ function CONTROLLABLE:GetDetectedTargets( DetectVisual, DetectOptical, DetectRad
local DetectionRWR = ( DetectRWR and DetectRWR == true ) and Controller.Detection.RWR or nil
local DetectionDLINK = ( DetectDLINK and DetectDLINK == true ) and Controller.Detection.DLINK or nil
self:T( { DetectionVisual, DetectionOptical, DetectionRadar, DetectionIRST, DetectionRWR, DetectionDLINK } )
return self:_GetController():getDetectedTargets( DetectionVisual, DetectionOptical, DetectionRadar, DetectionIRST, DetectionRWR, DetectionDLINK )
end
@@ -1849,21 +1772,25 @@ function CONTROLLABLE:GetDetectedTargets( DetectVisual, DetectOptical, DetectRad
return nil
end
function CONTROLLABLE:IsTargetDetected( DCSObject )
function CONTROLLABLE:IsTargetDetected( DCSObject, DetectVisual, DetectOptical, DetectRadar, DetectIRST, DetectRWR, DetectDLINK )
self:F2( self.ControllableName )
local DCSControllable = self:GetDCSObject()
if DCSControllable then
local DetectionVisual = ( DetectVisual and DetectVisual == true ) and Controller.Detection.VISUAL or nil
local DetectionOptical = ( DetectOptical and DetectOptical == true ) and Controller.Detection.OPTICAL or nil
local DetectionRadar = ( DetectRadar and DetectRadar == true ) and Controller.Detection.RADAR or nil
local DetectionIRST = ( DetectIRST and DetectIRST == true ) and Controller.Detection.IRST or nil
local DetectionRWR = ( DetectRWR and DetectRWR == true ) and Controller.Detection.RWR or nil
local DetectionDLINK = ( DetectDLINK and DetectDLINK == true ) and Controller.Detection.DLINK or nil
local Controller = self:_GetController()
local TargetIsDetected, TargetIsVisible, TargetLastTime, TargetKnowType, TargetKnowDistance, TargetLastPos, TargetLastVelocity
= self:_GetController().isTargetDetected( self:_GetController(), DCSObject,
Controller.Detection.VISUAL,
Controller.Detection.OPTIC,
Controller.Detection.RADAR,
Controller.Detection.IRST,
Controller.Detection.RWR,
Controller.Detection.DLINK
)
= Controller:isTargetDetected( DCSObject, DetectionVisual, DetectionOptical, DetectionRadar, DetectionIRST, DetectionRWR, DetectionDLINK )
return TargetIsDetected, TargetIsVisible, TargetLastTime, TargetKnowType, TargetKnowDistance, TargetLastPos, TargetLastVelocity
end

View File

@@ -1,8 +1,8 @@
--- This module contains the GROUP class.
--- **Wrapper** -- GROUP is a wrapper class for the DCS Class Group.
--
-- 1) @{Group#GROUP} class, extends @{Controllable#CONTROLLABLE}
-- =============================================================
-- The @{Group#GROUP} class is a wrapper class to handle the DCS Group objects:
-- ===
--
-- The @{#GROUP} class is a wrapper class to handle the DCS Group objects:
--
-- * Support all DCS Group APIs.
-- * Enhance with Group specific APIs not in the DCS Group API set.
@@ -11,60 +11,8 @@
--
-- **IMPORTANT: ONE SHOULD NEVER SANATIZE these GROUP OBJECT REFERENCES! (make the GROUP object references nil).**
--
-- 1.1) GROUP reference methods
-- -----------------------
-- For each DCS Group object alive within a running mission, a GROUP wrapper object (instance) will be created within the _@{DATABASE} object.
-- This is done at the beginning of the mission (when the mission starts), and dynamically when new DCS Group objects are spawned (using the @{SPAWN} class).
--
-- The GROUP class does not contain a :New() method, rather it provides :Find() methods to retrieve the object reference
-- using the DCS Group or the DCS GroupName.
--
-- Another thing to know is that GROUP objects do not "contain" the DCS Group object.
-- The GROUP methods will reference the DCS Group object by name when it is needed during API execution.
-- If the DCS Group object does not exist or is nil, the GROUP methods will return nil and log an exception in the DCS.log file.
--
-- The GROUP class provides the following functions to retrieve quickly the relevant GROUP instance:
--
-- * @{#GROUP.Find}(): Find a GROUP instance from the _DATABASE object using a DCS Group object.
-- * @{#GROUP.FindByName}(): Find a GROUP instance from the _DATABASE object using a DCS Group name.
--
-- ## 1.2) GROUP task methods
--
-- A GROUP is a @{Controllable}. See the @{Controllable} task methods section for a description of the task methods.
--
-- ### 1.2.4) Obtain the mission from group templates
-- See the detailed documentation on the GROUP class.
--
-- Group templates contain complete mission descriptions. Sometimes you want to copy a complete mission from a group and assign it to another:
--
-- * @{Controllable#CONTROLLABLE.TaskMission}: (AIR + GROUND) Return a mission task from a mission template.
--
-- ## 1.3) GROUP Command methods
--
-- A GROUP is a @{Controllable}. See the @{Controllable} command methods section for a description of the command methods.
--
-- ## 1.4) GROUP option methods
--
-- A GROUP is a @{Controllable}. See the @{Controllable} option methods section for a description of the option methods.
--
-- ## 1.5) GROUP Zone validation methods
--
-- The group can be validated whether it is completely, partly or not within a @{Zone}.
-- Use the following Zone validation methods on the group:
--
-- * @{#GROUP.IsCompletelyInZone}: Returns true if all units of the group are within a @{Zone}.
-- * @{#GROUP.IsPartlyInZone}: Returns true if some units of the group are within a @{Zone}.
-- * @{#GROUP.IsNotInZone}: Returns true if none of the group units of the group are within a @{Zone}.
--
-- The zone can be of any @{Zone} class derived from @{Zone#ZONE_BASE}. So, these methods are polymorphic to the zones tested on.
--
-- ## 1.6) GROUP AI methods
--
-- A GROUP has AI methods to control the AI activation.
--
-- * @{#GROUP.SetAIOnOff}(): Turns the GROUP AI On or Off.
-- * @{#GROUP.SetAIOn}(): Turns the GROUP AI On.
-- * @{#GROUP.SetAIOff}(): Turns the GROUP AI Off.
--
-- ====
--
-- # **API CHANGE HISTORY**
@@ -76,6 +24,11 @@
--
-- Hereby the change log:
--
-- 2017-03-26: GROUP:**RouteRTB( RTBAirbase, Speed )** added.
--
-- 2017-03-07: GROUP:**HandleEvent( Event, EventFunction )** added.
-- 2017-03-07: GROUP:**UnHandleEvent( Event )** added.
--
-- 2017-01-24: GROUP:**SetAIOnOff( AIOnOff )** added.
--
-- 2017-01-24: GROUP:**SetAIOn()** added.
@@ -97,10 +50,66 @@
-- @module Group
-- @author FlightControl
--- The GROUP class
-- @type GROUP
--- @type GROUP
-- @extends Wrapper.Controllable#CONTROLLABLE
-- @field #string GroupName The name of the group.
---
-- # GROUP class, extends @{Controllable#CONTROLLABLE}
--
-- For each DCS Group object alive within a running mission, a GROUP wrapper object (instance) will be created within the _@{DATABASE} object.
-- This is done at the beginning of the mission (when the mission starts), and dynamically when new DCS Group objects are spawned (using the @{SPAWN} class).
--
-- The GROUP class does not contain a :New() method, rather it provides :Find() methods to retrieve the object reference
-- using the DCS Group or the DCS GroupName.
--
-- Another thing to know is that GROUP objects do not "contain" the DCS Group object.
-- The GROUP methods will reference the DCS Group object by name when it is needed during API execution.
-- If the DCS Group object does not exist or is nil, the GROUP methods will return nil and log an exception in the DCS.log file.
--
-- The GROUP class provides the following functions to retrieve quickly the relevant GROUP instance:
--
-- * @{#GROUP.Find}(): Find a GROUP instance from the _DATABASE object using a DCS Group object.
-- * @{#GROUP.FindByName}(): Find a GROUP instance from the _DATABASE object using a DCS Group name.
--
-- ## GROUP task methods
--
-- A GROUP is a @{Controllable}. See the @{Controllable} task methods section for a description of the task methods.
--
-- ### Obtain the mission from group templates
--
-- Group templates contain complete mission descriptions. Sometimes you want to copy a complete mission from a group and assign it to another:
--
-- * @{Controllable#CONTROLLABLE.TaskMission}: (AIR + GROUND) Return a mission task from a mission template.
--
-- ## GROUP Command methods
--
-- A GROUP is a @{Controllable}. See the @{Controllable} command methods section for a description of the command methods.
--
-- ## GROUP option methods
--
-- A GROUP is a @{Controllable}. See the @{Controllable} option methods section for a description of the option methods.
--
-- ## GROUP Zone validation methods
--
-- The group can be validated whether it is completely, partly or not within a @{Zone}.
-- Use the following Zone validation methods on the group:
--
-- * @{#GROUP.IsCompletelyInZone}: Returns true if all units of the group are within a @{Zone}.
-- * @{#GROUP.IsPartlyInZone}: Returns true if some units of the group are within a @{Zone}.
-- * @{#GROUP.IsNotInZone}: Returns true if none of the group units of the group are within a @{Zone}.
--
-- The zone can be of any @{Zone} class derived from @{Zone#ZONE_BASE}. So, these methods are polymorphic to the zones tested on.
--
-- ## GROUP AI methods
--
-- A GROUP has AI methods to control the AI activation.
--
-- * @{#GROUP.SetAIOnOff}(): Turns the GROUP AI On or Off.
-- * @{#GROUP.SetAIOn}(): Turns the GROUP AI On.
-- * @{#GROUP.SetAIOff}(): Turns the GROUP AI Off.
--
-- @field #GROUP GROUP
GROUP = {
ClassName = "GROUP",
}
@@ -110,7 +119,7 @@ GROUP = {
-- @param Dcs.DCSWrapper.Group#Group GroupName The DCS Group name
-- @return #GROUP self
function GROUP:Register( GroupName )
local self = BASE:Inherit( self, CONTROLLABLE:New( GroupName ) )
self = BASE:Inherit( self, CONTROLLABLE:New( GroupName ) )
self:F2( GroupName )
self.GroupName = GroupName
@@ -174,19 +183,33 @@ function GROUP:GetPositionVec3() -- Overridden from POSITIONABLE:GetPositionVec3
return nil
end
--- Returns if the DCS Group is alive.
-- When the group exists at run-time, this method will return true, otherwise false.
--- Returns if the Group is alive.
-- The Group must:
--
-- * Exist at run-time.
-- * Has at least one unit.
--
-- When the first @{Unit} of the Group is active, it will return true.
-- If the first @{Unit} of the Group is inactive, it will return false.
--
-- @param #GROUP self
-- @return #boolean true if the DCS Group is alive.
-- @return #boolean true if the Group is alive and active.
-- @return #boolean false if the Group is alive but inactive.
-- @return #nil if the group does not exist anymore.
function GROUP:IsAlive()
self:F2( self.GroupName )
local DCSGroup = self:GetDCSObject()
local DCSGroup = self:GetDCSObject() -- Dcs.DCSGroup#Group
if DCSGroup then
local GroupIsAlive = DCSGroup:isExist() and DCSGroup:getUnit(1) ~= nil
self:T3( GroupIsAlive )
return GroupIsAlive
if DCSGroup:isExist() then
local DCSUnit = DCSGroup:getUnit(1) -- Dcs.DCSUnit#Unit
if DCSUnit then
local GroupIsAlive = DCSUnit:isActive()
self:T3( GroupIsAlive )
return GroupIsAlive
end
end
end
return nil
@@ -228,7 +251,7 @@ function GROUP:GetCategory()
return nil
end
--- Returns the category name of the DCS Group.
--- Returns the category name of the #GROUP.
-- @param #GROUP self
-- @return #string Category name = Helicopter, Airplane, Ground Unit, Ship
function GROUP:GetCategoryName()
@@ -296,6 +319,7 @@ function GROUP:GetUnit( UnitNumber )
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local DCSUnit = DCSGroup:getUnit( UnitNumber )
local UnitFound = UNIT:Find( DCSGroup:getUnit( UnitNumber ) )
self:T2( UnitFound )
return UnitFound
@@ -433,6 +457,7 @@ function GROUP:GetVec2()
end
--- Returns the current Vec3 vector of the first DCS Unit in the GROUP.
-- @param #GROUP self
-- @return Dcs.DCSTypes#Vec3 Current Vec3 of the first DCS Unit of the GROUP.
function GROUP:GetVec3()
self:F2( self.GroupName )
@@ -442,7 +467,65 @@ function GROUP:GetVec3()
return GroupVec3
end
--- Returns a POINT_VEC2 object indicating the point in 2D of the first UNIT of the GROUP within the mission.
-- @param #GROUP self
-- @return Core.Point#POINT_VEC2 The 2D point vector of the first DCS Unit of the GROUP.
-- @return #nil The first UNIT is not existing or alive.
function GROUP:GetPointVec2()
self:F2(self.GroupName)
local FirstUnit = self:GetUnit(1)
if FirstUnit then
local FirstUnitPointVec2 = FirstUnit:GetPointVec2()
self:T3(FirstUnitPointVec2)
return FirstUnitPointVec2
end
return nil
end
--- Returns a random @{DCSTypes#Vec3} vector (point in 3D of the UNIT within the mission) within a range around the first UNIT of the GROUP.
-- @param #GROUP self
-- @param #number Radius
-- @return Dcs.DCSTypes#Vec3 The random 3D point vector around the first UNIT of the GROUP.
-- @return #nil The GROUP is invalid or empty
-- @usage
-- -- If Radius is ignored, returns the Dcs.DCSTypes#Vec3 of first UNIT of the GROUP
function GROUP:GetRandomVec3(Radius)
self:F2(self.GroupName)
local FirstUnit = self:GetUnit(1)
if FirstUnit then
local FirstUnitRandomPointVec3 = FirstUnit:GetRandomVec3(Radius)
self:T3(FirstUnitRandomPointVec3)
return FirstUnitRandomPointVec3
end
return nil
end
--- Returns the mean heading of every UNIT in the GROUP in degrees
-- @param #GROUP self
-- @return #number mean heading of the GROUP
-- @return #nil The first UNIT is not existing or alive.
function GROUP:GetHeading()
self:F2(self.GroupName)
local GroupSize = self:GetSize()
local HeadingAccumulator = 0
if GroupSize then
for i = 1, GroupSize do
HeadingAccumulator = HeadingAccumulator + self:GetUnit(i):GetHeading()
end
return math.floor(HeadingAccumulator / GroupSize)
end
return nil
end
do -- Is Zone methods
@@ -467,24 +550,33 @@ end
--- Returns true if some units of the group are within a @{Zone}.
-- @param #GROUP self
-- @param Core.Zone#ZONE_BASE Zone The zone to test.
-- @return #boolean Returns true if the Group is completely within the @{Zone#ZONE_BASE}
-- @return #boolean Returns true if the Group is partially within the @{Zone#ZONE_BASE}
function GROUP:IsPartlyInZone( Zone )
self:F2( { self.GroupName, Zone } )
local IsOneUnitInZone = false
local IsOneUnitOutsideZone = false
for UnitID, UnitData in pairs( self:GetUnits() ) do
local Unit = UnitData -- Wrapper.Unit#UNIT
if Zone:IsVec3InZone( Unit:GetVec3() ) then
return true
IsOneUnitInZone = true
else
IsOneUnitOutsideZone = true
end
end
return false
if IsOneUnitInZone and IsOneUnitOutsideZone then
return true
else
return false
end
end
--- Returns true if none of the group units of the group are within a @{Zone}.
-- @param #GROUP self
-- @param Core.Zone#ZONE_BASE Zone The zone to test.
-- @return #boolean Returns true if the Group is completely within the @{Zone#ZONE_BASE}
-- @return #boolean Returns true if the Group is not within the @{Zone#ZONE_BASE}
function GROUP:IsNotInZone( Zone )
self:F2( { self.GroupName, Zone } )
@@ -498,6 +590,24 @@ function GROUP:IsNotInZone( Zone )
return true
end
--- Returns the number of UNITs that are in the @{Zone}
-- @param #GROUP self
-- @param Core.Zone#ZONE_BASE Zone The zone to test.
-- @return #number The number of UNITs that are in the @{Zone}
function GROUP:CountInZone( Zone )
self:F2( {self.GroupName, Zone} )
local Count = 0
for UnitID, UnitData in pairs( self:GetUnits() ) do
local Unit = UnitData -- Wrapper.Unit#UNIT
if Zone:IsVec3InZone( Unit:GetVec3() ) then
Count = Count + 1
end
end
return Count
end
--- Returns if the group is of an air category.
-- If the group is a helicopter or a plane, then this method will return true, otherwise false.
-- @param #GROUP self
@@ -744,6 +854,9 @@ function GROUP:Respawn( Template )
self:Destroy()
_DATABASE:Spawn( Template )
self:ResetEvents()
end
--- Returns the group template from the @{DATABASE} (_DATABASE object).
@@ -892,9 +1005,144 @@ function GROUP:InAir()
return nil
end
do -- Route methods
--- (AIR) Return the Group to an @{Airbase#AIRBASE}.
-- The following things are to be taken into account:
--
-- * The group is respawned to achieve the RTB, there may be side artefacts as a result of this. (Like weapons suddenly come back).
-- * A group consisting out of more than one unit, may rejoin formation when respawned.
-- * A speed can be given in km/h. If no speed is specified, the maximum speed of the first unit will be taken to return to base.
-- * When there is no @{Airbase} object specified, the group will return to the home base if the route of the group is pinned at take-off or at landing to a base.
-- * When there is no @{Airbase} object specified and the group route is not pinned to any airbase, it will return to the nearest airbase.
--
-- @param #GROUP self
-- @param Wrapper.Airbase#AIRBASE RTBAirbase (optional) The @{Airbase} to return to. If blank, the controllable will return to the nearest friendly airbase.
-- @param #number Speed (optional) The Speed, if no Speed is given, the maximum Speed of the first unit is selected.
-- @return #GROUP
function GROUP:RouteRTB( RTBAirbase, Speed )
self:F2( { RTBAirbase, Speed } )
local DCSGroup = self:GetDCSObject()
if DCSGroup then
if RTBAirbase then
local GroupPoint = self:GetVec2()
local GroupVelocity = self:GetUnit(1):GetDesc().speedMax
local PointFrom = {}
PointFrom.x = GroupPoint.x
PointFrom.y = GroupPoint.y
PointFrom.type = "Turning Point"
PointFrom.action = "Turning Point"
PointFrom.speed = GroupVelocity
local PointTo = {}
local AirbasePointVec2 = RTBAirbase:GetPointVec2()
local AirbaseAirPoint = AirbasePointVec2:RoutePointAir(
POINT_VEC3.RoutePointAltType.BARO,
"Land",
"Landing",
Speed or self:GetUnit(1):GetDesc().speedMax
)
AirbaseAirPoint["airdromeId"] = RTBAirbase:GetID()
AirbaseAirPoint["speed_locked"] = true,
self:E(AirbaseAirPoint )
local Points = { PointFrom, AirbaseAirPoint }
self:T3( Points )
local Template = self:GetTemplate()
Template.route.points = Points
self:Respawn( Template )
self:Route( Points )
self:Respawn(Template)
else
self:ClearTasks()
end
end
return self
end
end
function GROUP:OnReSpawn( ReSpawnFunction )
self.ReSpawnFunction = ReSpawnFunction
end
do -- Event Handling
--- Subscribe to a DCS Event.
-- @param #GROUP self
-- @param Core.Event#EVENTS Event
-- @param #function EventFunction (optional) The function to be called when the event occurs for the GROUP.
-- @return #GROUP
function GROUP:HandleEvent( Event, EventFunction )
self:EventDispatcher():OnEventForGroup( self:GetName(), EventFunction, self, Event )
return self
end
--- UnSubscribe to a DCS event.
-- @param #GROUP self
-- @param Core.Event#EVENTS Event
-- @return #GROUP
function GROUP:UnHandleEvent( Event )
self:EventDispatcher():Remove( self, Event )
return self
end
--- Reset the subscriptions.
-- @param #GROUP self
-- @return #GROUP
function GROUP:ResetEvents()
self:EventDispatcher():Reset( self )
for UnitID, UnitData in pairs( self:GetUnits() ) do
UnitData:ResetEvents()
end
return self
end
end
do -- Players
--- Get player names
-- @param #GROUP self
-- @return #table The group has players, an array of player names is returned.
-- @return #nil The group has no players
function GROUP:GetPlayerNames()
local PlayerNames = nil
local Units = self:GetUnits()
for UnitID, UnitData in pairs( Units ) do
local Unit = UnitData -- Wrapper.Unit#UNIT
local PlayerName = Unit:GetPlayerName()
if PlayerName and PlayerName ~= "" then
PlayerNames = PlayerNames or {}
table.insert( PlayerNames, PlayerName )
end
end
self:F2( PlayerNames )
return PlayerNames
end
end

View File

@@ -58,17 +58,19 @@ function IDENTIFIABLE:New( IdentifiableName )
return self
end
--- Returns if the Identifiable is alive.
--- Returns if the Identifiable is alive.
-- If the Identifiable is not alive, nil is returned.
-- If the Identifiable is alive, true is returned.
-- @param #IDENTIFIABLE self
-- @return #boolean true if Identifiable is alive.
-- @return #nil The DCS Identifiable is not existing or alive.
-- @return #nil if the Identifiable is not existing or is not alive.
function IDENTIFIABLE:IsAlive()
self:F3( self.IdentifiableName )
local DCSIdentifiable = self:GetDCSObject()
local DCSIdentifiable = self:GetDCSObject() -- Dcs.DCSObject#Object
if DCSIdentifiable then
local IdentifiableIsAlive = DCSIdentifiable:isExist()
local IdentifiableIsAlive = DCSIdentifiable:isExist()
return IdentifiableIsAlive
end

View File

@@ -1,25 +1,4 @@
--- This module contains the POSITIONABLE class.
--
-- 1) @{Positionable#POSITIONABLE} class, extends @{Identifiable#IDENTIFIABLE}
-- ===========================================================
-- The @{Positionable#POSITIONABLE} class is a wrapper class to handle the POSITIONABLE objects:
--
-- * Support all DCS APIs.
-- * Enhance with POSITIONABLE specific APIs not in the DCS API set.
-- * Manage the "state" of the POSITIONABLE.
--
-- 1.1) POSITIONABLE constructor:
-- ------------------------------
-- The POSITIONABLE class provides the following functions to construct a POSITIONABLE instance:
--
-- * @{Positionable#POSITIONABLE.New}(): Create a POSITIONABLE instance.
--
-- 1.2) POSITIONABLE methods:
-- --------------------------
-- The following methods can be used to identify an measurable object:
--
-- * @{Positionable#POSITIONABLE.GetID}(): Returns the ID of the measurable object.
-- * @{Positionable#POSITIONABLE.GetName}(): Returns the name of the measurable object.
--- **Wrapper** -- This module contains the POSITIONABLE class.
--
-- ===
--
@@ -29,11 +8,36 @@
-- @type POSITIONABLE
-- @extends Wrapper.Identifiable#IDENTIFIABLE
-- @field #string PositionableName The name of the measurable.
-- @field Core.Spot#SPOT Spot The laser Spot.
-- @field #number LaserCode The last assigned laser code.
--- # POSITIONABLE class, extends @{Identifiable#IDENTIFIABLE}
--
-- The POSITIONABLE class is a wrapper class to handle the POSITIONABLE objects:
--
-- * Support all DCS APIs.
-- * Enhance with POSITIONABLE specific APIs not in the DCS API set.
-- * Manage the "state" of the POSITIONABLE.
--
-- ## POSITIONABLE constructor
--
-- The POSITIONABLE class provides the following functions to construct a POSITIONABLE instance:
--
-- * @{Positionable#POSITIONABLE.New}(): Create a POSITIONABLE instance.
--
-- ## POSITIONABLE methods
--
-- The following methods can be used to identify an measurable object:
--
-- * @{Positionable#POSITIONABLE.GetID}(): Returns the ID of the measurable object.
-- * @{Positionable#POSITIONABLE.GetName}(): Returns the name of the measurable object.
--
--
-- @field #POSITIONABLE
POSITIONABLE = {
ClassName = "POSITIONABLE",
PositionableName = "",
}
--- A DCSPositionable
-- @type DCSPositionable
-- @field id_ The ID of the controllable in DCS
@@ -132,11 +136,35 @@ function POSITIONABLE:GetPointVec3()
return nil
end
--- Returns a COORDINATE object indicating the point in 3D of the POSITIONABLE within the mission.
-- @param Wrapper.Positionable#POSITIONABLE self
-- @return Core.Point#COORDINATE The COORDINATE of the POSITIONABLE.
-- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:GetCoordinate()
self:F2( self.PositionableName )
local DCSPositionable = self:GetDCSObject()
if DCSPositionable then
local PositionableVec3 = self:GetPositionVec3()
local PositionableCoordinate = COORDINATE:NewFromVec3( PositionableVec3 )
self:T2( PositionableCoordinate )
return PositionableCoordinate
end
return nil
end
--- Returns a random @{DCSTypes#Vec3} vector within a range, indicating the point in 3D of the POSITIONABLE within the mission.
-- @param Wrapper.Positionable#POSITIONABLE self
-- @param #number Radius
-- @return Dcs.DCSTypes#Vec3 The 3D point vector of the POSITIONABLE.
-- @return #nil The POSITIONABLE is not existing or alive.
-- @usage
-- -- If Radius is ignored, returns the Dcs.DCSTypes#Vec3 of first UNIT of the GROUP
function POSITIONABLE:GetRandomVec3( Radius )
self:F2( self.PositionableName )
@@ -144,14 +172,20 @@ function POSITIONABLE:GetRandomVec3( Radius )
if DCSPositionable then
local PositionablePointVec3 = DCSPositionable:getPosition().p
local PositionableRandomVec3 = {}
local angle = math.random() * math.pi*2;
PositionableRandomVec3.x = PositionablePointVec3.x + math.cos( angle ) * math.random() * Radius;
PositionableRandomVec3.y = PositionablePointVec3.y
PositionableRandomVec3.z = PositionablePointVec3.z + math.sin( angle ) * math.random() * Radius;
self:T3( PositionableRandomVec3 )
return PositionableRandomVec3
if Radius then
local PositionableRandomVec3 = {}
local angle = math.random() * math.pi*2;
PositionableRandomVec3.x = PositionablePointVec3.x + math.cos( angle ) * math.random() * Radius;
PositionableRandomVec3.y = PositionablePointVec3.y
PositionableRandomVec3.z = PositionablePointVec3.z + math.sin( angle ) * math.random() * Radius;
self:T3( PositionableRandomVec3 )
return PositionableRandomVec3
else
self:E("Radius is nil, returning the PointVec3 of the POSITIONABLE", PositionablePointVec3)
return PositionablePointVec3
end
end
return nil
@@ -175,6 +209,28 @@ function POSITIONABLE:GetVec3()
return nil
end
--- Get the bounding box of the underlying POSITIONABLE DCS Object.
-- @param #POSITIONABLE self
-- @return Dcs.DCSTypes#Distance The bounding box of the POSITIONABLE.
-- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:GetBoundingBox() --R2.1
self:F2()
local DCSPositionable = self:GetDCSObject()
if DCSPositionable then
local PositionableDesc = DCSPositionable:getDesc() --Dcs.DCSTypes#Desc
if PositionableDesc then
local PositionableBox = PositionableDesc.box
return PositionableBox
end
end
return nil
end
--- Returns the altitude of the POSITIONABLE.
-- @param Wrapper.Positionable#POSITIONABLE self
-- @return Dcs.DCSTypes#Distance The altitude of the POSITIONABLE.
@@ -219,6 +275,7 @@ end
--- Returns the POSITIONABLE heading in degrees.
-- @param Wrapper.Positionable#POSITIONABLE self
-- @return #number The POSTIONABLE heading
-- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:GetHeading()
local DCSPositionable = self:GetDCSObject()
@@ -270,6 +327,29 @@ function POSITIONABLE:GetVelocity()
return nil
end
--- Returns the POSITIONABLE height in meters.
-- @param Wrapper.Positionable#POSITIONABLE self
-- @return Dcs.DCSTypes#Vec3 The height of the positionable.
-- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:GetHeight() --R2.1
self:F2( self.PositionableName )
local DCSPositionable = self:GetDCSObject()
if DCSPositionable then
local PositionablePosition = DCSPositionable:getPosition()
if PositionablePosition then
local PositionableHeight = PositionablePosition.p.y
self:T2( PositionableHeight )
return PositionableHeight
end
end
return nil
end
--- Returns the POSITIONABLE velocity in km/h.
-- @param Wrapper.Positionable#POSITIONABLE self
-- @return #number The velocity in km/h
@@ -290,18 +370,38 @@ function POSITIONABLE:GetVelocityKMH()
return nil
end
--- Returns the message text with the callsign embedded (if there is one).
-- @param #POSITIONABLE self
-- @param #string Message The message text
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
-- @return #string The message text
function POSITIONABLE:GetMessageText( Message, Name ) --R2.1 added
local DCSObject = self:GetDCSObject()
if DCSObject then
Name = Name and ( " (" .. Name .. ")" ) or ""
local Callsign = string.format( "[%s]", self:GetCallsign() ~= "" and self:GetCallsign() or self:GetName() )
local MessageText = Callsign .. Name .. ": " .. Message
return MessageText
end
return nil
end
--- Returns a message with the callsign embedded (if there is one).
-- @param #POSITIONABLE self
-- @param #string Message The message text
-- @param Dcs.DCSTypes#Duration Duration The duration of the message.
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
-- @return Core.Message#MESSAGE
function POSITIONABLE:GetMessage( Message, Duration, Name )
function POSITIONABLE:GetMessage( Message, Duration, Name ) --R2.1 changed callsign and name and using GetMessageText
local DCSObject = self:GetDCSObject()
if DCSObject then
Name = Name or self:GetTypeName()
return MESSAGE:New( Message, Duration, self:GetCallsign() .. " (" .. Name .. ")" )
local MessageText = self:GetMessageText( Message, Name )
return MESSAGE:New( MessageText, Duration )
end
return nil
@@ -330,12 +430,19 @@ end
-- @param #string Message The message text
-- @param Dcs.DCSTYpes#Duration Duration The duration of the message.
-- @param Dcs.DCScoalition#coalition MessageCoalition The Coalition receiving the message.
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
function POSITIONABLE:MessageToCoalition( Message, Duration, MessageCoalition, Name )
function POSITIONABLE:MessageToCoalition( Message, Duration, MessageCoalition )
self:F2( { Message, Duration } )
local Name = ""
local DCSObject = self:GetDCSObject()
if DCSObject then
if MessageCoalition == coalition.side.BLUE then
Name = "Blue coalition"
end
if MessageCoalition == coalition.side.RED then
Name = "Red coalition"
end
self:GetMessage( Message, Duration, Name ):ToCoalition( MessageCoalition )
end
@@ -415,6 +522,30 @@ function POSITIONABLE:MessageToGroup( Message, Duration, MessageGroup, Name )
return nil
end
--- Send a message to a @{Set#SET_GROUP}.
-- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message.
-- @param #POSITIONABLE self
-- @param #string Message The message text
-- @param Dcs.DCSTypes#Duration Duration The duration of the message.
-- @param Core.Set#SET_GROUP MessageSetGroup The SET_GROUP collection receiving the message.
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
function POSITIONABLE:MessageToSetGroup( Message, Duration, MessageSetGroup, Name ) --R2.1
self:F2( { Message, Duration } )
local DCSObject = self:GetDCSObject()
if DCSObject then
if DCSObject:isExist() then
MessageSetGroup:ForEachGroup(
function( MessageGroup )
self:GetMessage( Message, Duration, Name ):ToGroup( MessageGroup )
end
)
end
end
return nil
end
--- Send a message to the players in the @{Group}.
-- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message.
-- @param #POSITIONABLE self
@@ -432,7 +563,90 @@ function POSITIONABLE:Message( Message, Duration, Name )
return nil
end
--- Create a @{Radio#RADIO}, to allow radio transmission for this POSITIONABLE.
-- Set parameters with the methods provided, then use RADIO:Broadcast() to actually broadcast the message
-- @param #POSITIONABLE self
-- @return #RADIO Radio
function POSITIONABLE:GetRadio() --R2.1
self:F2(self)
return RADIO:New(self)
end
--- Create a @{Radio#BEACON}, to allow this POSITIONABLE to broadcast beacon signals
-- @param #POSITIONABLE self
-- @return #RADIO Radio
function POSITIONABLE:GetBeacon() --R2.1
self:F2(self)
return BEACON:New(self)
end
--- Start Lasing a POSITIONABLE
-- @param #POSITIONABLE self
-- @param #POSITIONABLE Target
-- @param #number LaserCode
-- @param #number Duration
-- @return Core.Spot#SPOT
function POSITIONABLE:LaseUnit( Target, LaserCode, Duration ) --R2.1
self:F2()
LaserCode = LaserCode or math.random( 1000, 9999 )
local RecceDcsUnit = self:GetDCSObject()
local TargetVec3 = Target:GetVec3()
self:E("bulding spot")
self.Spot = SPOT:New( self ) -- Core.Spot#SPOT
self.Spot:LaseOn( Target, LaserCode, Duration)
self.LaserCode = LaserCode
return self.Spot
end
--- Stop Lasing a POSITIONABLE
-- @param #POSITIONABLE self
-- @return #POSITIONABLE
function POSITIONABLE:LaseOff() --R2.1
self:F2()
if self.Spot then
self.Spot:LaseOff()
self.Spot = nil
end
return self
end
--- Check if the POSITIONABLE is lasing a target
-- @param #POSITIONABLE self
-- @return #boolean true if it is lasing a target
function POSITIONABLE:IsLasing() --R2.1
self:F2()
local Lasing = false
if self.Spot then
Lasing = self.Spot:IsLasing()
end
return Lasing
end
--- Get the Spot
-- @param #POSITIONABLE self
-- @return Core.Spot#SPOT The Spot
function POSITIONABLE:GetSpot() --R2.1
return self.Spot
end
--- Get the last assigned laser code
-- @param #POSITIONABLE self
-- @return #number The laser code
function POSITIONABLE:GetLaserCode() --R2.1
return self.LaserCode
end

View File

@@ -48,19 +48,24 @@ STATIC = {
-- As an optional parameter, a briefing text can be given also.
-- @param #STATIC self
-- @param #string StaticName Name of the DCS **Static** as defined within the Mission Editor.
-- @param #boolean RaiseError Raise an error if not found.
-- @return #STATIC
function STATIC:FindByName( StaticName )
function STATIC:FindByName( StaticName, RaiseError )
local StaticFound = _DATABASE:FindStatic( StaticName )
self.StaticName = StaticName
if StaticFound then
StaticFound:F( { StaticName } )
StaticFound:F3( { StaticName } )
return StaticFound
end
error( "STATIC not found for: " .. StaticName )
if RaiseError == nil or RaiseError == true then
error( "STATIC not found for: " .. StaticName )
end
return nil
end
function STATIC:Register( StaticName )

View File

@@ -1,7 +1,7 @@
--- This module contains the UNIT class.
--- **Wrapper** - UNIT is a wrapper class for the DCS Class Unit.
--
-- ===
--
-- 1) @{#UNIT} class, extends @{Controllable#CONTROLLABLE}
-- ===========================================================
-- The @{#UNIT} class is a wrapper class to handle the DCS Unit objects:
--
-- * Support all DCS Unit APIs.
@@ -9,9 +9,14 @@
-- * Handle local Unit Controller.
-- * Manage the "state" of the DCS Unit.
--
--
-- 1.1) UNIT reference methods
-- ----------------------
-- @module Unit
--- @type UNIT
-- @extends Wrapper.Controllable#CONTROLLABLE
---
-- # UNIT class, extends @{Controllable#CONTROLLABLE}
--
-- For each DCS Unit object alive within a running mission, a UNIT wrapper object (instance) will be created within the _@{DATABASE} object.
-- This is done at the beginning of the mission (when the mission starts), and dynamically when new DCS Unit objects are spawned (using the @{SPAWN} class).
--
@@ -29,15 +34,15 @@
--
-- IMPORTANT: ONE SHOULD NEVER SANATIZE these UNIT OBJECT REFERENCES! (make the UNIT object references nil).
--
-- 1.2) DCS UNIT APIs
-- ------------------
-- ## DCS UNIT APIs
--
-- The DCS Unit APIs are used extensively within MOOSE. The UNIT class has for each DCS Unit API a corresponding method.
-- To be able to distinguish easily in your code the difference between a UNIT API call and a DCS Unit API call,
-- the first letter of the method is also capitalized. So, by example, the DCS Unit method @{DCSWrapper.Unit#Unit.getName}()
-- is implemented in the UNIT class as @{#UNIT.GetName}().
--
-- 1.3) Smoke, Flare Units
-- -----------------------
-- ## Smoke, Flare Units
--
-- The UNIT class provides methods to smoke or flare units easily.
-- The @{#UNIT.SmokeBlue}(), @{#UNIT.SmokeGreen}(),@{#UNIT.SmokeOrange}(), @{#UNIT.SmokeRed}(), @{#UNIT.SmokeRed}() methods
-- will smoke the unit in the corresponding color. Note that smoking a unit is done at the current position of the DCS Unit.
@@ -45,36 +50,34 @@
-- The @{#UNIT.FlareGreen}(), @{#UNIT.FlareRed}(), @{#UNIT.FlareWhite}(), @{#UNIT.FlareYellow}()
-- methods will fire off a flare in the air with the corresponding color. Note that a flare is a one-off shot and its effect is of very short duration.
--
-- 1.4) Location Position, Point
-- -----------------------------
-- ## Location Position, Point
--
-- The UNIT class provides methods to obtain the current point or position of the DCS Unit.
-- The @{#UNIT.GetPointVec2}(), @{#UNIT.GetVec3}() will obtain the current **location** of the DCS Unit in a Vec2 (2D) or a **point** in a Vec3 (3D) vector respectively.
-- If you want to obtain the complete **3D position** including ori<72>ntation and direction vectors, consult the @{#UNIT.GetPositionVec3}() method respectively.
--
-- 1.5) Test if alive
-- ------------------
-- ## Test if alive
--
-- The @{#UNIT.IsAlive}(), @{#UNIT.IsActive}() methods determines if the DCS Unit is alive, meaning, it is existing and active.
--
-- 1.6) Test for proximity
-- -----------------------
-- ## Test for proximity
--
-- The UNIT class contains methods to test the location or proximity against zones or other objects.
--
-- ### 1.6.1) Zones
-- ### Zones range
--
-- To test whether the Unit is within a **zone**, use the @{#UNIT.IsInZone}() or the @{#UNIT.IsNotInZone}() methods. Any zone can be tested on, but the zone must be derived from @{Zone#ZONE_BASE}.
--
-- ### 1.6.2) Units
-- Test if another DCS Unit is within a given radius of the current DCS Unit, use the @{#UNIT.OtherUnitInRadius}() method.
-- ### Unit range
--
-- @module Unit
-- @author FlightControl
--- The UNIT class
-- @type UNIT
-- @extends Wrapper.Controllable#CONTROLLABLE
-- * Test if another DCS Unit is within a given radius of the current DCS Unit, use the @{#UNIT.OtherUnitInRadius}() method.
--
-- ## Test Line of Sight
--
-- * Use the @{#UNIT.IsLOS}() method to check if the given unit is within line of sight.
--
--
-- @field #UNIT UNIT
UNIT = {
ClassName="UNIT",
}
@@ -216,7 +219,7 @@ function UNIT:ReSpawn( SpawnVec3, Heading )
end
-- Remove obscolete units from the group structure
i = 1
local i = 1
while i <= #SpawnGroupTemplate.units do
local UnitTemplateData = SpawnGroupTemplate.units[i]
@@ -252,6 +255,27 @@ function UNIT:IsActive()
return nil
end
--- Returns if the Unit is alive.
-- If the Unit is not alive, nil is returned.
-- If the Unit is alive and active, true is returned.
-- If the Unit is alive but not active, false is returned.
-- @param #UNIT self
-- @return #boolean true if Unit is alive and active.
-- @return #boolean false if Unit is alive but not active.
-- @return #nil if the Unit is not existing or is not alive.
function UNIT:IsAlive()
self:F3( self.UnitName )
local DCSUnit = self:GetDCSObject() -- Dcs.DCSUnit#Unit
if DCSUnit then
local UnitIsAlive = DCSUnit:isExist() and DCSUnit:isActive()
return UnitIsAlive
end
return nil
end
--- Returns the Unit's callsign - the localized string.
@@ -522,6 +546,31 @@ function UNIT:GetLife0()
return nil
end
--- Returns the category name of the #UNIT.
-- @param #UNIT self
-- @return #string Category name = Helicopter, Airplane, Ground Unit, Ship
function UNIT:GetCategoryName()
self:F3( self.UnitName )
local DCSUnit = self:GetDCSObject()
if DCSUnit then
local CategoryNames = {
[Unit.Category.AIRPLANE] = "Airplane",
[Unit.Category.HELICOPTER] = "Helicopter",
[Unit.Category.GROUND_UNIT] = "Ground Unit",
[Unit.Category.SHIP] = "Ship",
[Unit.Category.STRUCTURE] = "Structure",
}
local UnitCategory = DCSUnit:getDesc().category
self:T3( UnitCategory )
return CategoryNames[UnitCategory]
end
return nil
end
--- Returns the Unit's A2G threat level on a scale from 1 to 10 ...
-- The following threat levels are foreseen:
--
@@ -540,14 +589,14 @@ end
function UNIT:GetThreatLevel()
local Attributes = self:GetDesc().attributes
self:E( Attributes )
self:T( Attributes )
local ThreatLevel = 0
local ThreatText = ""
if self:IsGround() then
self:E( "Ground" )
self:T( "Ground" )
local ThreatLevels = {
"Unarmed",
@@ -585,7 +634,7 @@ function UNIT:GetThreatLevel()
if self:IsAir() then
self:E( "Air" )
self:T( "Air" )
local ThreatLevels = {
"Unarmed",
@@ -596,7 +645,7 @@ function UNIT:GetThreatLevel()
"Bomber",
"Strategic Bomber",
"Attack Helicopter",
"Interceptor",
"Battleplane",
"Multirole Fighter",
"Fighter"
}
@@ -619,7 +668,7 @@ function UNIT:GetThreatLevel()
if self:IsShip() then
self:E( "Ship" )
self:T( "Ship" )
--["Aircraft Carriers"] = {"Heavy armed ships",},
--["Cruisers"] = {"Heavy armed ships",},
@@ -675,7 +724,7 @@ function UNIT:IsInZone( Zone )
if self:IsAlive() then
local IsInZone = Zone:IsVec3InZone( self:GetVec3() )
self:T( { IsInZone } )
self:T2( { IsInZone } )
return IsInZone
end
@@ -815,6 +864,8 @@ function UNIT:SmokeBlue()
trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Blue )
end
-- Is methods
--- Returns if the unit is of an air category.
@@ -946,5 +997,42 @@ do -- Event Handling
return self
end
--- Reset the subscriptions.
-- @param #UNIT self
-- @return #UNIT
function UNIT:ResetEvents()
self:EventDispatcher():Reset( self )
return self
end
end
do -- Detection
--- Returns if a unit is detecting the TargetUnit.
-- @param #UNIT self
-- @param #UNIT TargetUnit
-- @return #boolean true If the TargetUnit is detected by the unit, otherwise false.
function UNIT:IsDetected( TargetUnit ) --R2.1
local TargetIsDetected, TargetIsVisible, TargetLastTime, TargetKnowType, TargetKnowDistance, TargetLastPos, TargetLastVelocity = self:IsTargetDetected( TargetUnit:GetDCSObject() )
return TargetIsDetected
end
--- Returns if a unit has Line of Sight (LOS) with the TargetUnit.
-- @param #UNIT self
-- @param #UNIT TargetUnit
-- @return #boolean true If the TargetUnit has LOS with the unit, otherwise false.
function UNIT:IsLOS( TargetUnit ) --R2.1
local IsLOS = self:GetPointVec3():IsLOS( TargetUnit:GetPointVec3() )
return IsLOS
end
end

View File

@@ -1,14 +0,0 @@
---
-- Name: XXX-999 - Title
-- Author: YYY
-- Date Created: DD Mmm YYYY
--
-- # Situation:
--
--
--
-- # Test cases:
--
-- 1.

View File

@@ -1,4 +1,4 @@
2017-02-08
2017-02-18
- Reworked some vector functions.
-- POINT_VEC3:NewFromVec2( Vec2, LandHeightAdd ) added.
@@ -6,9 +6,13 @@
-- ZONE_RADIUS:GetRandomPointVec3( inner, outer ) added.
-- ZONE_POLYGON_BASE:GetRandomPointVec2() added.
-- ZONE_POLYGON_BASE:GetRandomPointVec3() added.
2017-02-17
- Added ACT_ROUTE_POINT
-- Routes a controllable to a point with a defined distance.
-- Upon arrived within the engagement distance, an arrival text is shown.
2016-12-06
- Renamed the documentation references following the structure of the files.

View File

@@ -1,26 +1,21 @@
local base = _G
Include = {}
__Moose = {}
Include.File = function( IncludeFile )
if not Include.Files[ IncludeFile ] then
Include.Files[IncludeFile] = IncludeFile
env.info( "Include:" .. IncludeFile .. " from " .. Include.ProgramPath )
local f = assert( base.loadfile( Include.ProgramPath .. IncludeFile .. ".lua" ) )
__Moose.Include = function( IncludeFile )
if not __Moose.Includes[ IncludeFile ] then
__Moose.Includes[IncludeFile] = IncludeFile
local f = assert( base.loadfile( __Moose.ProgramPath .. IncludeFile ) )
if f == nil then
error ("Could not load MOOSE file " .. IncludeFile .. ".lua" )
error ("Moose: Could not load Moose file " .. IncludeFile )
else
env.info( "Include:" .. IncludeFile .. " loaded from " .. Include.ProgramPath )
env.info( "Moose: " .. IncludeFile .. " dynamically loaded from " .. __Moose.ProgramPath )
return f()
end
end
end
Include.ProgramPath = "Scripts/Moose/"
__Moose.ProgramPath = "Scripts/Moose/"
env.info( "Include.ProgramPath = " .. Include.ProgramPath)
Include.Files = {}
Include.File( "Moose" )
__Moose.Includes = {}

View File

@@ -1,2 +0,0 @@
BASE:TraceOnOff( true )

View File

@@ -1,7 +1 @@
local base = _G
Include = {}
Include.Files = {}
Include.File = function( IncludeFile )
end

View File

@@ -1,2 +0,0 @@
BASE:TraceOnOff( false )

View File

@@ -8,16 +8,16 @@ echo Path to Mission Files: %1
rem For /R %1 %%G IN (*.miz) do 7z u "%%G" "l10n\DEFAULT\Moose.lua"
For /R %1 %%M IN (*.miz) do (
echo off
cd
cd > NUL:
echo "Mission: %%M"
mkdir Temp
cd Temp
mkdir l10n
mkdir l10n\DEFAULT
copy ..\..\Moose.lua l10n\DEFAULT
copy "%%~pM%%~nM.lua" l10n\DEFAULT\*.*
dir l10n\DEFAULT
7z -bb0 u "%%M" "l10n\DEFAULT\*.lua"
copy ..\..\Moose.lua l10n\DEFAULT > NUL:
copy "%%~pM%%~nM.lua" l10n\DEFAULT\*.* > NUL:
rem dir l10n\DEFAULT
"%~dp0..\..\Utils\7-Zip\7z" -bb0 u "%%M" "l10n\DEFAULT\*.lua" > NUL:
cd ..
rmdir /S /Q Temp
)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,62 @@
Utilities/Routines.lua
Utilities/Utils.lua
Core/Base.lua
Core/Scheduler.lua
Core/ScheduleDispatcher.lua
Core/Event.lua
Core/Menu.lua
Core/Zone.lua
Core/Database.lua
Core/Set.lua
Core/Point.lua
Core/Message.lua
Core/Fsm.lua
Core/Radio.lua
Core/SpawnStatic.lua
Core/Cargo.lua
Core/Spot.lua
Wrapper/Object.lua
Wrapper/Identifiable.lua
Wrapper/Positionable.lua
Wrapper/Controllable.lua
Wrapper/Group.lua
Wrapper/Unit.lua
Wrapper/Client.lua
Wrapper/Static.lua
Wrapper/Airbase.lua
Wrapper/Scenery.lua
Functional/Scoring.lua
Functional/CleanUp.lua
Functional/Spawn.lua
Functional/Movement.lua
Functional/Sead.lua
Functional/Escort.lua
Functional/MissileTrainer.lua
Functional/AirbasePolice.lua
Functional/Detection.lua
Functional/Designate.lua
AI/AI_Balancer.lua
AI/AI_Patrol.lua
AI/AI_Cap.lua
AI/AI_Cas.lua
AI/AI_Bai.lua
AI/AI_Formation.lua
Actions/Act_Assign.lua
Actions/Act_Route.lua
Actions/Act_Account.lua
Actions/Act_Assist.lua
Tasking/CommandCenter.lua
Tasking/Mission.lua
Tasking/Task.lua
Tasking/DetectionManager.lua
Tasking/Task_A2G_Dispatcher.lua
Tasking/Task_A2G.lua
Tasking/Task_Cargo.lua
Moose.lua

File diff suppressed because it is too large Load Diff

View File

@@ -1,112 +0,0 @@
ECHO OFF
REM Create Moose.lua File
ECHO Path to Moose *.lua files: %1
ECHO Current Date: %2
ECHO Path to Update Missions: %3
ECHO Dynamic or Static: %4
DEL Moose.lua
IF %4 == D GOTO Dynamic
IF %4 == S GOTO Static
GOTO End
:Dynamic
ECHO Dynamic Moose.lua
REM Create a timestamp with is logged in the DCS.log file.
ECHO env.info( '*** MOOSE DYNAMIC INCLUDE START *** ' ) > Moose.lua
ECHO env.info( 'Moose Generation Timestamp: %2' ) >> Moose.lua
COPY /b Moose.lua + "Moose Create Dynamic\Moose_Dynamic_Loader.lua" Moose.lua
COPY /b Moose.lua + "Moose Create Dynamic\Moose_Trace_On.lua" Moose.lua
GOTO End
:Static
ECHO Static Moose.lua
REM Create a timestamp with is logged in the DCS.log file.
ECHO env.info( '*** MOOSE STATIC INCLUDE START *** ' ) > Moose.lua
ECHO env.info( 'Moose Generation Timestamp: %2' ) >> Moose.lua
COPY /b Moose.lua + "Moose Create Static\Moose_Static_Loader.lua" Moose.lua
rem Core Routines
COPY /b Moose.lua + %1\Utilities\Routines.lua Moose.lua
COPY /b Moose.lua + %1\Utilities\Utils.lua Moose.lua
rem Core Classes
COPY /b Moose.lua + %1\Core\Base.lua Moose.lua
COPY /b Moose.lua + %1\Core\Scheduler.lua Moose.lua
COPY /b Moose.lua + %1\Core\ScheduleDispatcher.lua Moose.lua
COPY /b Moose.lua + %1\Core\Event.lua Moose.lua
COPY /b Moose.lua + %1\Core\Menu.lua Moose.lua
COPY /b Moose.lua + %1\Core\Zone.lua Moose.lua
COPY /b Moose.lua + %1\Core\Database.lua Moose.lua
COPY /b Moose.lua + %1\Core\Set.lua Moose.lua
COPY /b Moose.lua + %1\Core\Point.lua Moose.lua
COPY /b Moose.lua + %1\Core\Message.lua Moose.lua
COPY /b Moose.lua + %1\Core\Fsm.lua Moose.lua
rem Wrapper Classes
COPY /b Moose.lua + %1\Wrapper\Object.lua Moose.lua
COPY /b Moose.lua + %1\Wrapper\Identifiable.lua Moose.lua
COPY /b Moose.lua + %1\Wrapper\Positionable.lua Moose.lua
COPY /b Moose.lua + %1\Wrapper\Controllable.lua Moose.lua
COPY /b Moose.lua + %1\Wrapper\Group.lua Moose.lua
COPY /b Moose.lua + %1\Wrapper\Unit.lua Moose.lua
COPY /b Moose.lua + %1\Wrapper\Client.lua Moose.lua
COPY /b Moose.lua + %1\Wrapper\Static.lua Moose.lua
COPY /b Moose.lua + %1\Wrapper\Airbase.lua Moose.lua
COPY /b Moose.lua + %1\Wrapper\Scenery.lua Moose.lua
rem Functional Classes
COPY /b Moose.lua + %1\Functional\Scoring.lua Moose.lua
COPY /b Moose.lua + %1\Functional\CleanUp.lua Moose.lua
COPY /b Moose.lua + %1\Functional\Spawn.lua Moose.lua
COPY /b Moose.lua + %1\Functional\Movement.lua Moose.lua
COPY /b Moose.lua + %1\Functional\Sead.lua Moose.lua
COPY /b Moose.lua + %1\Functional\Escort.lua Moose.lua
COPY /b Moose.lua + %1\Functional\MissileTrainer.lua Moose.lua
COPY /b Moose.lua + %1\Functional\AirbasePolice.lua Moose.lua
COPY /b Moose.lua + %1\Functional\Detection.lua Moose.lua
rem AI Classes
COPY /b Moose.lua + %1\AI\AI_Balancer.lua Moose.lua
COPY /b Moose.lua + %1\AI\AI_Patrol.lua Moose.lua
COPY /b Moose.lua + %1\AI\AI_Cas.lua Moose.lua
COPY /b Moose.lua + %1\AI\AI_Cap.lua Moose.lua
COPY /b Moose.lua + %1\AI\AI_Cargo.lua Moose.lua
rem Actions
COPY /b Moose.lua + %1\Actions\Act_Assign.lua Moose.lua
COPY /b Moose.lua + %1\Actions\Act_Route.lua Moose.lua
COPY /b Moose.lua + %1\Actions\Act_Account.lua Moose.lua
COPY /b Moose.lua + %1\Actions\Act_Assist.lua Moose.lua
rem Task Handling Classes
COPY /b Moose.lua + %1\Tasking\CommandCenter.lua Moose.lua
COPY /b Moose.lua + %1\Tasking\Mission.lua Moose.lua
COPY /b Moose.lua + %1\Tasking\Task.lua Moose.lua
COPY /b Moose.lua + %1\Tasking\DetectionManager.lua Moose.lua
COPY /b Moose.lua + %1\Tasking\Task_SEAD.lua Moose.lua
COPY /b Moose.lua + %1\Tasking\Task_A2G.lua Moose.lua
COPY /b Moose.lua + %1\Moose.lua Moose.lua
COPY /b Moose.lua + "Moose Create Static\Moose_Trace_Off.lua" Moose.lua
GOTO End
:End
ECHO env.info( '*** MOOSE INCLUDE END *** ' ) >> Moose.lua
COPY Moose.lua %3

View File

@@ -0,0 +1,78 @@
-- This routine is called from the LDT environment to create the Moose.lua file stub for use in .miz files.
local MooseDynamicStatic = arg[1]
local MooseDate = arg[2]
local MooseDevelopmentPath = arg[3]
local MooseSetupPath = arg[4]
print( "Moose (D)ynamic (S)tatic : " .. MooseDynamicStatic )
print( "Current Date : " .. MooseDate )
print( "Moose development path : " .. MooseDevelopmentPath )
print( "Moose setup path : " .. MooseSetupPath )
local MooseSourcesFilePath = MooseSetupPath .. "/Moose.files"
local MooseFilePath = MooseSetupPath .. "/Moose.lua"
print( "Reading Moose source list : " .. MooseSourcesFilePath )
local MooseFile = io.open( MooseFilePath, "w" )
if MooseDynamicStatic == "D" then
MooseFile:write( "env.info( '*** MOOSE DYNAMIC INCLUDE START *** ' )\n" )
end
if MooseDynamicStatic == "S" then
MooseFile:write( "env.info( '*** MOOSE STATIC INCLUDE START *** ' )\n" )
end
MooseFile:write( "env.info( 'Moose Generation Timestamp: " .. MooseDate .. "' )\n" )
local MooseLoaderPath
if MooseDynamicStatic == "D" then
MooseLoaderPath = MooseSetupPath .. "/Moose Create Dynamic/Moose_Dynamic_Loader.lua"
end
if MooseDynamicStatic == "S" then
MooseLoaderPath = MooseSetupPath .. "/Moose Create Static/Moose_Static_Loader.lua"
end
local MooseLoader = io.open( MooseLoaderPath, "r" )
local MooseLoaderText = MooseLoader:read( "*a" )
MooseLoader:close()
MooseFile:write( MooseLoaderText )
local MooseSourcesFile = io.open( MooseSourcesFilePath, "r" )
local MooseSource = MooseSourcesFile:read("*l")
while( MooseSource ) do
if MooseSource ~= "" then
local MooseFilePath = MooseDevelopmentPath .. "/" .. MooseSource
if MooseDynamicStatic == "D" then
print( "Load dynamic: " .. MooseSource )
MooseFile:write( "__Moose.Include( '" .. MooseSource .. "' )\n" )
end
if MooseDynamicStatic == "S" then
print( "Load static: " .. MooseSource )
local MooseSourceFile = io.open( MooseFilePath, "r" )
local MooseSourceFileText = MooseSourceFile:read( "*a" )
MooseSourceFile:close()
MooseFile:write( MooseSourceFileText )
end
end
MooseSource = MooseSourcesFile:read("*l")
end
if MooseDynamicStatic == "D" then
MooseFile:write( "BASE:TraceOnOff( true )\n" )
end
if MooseDynamicStatic == "S" then
MooseFile:write( "BASE:TraceOnOff( false )\n" )
end
MooseFile:write( "env.info( '*** MOOSE INCLUDE END *** ' )\n" )
MooseSourcesFile:close()
MooseFile:close()

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 254 KiB

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