Compare commits

..

924 Commits

Author SHA1 Message Date
FlightControl
bd9b844909 Static 2017-07-11 16:57:53 +02:00
FlightControl
3c4f0a4b97 Merge remote-tracking branch 'refs/remotes/origin/master' into release-2-2-pre 2017-07-11 16:57:05 +02:00
FlightControl
33916c2631 Merge remote-tracking branch 'refs/remotes/origin/master-release'
# Conflicts:
#	Moose Mission Setup/Moose.lua
#	Release 2.1.md
2017-07-11 16:56:17 +02:00
FlightControl
a0befeb34f Fixed a bug in the removal of GROUP objects in SET_GROUP
Upon a DEAD or CRASH event processing in a SET_GROUP, the GROUP object
would be removed regardless of how many UNITs are still in the GROUP
object.
The fix is that upon a DEAD or CRASH event in a SET_GROUP, it will be
checked if there is only one UNIT left in the GROUP, and only then the
GROUP will be removed from the SET_GROUP.
2017-07-11 16:20:15 +02:00
Rob Graham
0501959ab9 Merge pull request #619 from FlightControl-Master/RobGraham
Added Knots to Kmph
2017-07-11 17:08:00 +10:00
robgrahamau
764beb7c22 Added Knots to Kmph 2017-07-11 16:46:14 +10:00
FlightControl
9a40bceec5 Static 2017-07-08 14:19:43 +02:00
FlightControl
a402235da3 Merge remote-tracking branch 'refs/remotes/origin/master' into release-2-2-pre 2017-07-08 14:19:14 +02:00
FlightControl
1d939311c3 Removed trace 2017-07-08 14:17:43 +02:00
FlightControl
47f1b8ae66 Event Handling 2017-07-08 14:10:33 +02:00
FlightControl
cff8522922 Size 2017-07-08 13:04:37 +02:00
FlightControl
d78547aa33 Added picture 2017-07-08 13:02:51 +02:00
FlightControl
ab33d6b272 CLEANUP_AIRBASE documentation 2017-07-08 13:00:18 +02:00
FlightControl
388103afea Fixes
-- when carrier containing cargo goes to spectators, it was not handled
correctly. now it is
-- removed "unassigned" message when task is cancelled from group. It is
useless.
2017-07-08 12:40:56 +02:00
FlightControl
62b156c0e8 Static 2017-07-08 09:23:01 +02:00
FlightControl
752de77a82 Merge remote-tracking branch 'refs/remotes/origin/master' into release-2-2-pre 2017-07-08 09:22:07 +02:00
FlightControl
85975c01a4 Optimizations 2017-07-08 09:20:42 +02:00
FlightControl
3fe573926b Fixed scoring format 2017-07-08 06:35:06 +02:00
FlightControl
8e2aef17e7 Fixes to resolve exceptions in multi player situations 2017-07-08 05:54:33 +02:00
FlightControl
367c4d74af Fixes imperial / metric menu option setting 2017-07-08 05:00:47 +02:00
FlightControl
06e063d594 Fixes cargo 2017-07-07 18:45:32 +02:00
FlightControl
7ebf7a2bee Fixed #611 2017-07-07 18:20:58 +02:00
FlightControl
b5c53baf67 Fixed #592 2017-07-07 18:19:37 +02:00
132nd-etcher
536934390c Reduce AI CAP logging noise (#609)
Replace calls to E by calls to F in order to reduce the amount of log spam during an ongoing AI CAP patrol.
2017-07-07 12:07:27 +02:00
FlightControl
1e6035b282 Optimized CARGO
- Smoke position upon arrival at pickup zone
- Solved problem with deploy, deploy function was not called.
- Added Smoke to CARGO
- Moved Smoke to POSITIONABLE
2017-07-07 11:46:08 +02:00
FlightControl
5bbe5fca60 Fix to get correct parent class 2017-07-07 10:41:16 +02:00
FlightControl
e658a2a3bf Static 2017-07-07 08:26:33 +02:00
FlightControl
4b8b155fb8 Merge remote-tracking branch 'refs/remotes/origin/master' into release-2-2-pre 2017-07-07 08:25:57 +02:00
Sven Van de Velde
4c5aad51b3 Merge pull request #608 from FlightControl-Master/Performance
Fixed performance issue
2017-07-07 08:23:52 +02:00
FlightControl
688875dca5 Fixed performance issue
Problem was in BASE. I added a field "ParentClass", which was a
mistake...
at every deepcopy it started to copy ParentClass contents too! My god!
No wonder it suddenly went all slow.
2017-07-07 08:23:22 +02:00
FlightControl
ce2b982782 Static 2017-07-06 21:51:12 +02:00
FlightControl
af61fc6c74 Merge remote-tracking branch 'refs/remotes/origin/master' into release-2-2-pre 2017-07-06 21:50:48 +02:00
Sven Van de Velde
b4c8fbf75a Merge pull request #607 from FlightControl-Master/601-Respawn-Cargo
601 respawn cargo
2017-07-06 21:49:09 +02:00
FlightControl
edb53013b2 Progress 2017-07-06 21:47:02 +02:00
FlightControl
70f48a3d53 Progress 2017-07-06 19:13:15 +02:00
FlightControl
532a311db6 Cargo is now respawning correctly when:
The cargo is destroyed
The carrier crashes with cargo on board
The player exits the carrier with cargo on board.
2017-07-06 17:00:53 +02:00
FlightControl
71da9933d7 Cargo auto respawn first part is working
When all CARGO UNITS of a CARGO_GROUP are destroyed, then when
RespawnOnDestroyed( true ) is used, the CARGO will respawn
automatically.
2017-07-06 11:23:04 +02:00
FlightControl
9f5b9ab04c Progress 2017-07-06 08:51:08 +02:00
Sven Van de Velde
f76ac1e03a Merge pull request #606 from FlightControl-Master/596-CARGO-TRANSPORT-Limits
Implemented Cargo limits
2017-07-06 07:33:15 +02:00
FlightControl
b84541f232 Implemented Cargo limits
Cargo is only allowed to be boarded or a route can only be done if the
limit of the cargo has not been reached! A few additional methods have
been added like IsDeployed. CARGO_GROUP gets the deployed status if it
is transported.
2017-07-06 07:32:44 +02:00
Sven Van de Velde
6115e12309 Merge pull request #604 from FlightControl-Master/603-AI-A2A-GCICAP
Created AI_A2A_GCICAP
2017-07-05 12:54:56 +02:00
FlightControl
c22bc1c57f Created AI_A2A_GCICAP 2017-07-05 10:11:34 +02:00
FlightControl
84055e9798 Updates fixing cargo stuff 2017-07-04 16:34:00 +02:00
FlightControl
ccfcca8f9a Tweaks for the settings system
- Player settings are located at the group level. Only the first player
that joins the group will be able to configure the settings.
- Default system settings are located at the group of the commend
center. Thus, the COMMANDCENTER class will contain the default system
settings menu. You need to join the command center unit (ALT-J) as a
game master to be able to configure these settings.
2017-07-04 10:55:45 +02:00
FlightControl
c043eef5eb Added method CLEANUP:CleanMissilesOn() 2017-07-04 07:05:33 +02:00
FlightControl
2db0265ae6 Fixed first line of task report 2017-07-03 12:11:06 +02:00
FlightControl
3cd787fb1e Fix issue with deployment 2017-07-03 08:46:17 +02:00
FlightControl
93f7d1f013 Static 2017-07-02 23:17:59 +02:00
FlightControl
51f8c532ca Merge remote-tracking branch 'refs/remotes/origin/master' into release-2-2-pre 2017-07-02 23:17:28 +02:00
FlightControl
8825b26b36 New version 2017-07-02 23:16:35 +02:00
FlightControl
300ee0a16a Fix trace amount and performance 2017-07-02 17:59:56 +02:00
FlightControl
f18a56ab1a Static 2017-07-02 12:59:40 +02:00
FlightControl
9dc9fcfeff Merge remote-tracking branch 'refs/remotes/origin/master' into release-2-2-pre
# Conflicts:
#	Moose Mission Setup/Moose.lua
2017-07-02 12:58:26 +02:00
FlightControl
1283caf80b OK. fixed 2017-07-02 12:55:29 +02:00
FlightControl
af230d9874 Fixed goal problem in TASK_CARGO_TRANSPORT more or less, needs further investigation 2017-07-02 12:44:27 +02:00
FlightControl
f221047eba Updated various functions for tasking. 2017-07-02 12:14:41 +02:00
FlightControl
e7b3aa82f9 SWEEP tasks should fall under the SWEEP menu, not INTERCEPT menu 2017-07-02 01:08:00 +02:00
FlightControl
33c6290864 Fixed error with __
Had to test for the presence in raw format, not using __Index
2017-07-02 00:29:17 +02:00
FlightControl
9006e17c25 Fixed problem with BASE:GetParent method 2017-07-01 19:00:44 +02:00
FlightControl
22b02cd3ee Made __ methods invisible 2017-07-01 13:12:52 +02:00
FlightControl
507e4ef25a Made CleanUpScheduler private 2017-07-01 13:02:49 +02:00
FlightControl
8b1583df30 Made Airbases private 2017-07-01 13:00:12 +02:00
FlightControl
083568d3fd Updates 2017-07-01 12:55:51 +02:00
FlightControl
8e5af4ada4 New implementations
of inheritance
of private - public methods ....

This is a big improvement for many!
2017-07-01 12:32:44 +02:00
FlightControl
b217e855e9 Static 2017-06-30 10:38:30 +02:00
FlightControl
21c19bf97e Merge remote-tracking branch 'refs/remotes/origin/master' into release-2-2-pre
# Conflicts:
#	Moose Mission Setup/Moose.lua
2017-06-30 10:38:11 +02:00
FlightControl
5d2eb2ea15 Dynamic Moose.lua 2017-06-30 10:36:14 +02:00
FlightControl
76ec5aa009 Documentation 2017-06-30 10:33:21 +02:00
FlightControl
35681c6f96 Resize 2017-06-30 10:31:27 +02:00
FlightControl
d719c437ec Fixes 2017-06-30 10:30:46 +02:00
FlightControl
133910ac3b New revised CLEANUP class 2017-06-30 10:27:44 +02:00
FlightControl
862f2ab3ac new version 2017-06-29 16:58:12 +02:00
FlightControl
565237b29e Static 2017-06-29 15:00:12 +02:00
FlightControl
78085d3de2 Merge remote-tracking branch 'refs/remotes/origin/master' into release-2-2-pre 2017-06-29 14:59:32 +02:00
FlightControl
2f4361c97a Fixed dispatching S_EVENT_PLAYER_LEAVE_UNIT fired when no player in unit
S_EVENT_PLAYER_LEAVE_UNIT should not be handled when there is no player
in the unit!
but
S_EVENT_MISSION_END should be handled, and also has no initiator!
2017-06-29 14:58:10 +02:00
FlightControl
b245292d11 Static version 2017-06-29 13:53:13 +02:00
FlightControl
dd7d5d9a56 Merge remote-tracking branch 'refs/remotes/origin/master' into release-2-2-pre 2017-06-29 13:45:03 +02:00
FlightControl
4b7b042bb1 Removed trace 2017-06-29 13:23:03 +02:00
FlightControl
18a76fa355 Updated trace to minimize trace overhead. 2017-06-29 13:18:01 +02:00
FlightControl
ce688aabcb New static version 2017-06-29 10:40:56 +02:00
FlightControl
8e2e2e5a1e Merge remote-tracking branch 'refs/remotes/origin/master' into release-2-2-pre 2017-06-29 10:38:52 +02:00
FlightControl
bccc4abf26 Fixes bug that would loop over every unit in the group.
Only the players in a group should be unassigned, not all units.
2017-06-29 10:17:55 +02:00
FlightControl
a1b31e0065 Updated message bug 2017-06-26 20:52:30 +02:00
FlightControl
8af5de6fb5 Merge remote-tracking branch 'refs/remotes/origin/master' into release-2-2-pre 2017-06-26 20:52:06 +02:00
FlightControl
975566eb3c Fixed message bug in scoring 2017-06-26 20:51:01 +02:00
FlightControl
572d1f2902 Pre release 2017-06-26 20:35:28 +02:00
FlightControl
4a7f8b869d Merge remote-tracking branch 'refs/remotes/origin/master' into release-2-2-pre 2017-06-26 20:35:05 +02:00
FlightControl
55164c38bf Finetuning of the messages and menus 2017-06-26 20:33:26 +02:00
FlightControl
1aeb7b3ff6 Fixed issue #566 and #567 2017-06-26 19:37:31 +02:00
FlightControl
967c2a5633 New version 2017-06-26 10:39:32 +02:00
FlightControl
0203e73c71 Merge remote-tracking branch 'refs/remotes/origin/master' into release-2-2-pre 2017-06-25 07:41:47 +02:00
Sven Van de Velde
fa77ba3f48 Merge pull request #588 from FlightControl-Master/585-TASK-A2A-DISPATCHER
585 task a2a dispatcher
2017-06-25 07:41:07 +02:00
FlightControl
1aecd47d5a Show all tasks in Mission Task Progress report 2017-06-25 07:39:36 +02:00
FlightControl
6b920ea3f4 Mission Progress Report added
This reports lists for the tasks in the mission which players have made
progress for that task.
2017-06-25 07:28:55 +02:00
FlightControl
6a2869138e Merge remote-tracking branch 'refs/remotes/origin/master' into 585-TASK-A2A-DISPATCHER 2017-06-25 06:07:32 +02:00
FlightControl
1b36cee3b6 Documentation 2017-06-24 16:21:43 +02:00
FlightControl
05e7eb3456 Static 2017-06-24 15:48:50 +02:00
Sven Van de Velde
70f2c0051a Merge pull request #587 from FlightControl-Master/585-TASK-A2A-DISPATCHER
585 task a2a dispatcher
2017-06-24 15:24:05 +02:00
FlightControl
d396ca1684 Target progress accounting for players 2017-06-24 15:21:59 +02:00
FlightControl
aed71ca9e2 Task Progress Accounting
Record achievement history for players executing a task. Only show
progress when there is progress to report. Only score progress when
there is progress for a player, not for other groups.
2017-06-24 15:05:33 +02:00
FlightControl
50094fde6c Additional scores definitions 2017-06-24 11:35:20 +02:00
FlightControl
d5b66fd08c Merge remote-tracking branch 'refs/remotes/origin/master' into 585-TASK-A2A-DISPATCHER 2017-06-24 10:29:29 +02:00
Sven Van de Velde
34d2b12acc Merge pull request #586 from FlightControl-Master/585-TASK-A2A-DISPATCHER
Improved Task Goal monitoring
2017-06-23 21:55:53 +02:00
FlightControl
f0c20be967 Improved Task Goal monitoring 2017-06-23 21:54:59 +02:00
FlightControl
6126ec9450 Fixes bug in SET_GROUP
Was unable to filter on category "ground". Fixed now.
2017-06-23 08:14:02 +02:00
FlightControl
df7c45d7ef Documentation 2017-06-23 07:51:43 +02:00
FlightControl
f52d8a3ad4 Resize pictures 2017-06-23 07:42:31 +02:00
FlightControl
ab27a1bd2b Documentation 2017-06-23 07:40:38 +02:00
FlightControl
e341287c56 Documentation 2017-06-23 07:38:36 +02:00
FlightControl
ca5247ce1b TASK_A2A_DISPATCHER 2017-06-23 07:36:35 +02:00
FlightControl
81a8056233 Fix problems with pictures 2017-06-23 07:28:49 +02:00
Sven Van de Velde
a92174f32e Merge pull request #584 from FlightControl-Master/562-AI-A2A-Dispatcher
562 ai a2a dispatcher
2017-06-22 22:17:00 +02:00
FlightControl
79ec86f369 TASK_A2A_DISPATCHER documentation 2017-06-22 22:15:17 +02:00
FlightControl
a31abef3cf TASK_A2A_DISPATCHER documentation 2017-06-22 22:13:31 +02:00
Sven Van de Velde
f89964d8ba Merge pull request #583 from FlightControl-Master/562-AI-A2A-Dispatcher
562 ai a2a dispatcher
2017-06-22 10:13:34 +02:00
FlightControl
706a0949ee Docu 2017-06-22 09:58:46 +02:00
FlightControl
bdfd169d39 Added GCI engage range. 2017-06-22 09:58:05 +02:00
FlightControl
44f60169cb Merge remote-tracking branch 'refs/remotes/origin/master' into 562-AI-A2A-Dispatcher 2017-06-22 06:30:55 +02:00
FlightControl
caad080c6c Fixed but with distance when player was nearby.
Detection used PlayerUnitName, instead of Distance. Added new
FriendliesDistance field.
This is a better solution.
2017-06-22 06:30:36 +02:00
Sven Van de Velde
c4a2c9edc9 Merge pull request #582 from FlightControl-Master/562-AI-A2A-Dispatcher
562 ai a2a dispatcher
2017-06-21 11:49:40 +02:00
FlightControl
ee30fa6ac2 Merge remote-tracking branch 'refs/remotes/origin/master' into 562-AI-A2A-Dispatcher 2017-06-21 11:48:24 +02:00
FlightControl
d23cf6028b Updates 2017-06-21 11:48:19 +02:00
FlightControl
6a58290833 Updates 2017-06-21 10:55:35 +02:00
Sven Van de Velde
3b9fdbd5cd Merge pull request #580 from FlightControl-Master/FilterPrefixFix
Fix for '-' characters in the prefix string when using FilterPrefix o…
2017-06-21 06:22:42 +02:00
Sven Van de Velde
d88c3106d0 Merge pull request #581 from FlightControl-Master/562-AI-A2A-Dispatcher
562 ai a2a dispatcher
2017-06-21 06:22:16 +02:00
FlightControl
34bf013b9b TASK_A2A 2017-06-21 06:17:07 +02:00
FlightControl
2e8efe8f4a Merge remote-tracking branch 'refs/remotes/origin/master' into 562-AI-A2A-Dispatcher 2017-06-21 06:00:59 +02:00
FlightControl
54c8a6f9dd Updates 2017-06-21 06:00:24 +02:00
Fridge
18ddbdac84 Fix for '-' characters in the prefix string when using FilterPrefix on groups with '-' in the name
This should replace '-' characters in the search pattern with %- (the escaped version). Thedouble %% is necessary to get the escape sequence through.
2017-06-20 20:51:08 -03:00
Sven Van de Velde
7c4ee9bebd Merge pull request #579 from FlightControl-Master/562-AI-A2A-Dispatcher
562 ai a2a dispatcher
2017-06-20 12:15:00 +02:00
FlightControl
ce397d0a4e Merge remote-tracking branch 'refs/remotes/origin/master' into 562-AI-A2A-Dispatcher 2017-06-20 12:13:39 +02:00
FlightControl
06c8c00dc9 Airbases of Nevada and Normandy 2017-06-20 12:12:52 +02:00
FlightControl
220e5b17aa Merge remote-tracking branch 'refs/remotes/origin/master-release-2.1' into master-release 2017-06-19 20:11:18 +02:00
FlightControl
ef217c0b19 Fix for CTD in DCS 2.1.
Destroyed scenery object cannot be inspected with all the methods.
SceneryObject:getTypeName() goes into CTD with the scenery has been
destroyed.
The problem was in the event handler (Event.lua).
This is fixed by checking if the SceneryObject exists before getting the
type name.
If it does not exist, the type name is filled with "SCENERY".
2017-06-18 09:00:48 +02:00
FlightControl
c72e6ff9b4 Fixed DCS CTD in 2.1 for destroyed scenery objects.
The problem was in the event handler.
getTypeName() for a destroyed scenery object brings DCS into CTD.
2017-06-18 08:31:27 +02:00
FlightControl
305584344e Updated gitignore 2017-06-15 19:52:31 +02:00
FlightControl
a42b5fcea7 Fixes 2017-06-15 19:46:46 +02:00
FlightControl
18591c434f Documentation 2017-06-15 07:02:20 +02:00
FlightControl
439ebf1676 Documentation 2017-06-14 15:17:41 +02:00
FlightControl
05d69457f2 Documentation 2017-06-14 15:08:49 +02:00
FlightControl
a646dd900d Altitude was wrongly set in patrol 2017-06-14 14:34:55 +02:00
FlightControl
fb54b2f280 Trace 2017-06-14 11:50:35 +02:00
FlightControl
2f8b881186 Documentation 2017-06-14 11:17:20 +02:00
FlightControl
385dba63d9 Documentation 2017-06-14 11:13:14 +02:00
Sven Van de Velde
95ce44f2d8 Merge pull request #577 from FlightControl-Master/562-AI-A2A-Dispatcher
562 ai a2a dispatcher
2017-06-14 11:09:25 +02:00
FlightControl
915c65176e Documentation 2017-06-14 11:06:44 +02:00
FlightControl
4f472253de Documentation 2017-06-14 11:02:43 +02:00
FlightControl
c53f8a7033 Documentation 2017-06-14 10:25:19 +02:00
FlightControl
15cf215d49 Documentation 2017-06-14 10:24:59 +02:00
FlightControl
208f214e96 Merge remote-tracking branch 'refs/remotes/origin/master' into 562-AI-A2A-Dispatcher 2017-06-14 10:13:06 +02:00
Sven Van de Velde
e7f83669c4 Merge pull request #576 from FlightControl-Master/562-AI-A2A-Dispatcher
562 ai a2a dispatcher
2017-06-13 13:42:28 +02:00
FlightControl
2b6fecbe4d Documentation 2017-06-13 13:38:52 +02:00
Sven Van de Velde
3e5542e592 Merge pull request #575 from FlightControl-Master/562-AI-A2A-Dispatcher
562 ai a2a dispatcher
2017-06-13 13:35:44 +02:00
FlightControl
1369a28aca Updates 2017-06-13 13:34:49 +02:00
Sven Van de Velde
00933b2905 Merge pull request #574 from FlightControl-Master/562-AI-A2A-Dispatcher
Updates
2017-06-13 13:30:41 +02:00
FlightControl
dd39ff4e94 Updates 2017-06-13 13:29:48 +02:00
Sven Van de Velde
922b61b9fe Merge pull request #573 from FlightControl-Master/562-AI-A2A-Dispatcher
562 ai a2a dispatcher
2017-06-13 13:18:30 +02:00
FlightControl
5a7551d312 Progress 2017-06-13 13:17:17 +02:00
FlightControl
94c208cbc9 Progress 2017-06-13 07:00:44 +02:00
FlightControl
1ea916ec73 Progress 2017-06-12 16:25:05 +02:00
FlightControl
f56b2229a7 Progress 2017-06-12 06:44:01 +02:00
FlightControl
4f91ba6081 Progress 2017-06-10 10:59:41 +02:00
FlightControl
9f22e2cc71 Progress 2017-06-09 20:35:46 +02:00
FlightControl
e17de754a3 Progress 2017-06-08 15:44:35 +02:00
FlightControl
18885f0450 Merge remote-tracking branch 'refs/remotes/origin/master' into 562-AI-A2A-Dispatcher 2017-06-08 11:53:56 +02:00
FlightControl
0f9f615313 Progress 2017-06-08 11:53:53 +02:00
FlightControl
d84f3fcd24 Undo bugfix, the thing worked perfectly :-( 2017-06-08 08:46:03 +02:00
FlightControl
fa6d53634b Bugfix in SpawnInZone where SpawnIndex was not incremented correctly. 2017-06-08 00:49:52 +02:00
FlightControl
ce24d2b4a6 Progress 2017-06-08 00:47:56 +02:00
FlightControl
f151e1e5f4 Progress 2017-06-08 00:35:18 +02:00
FlightControl
59ab62685c Progress 2017-06-08 00:20:53 +02:00
FlightControl
e68b715321 Documentation 2017-06-07 15:18:22 +02:00
FlightControl
ef95cfb1f5 Progress 2017-06-07 12:56:43 +02:00
FlightControl
d120875fa9 Merge remote-tracking branch 'refs/remotes/origin/master' into 562-AI-A2A-Dispatcher 2017-06-07 09:06:53 +02:00
FlightControl
9dfff9ae5e Added TaskRouteToVec2 method 2017-06-06 22:11:25 +02:00
FlightControl
86d8eb023d Progress 2017-06-06 21:46:51 +02:00
FlightControl
b48c467d57 Progress 2017-06-06 21:36:32 +02:00
FlightControl
cf4c269f77 Progress 2017-06-06 18:33:24 +02:00
FlightControl
2fb83c89af Progress 2017-06-06 14:43:40 +02:00
FlightControl
37a176e3ae Progress 2017-06-03 10:28:50 +02:00
FlightControl
09776a60c9 ooofff! waypoint functions and route setting is now working how i wanted it to work 2017-06-02 11:21:53 +02:00
FlightControl
17838d7099 Progress 2017-06-01 13:18:50 +02:00
FlightControl
531f8a9106 Progress 2017-05-31 22:41:29 +02:00
FlightControl
a3289205e6 Progress 2017-05-31 18:58:34 +02:00
FlightControl
0af5e1428b Progress 2017-05-30 19:38:56 +02:00
FlightControl
7ec9a93231 Resize the images 2017-05-29 18:07:45 +02:00
FlightControl
9984055f7d Documentation 2017-05-29 18:02:57 +02:00
FlightControl
4e29565382 Documentation 2017-05-29 14:09:07 +02:00
FlightControl
bc734f1190 Removed disturbing reports when joining a client slot (forgot to delete). 2017-05-29 10:32:54 +02:00
Sven Van de Velde
7d8add6d4c Merge pull request #561 from FlightControl-Master/553-player-reports
553 player reports
2017-05-29 07:13:33 +02:00
FlightControl
ec8a399ca6 Players per Task report added 2017-05-29 07:12:42 +02:00
FlightControl
1935bd235e Merge remote-tracking branch 'refs/remotes/origin/master' into 553-player-reports 2017-05-29 07:02:40 +02:00
Sven Van de Velde
c6631356ea Merge pull request #560 from FlightControl-Master/419-A2A-Tasking
A2A SWEEP Tasking
2017-05-29 05:30:33 +02:00
FlightControl
f2e966735c A2A SWEEP Tasking 2017-05-29 05:29:41 +02:00
Delta-99
35c2cb45bb Merge pull request #558 from FlightControl-Master/OnSpawnGroup-fix-delay
OnSpawnGroup happening before BIRTH sometimes
2017-05-28 13:33:02 -04:00
Delta-99
33fcb86383 Merge pull request #559 from FlightControl-Master/555-EnrouteTaskEngage-fix
EnRouteTaskEngageTargets defined twice
2017-05-28 13:32:51 -04:00
Delta-99
8096c170d5 EnRouteTaskEngageTargets defined twice
EnRouteTaskEngageTargets was defined twice. The 2nd instance looks like
it should have been named EnRouteTaskEngageTargetsInZone. Fixed and
added documentation at the top.
2017-05-28 13:09:50 -04:00
Delta-99
333eba2cb8 OnSpawnGroup happening before BIRTH sometimes
Add a little bit of a delay in calling the OnSpawnGroup function as it
sometimes happens before the actual BIRTH event of the group.
2017-05-28 12:51:50 -04:00
Sven Van de Velde
3cb6bd3a99 Merge pull request #557 from FlightControl-Master/419-A2A-Tasking
419 a2a tasking
2017-05-28 18:25:44 +02:00
FlightControl
f8ab65ce0e Reference points implemented 2017-05-28 18:24:44 +02:00
FlightControl
707a5a778a Merge remote-tracking branch 'refs/remotes/origin/master' into 419-A2A-Tasking 2017-05-28 09:13:03 +02:00
FlightControl
6f183bad74 Update 2017-05-28 09:13:00 +02:00
Sven Van de Velde
28a38d04fd Merge pull request #554 from FlightControl-Master/553-player-reports
Player reports
2017-05-27 10:11:17 +02:00
FlightControl
051cc4955f CARGO 2017-05-27 10:09:44 +02:00
FlightControl
e06b2c5e4f Documentation 2017-05-27 10:04:43 +02:00
FlightControl
8157d7a8d0 Player reports 2017-05-27 09:57:38 +02:00
Sven Van de Velde
a522568a60 Merge pull request #552 from FlightControl-Master/419-A2A-Tasking
Mission goals
2017-05-26 09:44:16 +02:00
FlightControl
b6ecd52444 Mission goals 2017-05-26 09:43:49 +02:00
Sven Van de Velde
3105ef7cb6 Merge pull request #551 from FlightControl-Master/419-A2A-Tasking
419 a2a tasking
2017-05-26 08:44:23 +02:00
FlightControl
356f4a041f Cleaned up the reporting side of A2G tasking 2017-05-26 08:43:30 +02:00
FlightControl
6d43ab371e Merge remote-tracking branch 'refs/remotes/origin/master' into 419-A2A-Tasking 2017-05-26 08:37:04 +02:00
FlightControl
a1a8f90cc5 Removed some old code 2017-05-26 08:35:49 +02:00
FlightControl
c10b4fb129 Fix problem with an end statement that is too much in Task_CARGO.lua 2017-05-26 07:53:40 +02:00
Wingthor
e025b6b407 Merge pull request #537 from FlightControl-Master/baluballa
Added posibilty to SetSmokeColor on Cargo
2017-05-25 11:14:22 +02:00
Wingthor
10f12e4ead Changed algo for starting smoke process 2017-05-25 10:28:04 +02:00
Wingthor
3c71af48ee Added A space to test syncing 2017-05-25 10:02:53 +02:00
Sven Van de Velde
daa68cb110 Merge pull request #542 from FlightControl-Master/541-fix-radio-loop
set default value for Loop
2017-05-25 08:52:53 +02:00
FlightControl
72ccd7c6ea Cleanup 2017-05-25 08:23:34 +02:00
FlightControl
e23fd20d69 Moved moose.lua to the root of the repository 2017-05-25 08:22:18 +02:00
Sven Van de Velde
10b49b4a15 Merge pull request #547 from FlightControl-Master/546-Fix-Coordinates
Fixed error as reported in #546
2017-05-25 07:56:21 +02:00
FlightControl
bcae1bbd89 Fixed some stuff 2017-05-25 07:44:27 +02:00
FlightControl
624a4aa70a Planned task removal for A2G areas that have no targets anymore. 2017-05-24 12:58:14 +02:00
FlightControl
0702057f47 Only remove planned tasks 2017-05-24 12:07:49 +02:00
FlightControl
89371378b7 Fixed non removal of Planned Tasks 2017-05-24 11:49:03 +02:00
FlightControl
f3b49ecc0a Fixed the removal of obscolete planned tasks 2017-05-24 07:14:40 +02:00
FlightControl
fb1e9972a5 Documentation 2017-05-23 22:20:29 +02:00
FlightControl
f6a26e3723 trace 2017-05-23 22:17:38 +02:00
Sven Van de Velde
26027245f0 Merge pull request #545 from FlightControl-Master/419-A2A-Tasking
419 a2a tasking
2017-05-23 20:04:05 +02:00
FlightControl
a66529d482 ENGAGE task is working now
-- Also the switching between INTERCEPT and ENGAGE (players move in and
out of proximity), is working now !!!
2017-05-23 20:02:58 +02:00
FlightControl
bbb086ae6b Merge remote-tracking branch 'refs/remotes/origin/master' into 419-A2A-Tasking 2017-05-23 14:01:50 +02:00
Sven Van de Velde
4ed387cc6b Merge pull request #544 from FlightControl-Master/419-A2A-Tasking
ENGAGE A2A Task Type
2017-05-23 13:53:34 +02:00
FlightControl
b88c6b5f6c Merge remote-tracking branch 'refs/remotes/origin/master' into 419-A2A-Tasking 2017-05-23 13:52:40 +02:00
FlightControl
30ae32e539 Got ENGAGE A2A task type working. 2017-05-23 13:51:47 +02:00
Sven Van de Velde
e42ea47ea8 Merge pull request #543 from FlightControl-Master/419-A2A-Tasking
419 a2a tasking
2017-05-23 11:51:18 +02:00
FlightControl
96f7a79f3a Working INTERCEPT! 2017-05-23 11:50:28 +02:00
Wingthor
6378cbc0ee Changed the structure
Change the structure, took away the nil value error when calling the
setter. How ever color is not set proper, or turns out false
2017-05-23 01:24:39 +02:00
Delta-99
f3a5b735d6 set default value for Loop
Set the default value for loop. If not set Broadcast generates an error.
2017-05-22 15:38:24 -04:00
Wingthor
473735dcd7 Changed the code to support Moose Color Enumeration 2017-05-22 14:37:21 +02:00
FlightControl
220edef653 Merge remote-tracking branch 'refs/remotes/origin/master' into 419-A2A-Tasking 2017-05-22 12:22:34 +02:00
Sven Van de Velde
2619fe814a Merge pull request #540 from FlightControl-Master/419-A2A-Tasking
419 a2a tasking
2017-05-22 12:18:22 +02:00
FlightControl
599f31dfae Final tests done, publishing 2017-05-22 12:17:18 +02:00
FlightControl
8ab12e5e9a Progress 2017-05-22 10:59:19 +02:00
FlightControl
314032ba3d Progress 2017-05-22 09:54:07 +02:00
FlightControl
961658ee9a Small Fix 2017-05-20 22:07:36 +02:00
FlightControl
2b0fcd3426 Progress 2017-05-20 22:04:44 +02:00
Wingthor
824431ae94 Found a few error in my code, still does not work 2017-05-20 17:09:14 +02:00
FlightControl
923ea597ec Updates 2017-05-20 16:41:45 +02:00
Wingthor
9112d6cc6e Added posibilty to SetSmokeColor on Cargo
Someting is wrong with the setter, when calling it, it seems to be nil.
Need som advice
2017-05-20 16:36:12 +02:00
FlightControl
ece08e5e37 Got something working with these coordinates 2017-05-20 16:32:56 +02:00
FlightControl
96fdf72400 Progress implementing teh SETTINGS class and coordinates per Controllable of the player 2017-05-20 13:32:21 +02:00
FlightControl
5fd4f96fc8 Merge remote-tracking branch 'refs/remotes/origin/master' into 419-A2A-Tasking 2017-05-20 10:38:29 +02:00
FlightControl
13449cc9ee Progress designing system settings 2017-05-20 08:48:57 +02:00
Sven Van de Velde
eab81a2bf9 Merge pull request #534 from FlightControl-Master/419-A2A-Tasking
419 a2a tasking
2017-05-18 21:19:48 +02:00
FlightControl
264cf69a6f A2A_Dispatcher is ready, i think 2017-05-18 21:18:40 +02:00
FlightControl
0e9caf2d3f Merge remote-tracking branch 'refs/remotes/origin/master-release-prep' 2017-05-18 21:09:44 +02:00
FlightControl
45429c8f2a Dynamic 2017-05-18 21:08:53 +02:00
Sven Van de Velde
e2a0aa5573 Merge pull request #532 from FlightControl-Master/master-release-prep
Fix DETECTION_AREAS
2017-05-18 21:04:26 +02:00
FlightControl
a932f49554 Fix DetectedItemCount for DETECTION_AREAS 2017-05-18 20:59:36 +02:00
FlightControl
a57e24212e Merge remote-tracking branch 'refs/remotes/origin/master-release-2.1' into master-release-prep 2017-05-18 20:59:03 +02:00
FlightControl
312007b51c Progress 2017-05-18 20:53:15 +02:00
FlightControl
3106f62709 Progress 2017-05-18 16:26:31 +02:00
FlightControl
48595e1282 Merge remote-tracking branch 'refs/remotes/origin/master' into 419-A2A-Tasking
# Conflicts:
#	Moose Mission Setup/Moose.lua
2017-05-18 14:20:23 +02:00
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
Sven Van de Velde
0d2e398e37 Merge pull request #529 from FlightControl-Master/master-release-prep
Fixes #528
2017-05-18 14:14:39 +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
9abc4f9725 INTERCEPT logic working 2017-05-18 13:26:25 +02:00
FlightControl
cc064c95b1 Merge remote-tracking branch 'refs/remotes/origin/master' into 419-A2A-Tasking
# Conflicts:
#	Moose Mission Setup/Moose.lua
2017-05-18 12:22:09 +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
4b62fbd497 First A2A class 2017-05-17 19:12: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
1067 changed files with 192205 additions and 79899 deletions

5
.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,4 @@ _gsdata_/
#GITHUB
.gitattributes
.gitignore
Moose Test Missions/MOOSE_Test_Template.miz

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/GenerateDocumentations.bat}"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value="*.lua"/>
<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.

Binary file not shown.

View File

@@ -0,0 +1,553 @@
--- **AI** -- **AI A2A Air Patrolling or Staging.**
--
-- ====
--
-- ### Author: **Sven Van de Velde (FlightControl)**
-- ### Contributions:
--
-- * **[Dutch_Baron](https://forums.eagle.ru/member.php?u=112075)**: Working together with James has resulted in the creation of the AI_BALANCER class. James has shared his ideas on balancing AI with air units, and together we made a first design which you can use now :-)
-- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Testing and API concept review.
--
-- ====
--
-- @module AI_A2A
--BASE:TraceClass("AI_A2A")
--- @type AI_A2A
-- @extends Core.Fsm#FSM_CONTROLLABLE
--- # AI_A2A class, extends @{Fsm#FSM_CONTROLLABLE}
--
-- The AI_A2A class implements the core functions to operate an AI @{Group} A2A tasking.
--
--
-- ## AI_A2A constructor
--
-- * @{#AI_A2A.New}(): Creates a new AI_A2A object.
--
-- ## 2. AI_A2A is a FSM
--
-- ![Process](..\Presentations\AI_PATROL\Dia2.JPG)
--
-- ### 2.1. AI_A2A 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_A2A 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_A2A.SetControllable}(): Set the AIControllable.
-- * @{#AI_A2A.GetControllable}(): Get the AIControllable.
--
-- @field #AI_A2A
AI_A2A = {
ClassName = "AI_A2A",
}
--- Creates a new AI_A2A object
-- @param #AI_A2A self
-- @param Wrapper.Group#GROUP AIGroup The GROUP object to receive the A2A Process.
-- @return #AI_A2A
function AI_A2A:New( AIGroup )
-- Inherits from BASE
local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- #AI_A2A
self:SetControllable( AIGroup )
self:ManageFuel( .2, 60 )
self:ManageDamage( 0.4 )
self:SetStartState( "Stopped" )
self:AddTransition( "*", "Start", "Started" )
--- Start Handler OnBefore for AI_A2A
-- @function [parent=#AI_A2A] OnBeforeStart
-- @param #AI_A2A self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #boolean
--- Start Handler OnAfter for AI_A2A
-- @function [parent=#AI_A2A] OnAfterStart
-- @param #AI_A2A self
-- @param #string From
-- @param #string Event
-- @param #string To
--- Start Trigger for AI_A2A
-- @function [parent=#AI_A2A] Start
-- @param #AI_A2A self
--- Start Asynchronous Trigger for AI_A2A
-- @function [parent=#AI_A2A] __Start
-- @param #AI_A2A self
-- @param #number Delay
self:AddTransition( "*", "Stop", "Stopped" )
--- OnLeave Transition Handler for State Stopped.
-- @function [parent=#AI_A2A] OnLeaveStopped
-- @param #AI_A2A 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_A2A] OnEnterStopped
-- @param #AI_A2A 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_A2A] OnBeforeStop
-- @param #AI_A2A 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_A2A] OnAfterStop
-- @param #AI_A2A 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_A2A] Stop
-- @param #AI_A2A self
--- Asynchronous Event Trigger for Event Stop.
-- @function [parent=#AI_A2A] __Stop
-- @param #AI_A2A self
-- @param #number Delay The delay in seconds.
self:AddTransition( "*", "Status", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A.
--- OnBefore Transition Handler for Event Status.
-- @function [parent=#AI_A2A] OnBeforeStatus
-- @param #AI_A2A 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 Status.
-- @function [parent=#AI_A2A] OnAfterStatus
-- @param #AI_A2A 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 Status.
-- @function [parent=#AI_A2A] Status
-- @param #AI_A2A self
--- Asynchronous Event Trigger for Event Status.
-- @function [parent=#AI_A2A] __Status
-- @param #AI_A2A self
-- @param #number Delay The delay in seconds.
self:AddTransition( "*", "RTB", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A.
--- OnBefore Transition Handler for Event RTB.
-- @function [parent=#AI_A2A] OnBeforeRTB
-- @param #AI_A2A 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 RTB.
-- @function [parent=#AI_A2A] OnAfterRTB
-- @param #AI_A2A 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 RTB.
-- @function [parent=#AI_A2A] RTB
-- @param #AI_A2A self
--- Asynchronous Event Trigger for Event RTB.
-- @function [parent=#AI_A2A] __RTB
-- @param #AI_A2A self
-- @param #number Delay The delay in seconds.
--- OnLeave Transition Handler for State Returning.
-- @function [parent=#AI_A2A] OnLeaveReturning
-- @param #AI_A2A 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 Returning.
-- @function [parent=#AI_A2A] OnEnterReturning
-- @param #AI_A2A 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( "*", "Return", "Returning" )
self:AddTransition( "*", "Home", "Home" )
self:AddTransition( "*", "LostControl", "LostControl" )
self:AddTransition( "*", "Fuel", "Fuel" )
self:AddTransition( "*", "Damaged", "Damaged" )
self:AddTransition( "*", "Eject", "*" )
self:AddTransition( "*", "Crash", "Crashed" )
self:AddTransition( "*", "PilotDead", "*" )
self.IdleCount = 0
return self
end
function AI_A2A:SetDispatcher( Dispatcher )
self.Dispatcher = Dispatcher
end
function AI_A2A:GetDispatcher()
return self.Dispatcher
end
function AI_A2A:SetTargetDistance( Coordinate )
local CurrentCoord = self.Controllable:GetCoordinate()
self.TargetDistance = CurrentCoord:Get2DDistance( Coordinate )
self.ClosestTargetDistance = ( not self.ClosestTargetDistance or self.ClosestTargetDistance > self.TargetDistance ) and self.TargetDistance or self.ClosestTargetDistance
end
function AI_A2A:ClearTargetDistance()
self.TargetDistance = nil
self.ClosestTargetDistance = nil
end
--- Sets (modifies) the minimum and maximum speed of the patrol.
-- @param #AI_A2A self
-- @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.
-- @return #AI_A2A self
function AI_A2A:SetSpeed( PatrolMinSpeed, PatrolMaxSpeed )
self:F2( { PatrolMinSpeed, PatrolMaxSpeed } )
self.PatrolMinSpeed = PatrolMinSpeed
self.PatrolMaxSpeed = PatrolMaxSpeed
end
--- Sets the floor and ceiling altitude of the patrol.
-- @param #AI_A2A self
-- @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.
-- @return #AI_A2A self
function AI_A2A:SetAltitude( PatrolFloorAltitude, PatrolCeilingAltitude )
self:F2( { PatrolFloorAltitude, PatrolCeilingAltitude } )
self.PatrolFloorAltitude = PatrolFloorAltitude
self.PatrolCeilingAltitude = PatrolCeilingAltitude
end
--- Sets the home airbase.
-- @param #AI_A2A self
-- @param Wrapper.Airbase#AIRBASE HomeAirbase
-- @return #AI_A2A self
function AI_A2A:SetHomeAirbase( HomeAirbase )
self:F2( { HomeAirbase } )
self.HomeAirbase = HomeAirbase
end
--- Set the status checking off.
-- @param #AI_A2A self
-- @return #AI_A2A self
function AI_A2A:SetStatusOff()
self:F2()
self.CheckStatus = false
end
--- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base.
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel treshold is calculated.
-- When the fuel treshold is reached, the AI will continue for a given time its patrol task in orbit, while a new AIControllable is targetted to the AI_A2A.
-- Once the time is finished, the old AI will return to the base.
-- @param #AI_A2A self
-- @param #number PatrolFuelTresholdPercentage The treshold in percentage (between 0 and 1) when the AIControllable is considered to get out of fuel.
-- @param #number PatrolOutOfFuelOrbitTime The amount of seconds the out of fuel AIControllable will orbit before returning to the base.
-- @return #AI_A2A self
function AI_A2A:ManageFuel( PatrolFuelTresholdPercentage, PatrolOutOfFuelOrbitTime )
self.PatrolManageFuel = true
self.PatrolFuelTresholdPercentage = PatrolFuelTresholdPercentage
self.PatrolOutOfFuelOrbitTime = PatrolOutOfFuelOrbitTime
self.Controllable:OptionRTBBingoFuel( false )
return self
end
--- When the AI is damaged beyond a certain treshold, it is required that the AI returns to the home base.
-- However, damage cannot be foreseen early on.
-- Therefore, when the damage treshold is reached,
-- the AI will return immediately to the home base (RTB).
-- Note that for groups, the average damage of the complete group will be calculated.
-- So, in a group of 4 airplanes, 2 lost and 2 with damage 0.2, the damage treshold will be 0.25.
-- @param #AI_A2A self
-- @param #number PatrolDamageTreshold The treshold in percentage (between 0 and 1) when the AI is considered to be damaged.
-- @return #AI_A2A self
function AI_A2A:ManageDamage( PatrolDamageTreshold )
self.PatrolManageDamage = true
self.PatrolDamageTreshold = PatrolDamageTreshold
return self
end
--- Defines a new patrol route using the @{Process_PatrolZone} parameters and settings.
-- @param #AI_A2A self
-- @return #AI_A2A 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_A2A:onafterStart( Controllable, From, Event, To )
self:F2()
self:__Status( 10 ) -- Check status status every 30 seconds.
self:HandleEvent( EVENTS.PilotDead, self.OnPilotDead )
self:HandleEvent( EVENTS.Crash, self.OnCrash )
self:HandleEvent( EVENTS.Ejection, self.OnEjection )
Controllable:OptionROEHoldFire()
Controllable:OptionROTVertical()
end
--- @param #AI_A2A self
function AI_A2A:onbeforeStatus()
return self.CheckStatus
end
--- @param #AI_A2A self
function AI_A2A:onafterStatus()
self:F()
if self.Controllable and self.Controllable:IsAlive() then
local RTB = false
local Fuel = self.Controllable:GetUnit(1):GetFuel()
self:F({Fuel=Fuel})
if Fuel < self.PatrolFuelTresholdPercentage then
self:E( self.Controllable:GetName() .. " is out of fuel: " .. Fuel .. " ... RTB!" )
local OldAIControllable = self.Controllable
local AIControllableTemplate = self.Controllable:GetTemplate()
local OrbitTask = OldAIControllable:TaskOrbitCircle( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ), self.PatrolMinSpeed )
local TimedOrbitTask = OldAIControllable:TaskControlled( OrbitTask, OldAIControllable:TaskCondition(nil,nil,nil,nil,self.PatrolOutOfFuelOrbitTime,nil ) )
OldAIControllable:SetTask( TimedOrbitTask, 10 )
self:Fuel()
RTB = true
else
end
-- TODO: Check GROUP damage function.
local Damage = self.Controllable:GetLife()
local InitialLife = self.Controllable:GetLife0()
self:F( { Damage = Damage, InitialLife = InitialLife, DamageTreshold = self.PatrolDamageTreshold } )
if ( Damage / InitialLife ) < self.PatrolDamageTreshold then
self:E( self.Controllable:GetName() .. " is damaged: " .. Damage .. " ... RTB!" )
self:Damaged()
RTB = true
end
-- Check if planes went RTB and are out of control.
if self.Controllable:HasTask() == false then
if not self:Is( "Started" ) and
not self:Is( "Stopped" ) then
if self.IdleCount >= 2 then
self:E( self.Controllable:GetName() .. " control lost! " )
self:LostControl()
else
self.IdleCount = self.IdleCount + 1
end
end
else
self.IdleCount = 0
end
if RTB == true then
self:__RTB( 0.5 )
end
self:__Status( 10 )
end
end
--- @param Wrapper.Group#GROUP AIGroup
function AI_A2A.RTBRoute( AIGroup )
AIGroup:E( { "RTBRoute:", AIGroup:GetName() } )
local _AI_A2A = AIGroup:GetState( AIGroup, "AI_A2A" ) -- #AI_A2A
_AI_A2A:__RTB( 0.5 )
end
--- @param #AI_A2A self
-- @param Wrapper.Group#GROUP AIGroup
function AI_A2A:onafterRTB( AIGroup, From, Event, To )
self:F( { AIGroup, From, Event, To } )
if AIGroup and AIGroup:IsAlive() then
self:E( "Group " .. AIGroup:GetName() .. " ... RTB! ( " .. self:GetState() .. " )" )
self.CheckStatus = false
self:ClearTargetDistance()
AIGroup:ClearTasks()
local EngageRoute = {}
--- Calculate the target route point.
local CurrentCoord = AIGroup:GetCoordinate()
local ToTargetCoord = self.HomeAirbase:GetCoordinate()
local ToTargetSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed )
local ToAirbaseAngle = CurrentCoord:GetAngleDegrees( CurrentCoord:GetDirectionVec3( ToTargetCoord ) )
local Distance = CurrentCoord:Get2DDistance( ToTargetCoord )
local ToAirbaseCoord = CurrentCoord:Translate( 5000, ToAirbaseAngle )
if Distance < 5000 then
self:Home()
return
end
--- Create a route point of type air.
local ToPatrolRoutePoint = ToAirbaseCoord:RoutePointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
ToTargetSpeed,
true
)
self:F( { Angle = ToAirbaseAngle, ToTargetSpeed = ToTargetSpeed } )
self:T2( { self.MinSpeed, self.MaxSpeed, ToTargetSpeed } )
EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint
AIGroup:OptionROEHoldFire()
AIGroup:OptionROTEvadeFire()
--- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable...
AIGroup:WayPointInitialize( EngageRoute )
local Tasks = {}
Tasks[#Tasks+1] = AIGroup:TaskFunction( 1, 1, "AI_A2A.RTBRoute" )
EngageRoute[1].task = AIGroup:TaskCombo( Tasks )
AIGroup:SetState( AIGroup, "AI_A2A", self )
--- NOW ROUTE THE GROUP!
AIGroup:WayPointExecute( 1, 0 )
end
end
--- @param #AI_A2A self
-- @param Wrapper.Group#GROUP AIGroup
function AI_A2A:onafterHome( AIGroup, From, Event, To )
self:F( { AIGroup, From, Event, To } )
self:E( "Group " .. self.Controllable:GetName() .. " ... Home! ( " .. self:GetState() .. " )" )
if AIGroup and AIGroup:IsAlive() then
end
end
--- @param #AI_A2A self
function AI_A2A:onafterDead()
self:SetStatusOff()
end
--- @param #AI_A2A self
-- @param Core.Event#EVENTDATA EventData
function AI_A2A:OnCrash( EventData )
if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then
self:E( self.Controllable:GetUnits() )
if #self.Controllable:GetUnits() == 1 then
self:__Crash( 1, EventData )
end
end
end
--- @param #AI_A2A self
-- @param Core.Event#EVENTDATA EventData
function AI_A2A:OnEjection( EventData )
if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then
self:__Eject( 1, EventData )
end
end
--- @param #AI_A2A self
-- @param Core.Event#EVENTDATA EventData
function AI_A2A:OnPilotDead( EventData )
if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then
self:__PilotDead( 1, EventData )
end
end

View File

@@ -0,0 +1,491 @@
--- **AI** -- **Execute Combat Air Patrol (CAP).**
--
-- ![Banner Image](..\Presentations\AI_CAP\Dia1.JPG)
--
-- ===
--
-- AI CAP classes makes AI Controllables execute a Combat Air Patrol.
--
-- There are the following types of CAP classes defined:
--
-- * @{#AI_A2A_CAP}: Perform a CAP in a zone.
--
-- ====
--
-- ### Author: **Sven Van de Velde (FlightControl)**
--
-- ### 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.
-- * **[Whisper](http://forums.eagle.ru/member.php?u=3829): Testing.
-- * **[Delta99](https://forums.eagle.ru/member.php?u=125166): Testing.
--
-- ====
--
-- @module AI_A2A_Cap
--BASE:TraceClass("AI_A2A_CAP")
--- @type AI_A2A_CAP
-- @extends AI.AI_A2A_Patrol#AI_A2A_PATROL
--- # AI_A2A_CAP class, extends @{AI_CAP#AI_PATROL_ZONE}
--
-- The AI_A2A_CAP 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_A2A_CAP is assigned a @{Group} and this must be done before the AI_A2A_CAP process can be started using the **Start** event.
--
-- ![Process](..\Presentations\AI_CAP\Dia4.JPG)
--
-- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits.
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
--
-- ![Process](..\Presentations\AI_CAP\Dia5.JPG)
--
-- This cycle will continue.
--
-- ![Process](..\Presentations\AI_CAP\Dia6.JPG)
--
-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event.
--
-- ![Process](..\Presentations\AI_CAP\Dia9.JPG)
--
-- When enemies are detected, the AI will automatically engage the enemy.
--
-- ![Process](..\Presentations\AI_CAP\Dia10.JPG)
--
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB.
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
--
-- ![Process](..\Presentations\AI_CAP\Dia13.JPG)
--
-- ## 1. AI_A2A_CAP constructor
--
-- * @{#AI_A2A_CAP.New}(): Creates a new AI_A2A_CAP object.
--
-- ## 2. AI_A2A_CAP is a FSM
--
-- ![Process](..\Presentations\AI_CAP\Dia2.JPG)
--
-- ### 2.1 AI_A2A_CAP States
--
-- * **None** ( Group ): The process is not started yet.
-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone.
-- * **Engaging** ( Group ): The AI is engaging the bogeys.
-- * **Returning** ( Group ): The AI is returning to Base..
--
-- ### 2.2 AI_A2A_CAP Events
--
-- * **@{AI_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_A2A_CAP.Engage}**: Let the AI engage the bogeys.
-- * **@{#AI_A2A_CAP.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_A2A_CAP.Destroy}**: The AI has destroyed a bogey @{Unit}.
-- * **@{#AI_A2A_CAP.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_A2A_CAP.SetEngageRange}() to define that range.
--
-- ## 4. Set the Zone of Engagement
--
-- ![Zone](..\Presentations\AI_CAP\Dia12.JPG)
--
-- An optional @{Zone} can be set,
-- that will define when the AI will engage with the detected airborne enemy targets.
-- Use the method @{AI_Cap#AI_A2A_CAP.SetEngageZone}() to define that Zone.
--
-- ===
--
-- @field #AI_A2A_CAP
AI_A2A_CAP = {
ClassName = "AI_A2A_CAP",
}
--- Creates a new AI_A2A_CAP object
-- @param #AI_A2A_CAP self
-- @param Wrapper.Group#GROUP AIGroup
-- @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 Dcs.DCSTypes#Speed EngageMinSpeed The minimum speed of the @{Controllable} in km/h when engaging a target.
-- @param Dcs.DCSTypes#Speed EngageMaxSpeed The maximum speed of the @{Controllable} in km/h when engaging a target.
-- @param Dcs.DCSTypes#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
-- @return #AI_A2A_CAP
function AI_A2A_CAP:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageMinSpeed, EngageMaxSpeed, PatrolAltType )
-- Inherits from BASE
local self = BASE:Inherit( self, AI_A2A_PATROL:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) ) -- #AI_A2A_CAP
self.Accomplished = false
self.Engaging = false
self.EngageMinSpeed = EngageMinSpeed
self.EngageMaxSpeed = EngageMaxSpeed
self:AddTransition( { "Patrolling", "Engaging", "Returning" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_CAP.
--- OnBefore Transition Handler for Event Engage.
-- @function [parent=#AI_A2A_CAP] OnBeforeEngage
-- @param #AI_A2A_CAP self
-- @param Wrapper.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_A2A_CAP] OnAfterEngage
-- @param #AI_A2A_CAP 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_A2A_CAP] Engage
-- @param #AI_A2A_CAP self
--- Asynchronous Event Trigger for Event Engage.
-- @function [parent=#AI_A2A_CAP] __Engage
-- @param #AI_A2A_CAP self
-- @param #number Delay The delay in seconds.
--- OnLeave Transition Handler for State Engaging.
-- @function [parent=#AI_A2A_CAP] OnLeaveEngaging
-- @param #AI_A2A_CAP self
-- @param Wrapper.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_A2A_CAP] OnEnterEngaging
-- @param #AI_A2A_CAP 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", "Fired", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_CAP.
--- OnBefore Transition Handler for Event Fired.
-- @function [parent=#AI_A2A_CAP] OnBeforeFired
-- @param #AI_A2A_CAP self
-- @param Wrapper.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_A2A_CAP] OnAfterFired
-- @param #AI_A2A_CAP 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_A2A_CAP] Fired
-- @param #AI_A2A_CAP self
--- Asynchronous Event Trigger for Event Fired.
-- @function [parent=#AI_A2A_CAP] __Fired
-- @param #AI_A2A_CAP self
-- @param #number Delay The delay in seconds.
self:AddTransition( "*", "Destroy", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_CAP.
--- OnBefore Transition Handler for Event Destroy.
-- @function [parent=#AI_A2A_CAP] OnBeforeDestroy
-- @param #AI_A2A_CAP self
-- @param Wrapper.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_A2A_CAP] OnAfterDestroy
-- @param #AI_A2A_CAP 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_A2A_CAP] Destroy
-- @param #AI_A2A_CAP self
--- Asynchronous Event Trigger for Event Destroy.
-- @function [parent=#AI_A2A_CAP] __Destroy
-- @param #AI_A2A_CAP self
-- @param #number Delay The delay in seconds.
self:AddTransition( "Engaging", "Abort", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_CAP.
--- OnBefore Transition Handler for Event Abort.
-- @function [parent=#AI_A2A_CAP] OnBeforeAbort
-- @param #AI_A2A_CAP self
-- @param Wrapper.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_A2A_CAP] OnAfterAbort
-- @param #AI_A2A_CAP 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_A2A_CAP] Abort
-- @param #AI_A2A_CAP self
--- Asynchronous Event Trigger for Event Abort.
-- @function [parent=#AI_A2A_CAP] __Abort
-- @param #AI_A2A_CAP self
-- @param #number Delay The delay in seconds.
self:AddTransition( "Engaging", "Accomplish", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_CAP.
--- OnBefore Transition Handler for Event Accomplish.
-- @function [parent=#AI_A2A_CAP] OnBeforeAccomplish
-- @param #AI_A2A_CAP self
-- @param Wrapper.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_A2A_CAP] OnAfterAccomplish
-- @param #AI_A2A_CAP 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_A2A_CAP] Accomplish
-- @param #AI_A2A_CAP self
--- Asynchronous Event Trigger for Event Accomplish.
-- @function [parent=#AI_A2A_CAP] __Accomplish
-- @param #AI_A2A_CAP self
-- @param #number Delay The delay in seconds.
return self
end
--- Set the Engage Zone which defines where the AI will engage bogies.
-- @param #AI_A2A_CAP self
-- @param Core.Zone#ZONE EngageZone The zone where the AI is performing CAP.
-- @return #AI_A2A_CAP self
function AI_A2A_CAP:SetEngageZone( EngageZone )
self:F2()
if EngageZone then
self.EngageZone = EngageZone
else
self.EngageZone = nil
end
end
--- Set the Engage Range when the AI will engage with airborne enemies.
-- @param #AI_A2A_CAP self
-- @param #number EngageRange The Engage Range.
-- @return #AI_A2A_CAP self
function AI_A2A_CAP:SetEngageRange( EngageRange )
self:F2()
if EngageRange then
self.EngageRange = EngageRange
else
self.EngageRange = nil
end
end
--- onafter State Transition for Event Patrol.
-- @param #AI_A2A_CAP self
-- @param Wrapper.Controllable#CONTROLLABLE AIGroup The AI Group managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_A2A_CAP:onafterPatrol( AIGroup, From, Event, To )
-- Call the parent Start event handler
self:GetParent(self).onafterPatrol( self, AIGroup, From, Event, To )
self:HandleEvent( EVENTS.Dead )
end
-- todo: need to fix this global function
--- @param Wrapper.Group#GROUP AIGroup
function AI_A2A_CAP.AttackRoute( AIGroup )
local EngageZone = AIGroup:GetState( AIGroup, "AI_A2A_CAP" ) -- AI.AI_Cap#AI_A2A_CAP
EngageZone:__Engage( 0.5 )
end
--- @param #AI_A2A_CAP self
-- @param Wrapper.Controllable#CONTROLLABLE AIGroup 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_A2A_CAP:onbeforeEngage( AIGroup, From, Event, To )
if self.Accomplished == true then
return false
end
end
--- @param #AI_A2A_CAP self
-- @param Wrapper.Controllable#CONTROLLABLE AIGroup The AI Group managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_A2A_CAP:onafterAbort( AIGroup, From, Event, To )
AIGroup:ClearTasks()
self:__Route( 0.5 )
end
--- @param #AI_A2A_CAP self
-- @param Wrapper.Controllable#CONTROLLABLE AIGroup 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_A2A_CAP:onafterEngage( AIGroup, From, Event, To, AttackSetUnit )
self:F( { AIGroup, From, Event, To, AttackSetUnit} )
self.AttackSetUnit = AttackSetUnit or self.AttackSetUnit -- Core.Set#SET_UNIT
local FirstAttackUnit = self.AttackSetUnit:GetFirst()
if FirstAttackUnit then
if AIGroup:IsAlive() then
local EngageRoute = {}
--- Calculate the target route point.
local CurrentCoord = AIGroup:GetCoordinate()
local ToTargetCoord = self.AttackSetUnit:GetFirst():GetCoordinate()
local ToTargetSpeed = math.random( self.EngageMinSpeed, self.EngageMaxSpeed )
local ToInterceptAngle = CurrentCoord:GetAngleDegrees( CurrentCoord:GetDirectionVec3( ToTargetCoord ) )
--- Create a route point of type air.
local ToPatrolRoutePoint = CurrentCoord:Translate( 5000, ToInterceptAngle ):RoutePointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
ToTargetSpeed,
true
)
self:F( { Angle = ToInterceptAngle, ToTargetSpeed = ToTargetSpeed } )
self:T2( { self.MinSpeed, self.MaxSpeed, ToTargetSpeed } )
EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint
AIGroup:OptionROEOpenFire()
AIGroup:OptionROTPassiveDefense()
local AttackTasks = {}
for AttackUnitID, AttackUnit in pairs( self.AttackSetUnit:GetSet() ) do
local AttackUnit = AttackUnit -- Wrapper.Unit#UNIT
self:T( { "Attacking Unit:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsAir() } )
if AttackUnit:IsAlive() and AttackUnit:IsAir() then
AttackTasks[#AttackTasks+1] = AIGroup:TaskAttackUnit( AttackUnit )
end
end
--- 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 )
if #AttackTasks == 0 then
self:E("No targets found -> Going back to Patrolling")
self:__Abort( 0.5 )
else
AttackTasks[#AttackTasks+1] = AIGroup:TaskFunction( 1, #AttackTasks, "AI_A2A_CAP.AttackRoute" )
AttackTasks[#AttackTasks+1] = AIGroup:TaskOrbitCircle( 4000, self.PatrolMinSpeed )
EngageRoute[1].task = AIGroup:TaskCombo( AttackTasks )
--- Do a trick, link the NewEngageRoute function of the object to the AIControllable in a temporary variable ...
AIGroup:SetState( AIGroup, "AI_A2A_CAP", self )
end
--- NOW ROUTE THE GROUP!
AIGroup:WayPointExecute( 1, 0 )
end
else
self:E("No targets found -> Going back to Patrolling")
self:__Abort( 0.5 )
end
end
--- @param #AI_A2A_CAP 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_A2A_CAP:onafterAccomplish( Controllable, From, Event, To )
self.Accomplished = true
self:SetDetectionOff()
end
--- @param #AI_A2A_CAP 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_A2A_CAP:onafterDestroy( Controllable, From, Event, To, EventData )
if EventData.IniUnit then
self.AttackUnits[EventData.IniUnit] = nil
end
end
--- @param #AI_A2A_CAP self
-- @param Core.Event#EVENTDATA EventData
function AI_A2A_CAP:OnEventDead( EventData )
self:F( { "EventDead", EventData } )
if EventData.IniDCSUnit then
if self.AttackUnits and self.AttackUnits[EventData.IniUnit] then
self:__Destroy( 1, EventData )
end
end
end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,462 @@
--- **AI** -- **Execute Ground Controlled Interception (GCI).**
--
-- ![Banner Image](..\Presentations\AI_GCI\Dia1.JPG)
--
-- ===
--
-- AI A2A_INTEREPT class makes AI Groups execute an Intercept.
--
-- There are the following types of GCI classes defined:
--
-- * @{#AI_A2A_GCI}: Perform a GCI in a zone.
--
-- ====
--
-- ### Author: **Sven Van de Velde (FlightControl)**
--
-- ### Contributions:
--
-- ====
--
-- @module AI_A2A_GCI
--BASE:TraceClass("AI_A2A_GCI")
--- @type AI_A2A_GCI
-- @extends AI.AI_A2A#AI_A2A
--- # AI_A2A_GCI class, extends @{AI_A2A#AI_A2A}
--
-- The AI_A2A_GCI class implements the core functions to intercept intruders. The Engage function will intercept intruders.
--
-- ![Process](..\Presentations\AI_GCI\Dia3.JPG)
--
-- The AI_A2A_GCI is assigned a @{Group} and this must be done before the AI_A2A_GCI process can be started using the **Start** event.
--
-- ![Process](..\Presentations\AI_GCI\Dia4.JPG)
--
-- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits.
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
--
-- ![Process](..\Presentations\AI_GCI\Dia5.JPG)
--
-- This cycle will continue.
--
-- ![Process](..\Presentations\AI_GCI\Dia6.JPG)
--
-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event.
--
-- ![Process](..\Presentations\AI_GCI\Dia9.JPG)
--
-- When enemies are detected, the AI will automatically engage the enemy.
--
-- ![Process](..\Presentations\AI_GCI\Dia10.JPG)
--
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB.
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
--
-- ![Process](..\Presentations\AI_GCI\Dia13.JPG)
--
-- ## 1. AI_A2A_GCI constructor
--
-- * @{#AI_A2A_GCI.New}(): Creates a new AI_A2A_GCI object.
--
-- ## 2. AI_A2A_GCI is a FSM
--
-- ![Process](..\Presentations\AI_GCI\Dia2.JPG)
--
-- ### 2.1 AI_A2A_GCI States
--
-- * **None** ( Group ): The process is not started yet.
-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone.
-- * **Engaging** ( Group ): The AI is engaging the bogeys.
-- * **Returning** ( Group ): The AI is returning to Base..
--
-- ### 2.2 AI_A2A_GCI Events
--
-- * **@{AI_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_A2A_GCI.Engage}**: Let the AI engage the bogeys.
-- * **@{#AI_A2A_GCI.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_A2A_GCI.Destroy}**: The AI has destroyed a bogey @{Unit}.
-- * **@{#AI_A2A_GCI.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_GCI\Dia11.JPG)
--
-- An optional range can be set in meters,
-- that will define when the AI will engage with the detected airborne enemy targets.
-- The range can be beyond or smaller than the range of the Patrol Zone.
-- The range is applied at the position of the AI.
-- Use the method @{AI_GCI#AI_A2A_GCI.SetEngageRange}() to define that range.
--
-- ## 4. Set the Zone of Engagement
--
-- ![Zone](..\Presentations\AI_GCI\Dia12.JPG)
--
-- An optional @{Zone} can be set,
-- that will define when the AI will engage with the detected airborne enemy targets.
-- Use the method @{AI_Cap#AI_A2A_GCI.SetEngageZone}() to define that Zone.
--
-- ===
--
-- @field #AI_A2A_GCI
AI_A2A_GCI = {
ClassName = "AI_A2A_GCI",
}
--- Creates a new AI_A2A_GCI object
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIGroup
-- @return #AI_A2A_GCI
function AI_A2A_GCI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed )
-- Inherits from BASE
local self = BASE:Inherit( self, AI_A2A:New( AIGroup ) ) -- #AI_A2A_GCI
self.Accomplished = false
self.Engaging = false
self.EngageMinSpeed = EngageMinSpeed
self.EngageMaxSpeed = EngageMaxSpeed
self.PatrolMinSpeed = EngageMinSpeed
self.PatrolMaxSpeed = EngageMaxSpeed
self.PatrolAltType = "RADIO"
self:AddTransition( { "Started", "Engaging", "Returning" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_GCI.
--- OnBefore Transition Handler for Event Engage.
-- @function [parent=#AI_A2A_GCI] OnBeforeEngage
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Engage.
-- @function [parent=#AI_A2A_GCI] OnAfterEngage
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Engage.
-- @function [parent=#AI_A2A_GCI] Engage
-- @param #AI_A2A_GCI self
--- Asynchronous Event Trigger for Event Engage.
-- @function [parent=#AI_A2A_GCI] __Engage
-- @param #AI_A2A_GCI self
-- @param #number Delay The delay in seconds.
--- OnLeave Transition Handler for State Engaging.
-- @function [parent=#AI_A2A_GCI] OnLeaveEngaging
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnEnter Transition Handler for State Engaging.
-- @function [parent=#AI_A2A_GCI] OnEnterEngaging
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
self:AddTransition( "Engaging", "Fired", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_GCI.
--- OnBefore Transition Handler for Event Fired.
-- @function [parent=#AI_A2A_GCI] OnBeforeFired
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Fired.
-- @function [parent=#AI_A2A_GCI] OnAfterFired
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Fired.
-- @function [parent=#AI_A2A_GCI] Fired
-- @param #AI_A2A_GCI self
--- Asynchronous Event Trigger for Event Fired.
-- @function [parent=#AI_A2A_GCI] __Fired
-- @param #AI_A2A_GCI self
-- @param #number Delay The delay in seconds.
self:AddTransition( "*", "Destroy", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_GCI.
--- OnBefore Transition Handler for Event Destroy.
-- @function [parent=#AI_A2A_GCI] OnBeforeDestroy
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Destroy.
-- @function [parent=#AI_A2A_GCI] OnAfterDestroy
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Destroy.
-- @function [parent=#AI_A2A_GCI] Destroy
-- @param #AI_A2A_GCI self
--- Asynchronous Event Trigger for Event Destroy.
-- @function [parent=#AI_A2A_GCI] __Destroy
-- @param #AI_A2A_GCI self
-- @param #number Delay The delay in seconds.
self:AddTransition( "Engaging", "Abort", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_GCI.
--- OnBefore Transition Handler for Event Abort.
-- @function [parent=#AI_A2A_GCI] OnBeforeAbort
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Abort.
-- @function [parent=#AI_A2A_GCI] OnAfterAbort
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Abort.
-- @function [parent=#AI_A2A_GCI] Abort
-- @param #AI_A2A_GCI self
--- Asynchronous Event Trigger for Event Abort.
-- @function [parent=#AI_A2A_GCI] __Abort
-- @param #AI_A2A_GCI self
-- @param #number Delay The delay in seconds.
self:AddTransition( "Engaging", "Accomplish", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_GCI.
--- OnBefore Transition Handler for Event Accomplish.
-- @function [parent=#AI_A2A_GCI] OnBeforeAccomplish
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @return #boolean Return false to cancel Transition.
--- OnAfter Transition Handler for Event Accomplish.
-- @function [parent=#AI_A2A_GCI] OnAfterAccomplish
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
--- Synchronous Event Trigger for Event Accomplish.
-- @function [parent=#AI_A2A_GCI] Accomplish
-- @param #AI_A2A_GCI self
--- Asynchronous Event Trigger for Event Accomplish.
-- @function [parent=#AI_A2A_GCI] __Accomplish
-- @param #AI_A2A_GCI self
-- @param #number Delay The delay in seconds.
return self
end
--- onafter State Transition for Event Patrol.
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIGroup The AI Group managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_A2A_GCI:onafterEngage( AIGroup, From, Event, To )
self:HandleEvent( EVENTS.Dead )
end
-- todo: need to fix this global function
--- @param Wrapper.Group#GROUP AIControllable
function AI_A2A_GCI.InterceptRoute( AIControllable )
AIControllable:T( "NewEngageRoute" )
local EngageZone = AIControllable:GetState( AIControllable, "EngageZone" ) -- AI.AI_Cap#AI_A2A_GCI
EngageZone:__Engage( 0.5 )
end
--- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_A2A_GCI:onbeforeEngage( AIGroup, From, Event, To )
if self.Accomplished == true then
return false
end
end
--- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIGroup The AI Group managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_A2A_GCI:onafterAbort( AIGroup, From, Event, To )
AIGroup:ClearTasks()
self:Return()
self:__RTB( 0.5 )
end
--- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_A2A_GCI:onafterEngage( AIGroup, From, Event, To, AttackSetUnit )
self:F( { AIGroup, From, Event, To, AttackSetUnit} )
self.AttackSetUnit = AttackSetUnit or self.AttackSetUnit -- Core.Set#SET_UNIT
local FirstAttackUnit = self.AttackSetUnit:GetFirst()
if FirstAttackUnit then
if AIGroup:IsAlive() then
local EngageRoute = {}
--- Calculate the target route point.
local CurrentCoord = AIGroup:GetCoordinate()
local ToTargetCoord = self.AttackSetUnit:GetFirst():GetCoordinate()
self:SetTargetDistance( ToTargetCoord ) -- For RTB status check
local ToTargetSpeed = math.random( self.EngageMinSpeed, self.EngageMaxSpeed )
local ToInterceptAngle = CurrentCoord:GetAngleDegrees( CurrentCoord:GetDirectionVec3( ToTargetCoord ) )
--- Create a route point of type air.
local ToPatrolRoutePoint = CurrentCoord:Translate( 5000, ToInterceptAngle ):RoutePointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
ToTargetSpeed,
true
)
self:F( { Angle = ToInterceptAngle, ToTargetSpeed = ToTargetSpeed } )
self:T2( { self.EngageMinSpeed, self.EngageMaxSpeed, ToTargetSpeed } )
EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint
AIGroup:OptionROEOpenFire()
AIGroup:OptionROTPassiveDefense()
local AttackTasks = {}
for AttackUnitID, AttackUnit in pairs( self.AttackSetUnit:GetSet() ) do
local AttackUnit = AttackUnit -- Wrapper.Unit#UNIT
self:T( { "Intercepting Unit:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsAir() } )
if AttackUnit:IsAlive() and AttackUnit:IsAir() then
AttackTasks[#AttackTasks+1] = AIGroup:TaskAttackUnit( AttackUnit )
end
end
--- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable...
AIGroup:WayPointInitialize( EngageRoute )
if #AttackTasks == 0 then
self:E("No targets found -> Going RTB")
self:Return()
self:__RTB( 0.5 )
else
AttackTasks[#AttackTasks+1] = AIGroup:TaskFunction( 1, #AttackTasks, "AI_A2A_GCI.InterceptRoute" )
AttackTasks[#AttackTasks+1] = AIGroup:TaskOrbitCircle( 4000, self.EngageMinSpeed )
EngageRoute[1].task = AIGroup:TaskCombo( AttackTasks )
--- Do a trick, link the NewEngageRoute function of the object to the AIControllable in a temporary variable ...
AIGroup:SetState( AIGroup, "EngageZone", self )
end
--- NOW ROUTE THE GROUP!
AIGroup:WayPointExecute( 1, 0 )
end
else
self:E("No targets found -> Going RTB")
self:Return()
self:__RTB( 0.5 )
end
end
--- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_A2A_GCI:onafterAccomplish( AIGroup, From, Event, To )
self.Accomplished = true
self:SetDetectionOff()
end
--- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param Core.Event#EVENTDATA EventData
function AI_A2A_GCI:onafterDestroy( AIGroup, From, Event, To, EventData )
if EventData.IniUnit then
self.AttackUnits[EventData.IniUnit] = nil
end
end
--- @param #AI_A2A_GCI self
-- @param Core.Event#EVENTDATA EventData
function AI_A2A_GCI:OnEventDead( EventData )
self:F( { "EventDead", EventData } )
if EventData.IniDCSUnit then
if self.AttackUnits and self.AttackUnits[EventData.IniUnit] then
self:__Destroy( 1, EventData )
end
end
end

View File

@@ -0,0 +1,382 @@
--- **AI** -- **Air Patrolling or Staging.**
--
-- ![Banner Image](..\Presentations\AI_PATROL\Dia1.JPG)
--
-- ===
--
-- AI PATROL classes makes AI Controllables execute an Patrol.
--
-- There are the following types of PATROL classes defined:
--
-- * @{#AI_A2A_PATROL}: Perform a PATROL in a zone.
--
-- ====
--
-- # Demo Missions
--
-- ### [AI_PATROL Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/PAT%20-%20Patrolling)
--
-- ### [AI_PATROL Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/PAT%20-%20Patrolling)
--
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
--
-- ====
--
-- # YouTube Channel
--
-- ### [AI_PATROL YouTube Channel](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl35HvYZKA6G22WMt7iI3zky)
--
-- ====
--
-- ### Author: **Sven Van de Velde (FlightControl)**
-- ### Contributions:
--
-- * **[Dutch_Baron](https://forums.eagle.ru/member.php?u=112075)**: Working together with James has resulted in the creation of the AI_BALANCER class. James has shared his ideas on balancing AI with air units, and together we made a first design which you can use now :-)
-- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Testing and API concept review.
--
-- ====
--
-- @module AI_A2A_Patrol
--- @type AI_A2A_PATROL
-- @extends AI.AI_A2A#AI_A2A
--- # AI_A2A_PATROL class, extends @{Fsm#FSM_CONTROLLABLE}
--
-- The AI_A2A_PATROL class implements the core functions to patrol a @{Zone} by an AI @{Controllable} or @{Group}.
--
-- ![Process](..\Presentations\AI_PATROL\Dia3.JPG)
--
-- The AI_A2A_PATROL is assigned a @{Group} and this must be done before the AI_A2A_PATROL 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_A2A_PATROL constructor
--
-- * @{#AI_A2A_PATROL.New}(): Creates a new AI_A2A_PATROL object.
--
-- ## 2. AI_A2A_PATROL is a FSM
--
-- ![Process](..\Presentations\AI_PATROL\Dia2.JPG)
--
-- ### 2.1. AI_A2A_PATROL 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_A2A_PATROL 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_A2A_PATROL.SetControllable}(): Set the AIControllable.
-- * @{#AI_A2A_PATROL.GetControllable}(): Get the AIControllable.
--
-- ## 4. Set the Speed and Altitude boundaries of the AI controllable
--
-- * @{#AI_A2A_PATROL.SetSpeed}(): Set the patrol speed boundaries of the AI, for the next patrol.
-- * @{#AI_A2A_PATROL.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_A2A_PATROL.SetDetectionOn}(): Set the detection on. The AI will detect for targets.
-- * @{#AI_A2A_PATROL.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_A2A_PATROL.SetDetectionInterval}( seconds ), where the amount of seconds specify how much seconds will be waited before the next detection.
-- Use the method @{#AI_A2A_PATROL.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_A2A_PATROL.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_A2A_PATROL
--
-- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base.
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel treshold is calculated.
-- When the fuel treshold is reached, the AI will continue for a given time its patrol task in orbit,
-- while a new AI is targetted to the AI_A2A_PATROL.
-- Once the time is finished, the old AI will return to the base.
-- Use the method @{#AI_A2A_PATROL.ManageFuel}() to have this proces in place.
--
-- ## 7. Manage "damage" behaviour of the AI in the AI_A2A_PATROL
--
-- 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_A2A_PATROL.ManageDamage}() to have this proces in place.
--
-- ===
--
-- @field #AI_A2A_PATROL
AI_A2A_PATROL = {
ClassName = "AI_A2A_PATROL",
}
--- Creates a new AI_A2A_PATROL object
-- @param #AI_A2A_PATROL self
-- @param Wrapper.Group#GROUP AIGroup
-- @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 Dcs.DCSTypes#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
-- @return #AI_A2A_PATROL self
-- @usage
-- -- Define a new AI_A2A_PATROL Object. This PatrolArea will patrol an AIControllable within PatrolZone between 3000 and 6000 meters, with a variying speed between 600 and 900 km/h.
-- PatrolZone = ZONE:New( 'PatrolZone' )
-- PatrolSpawn = SPAWN:New( 'Patrol Group' )
-- PatrolArea = AI_A2A_PATROL:New( PatrolZone, 3000, 6000, 600, 900 )
function AI_A2A_PATROL:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
-- Inherits from BASE
local self = BASE:Inherit( self, AI_A2A:New( AIGroup ) ) -- #AI_A2A_PATROL
self.PatrolZone = PatrolZone
self.PatrolFloorAltitude = PatrolFloorAltitude
self.PatrolCeilingAltitude = PatrolCeilingAltitude
self.PatrolMinSpeed = PatrolMinSpeed
self.PatrolMaxSpeed = PatrolMaxSpeed
-- defafult PatrolAltType to "RADIO" if not specified
self.PatrolAltType = PatrolAltType or "RADIO"
self:AddTransition( "Started", "Patrol", "Patrolling" )
--- OnBefore Transition Handler for Event Patrol.
-- @function [parent=#AI_A2A_PATROL] OnBeforePatrol
-- @param #AI_A2A_PATROL 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 Patrol.
-- @function [parent=#AI_A2A_PATROL] OnAfterPatrol
-- @param #AI_A2A_PATROL 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 Patrol.
-- @function [parent=#AI_A2A_PATROL] Patrol
-- @param #AI_A2A_PATROL self
--- Asynchronous Event Trigger for Event Patrol.
-- @function [parent=#AI_A2A_PATROL] __Patrol
-- @param #AI_A2A_PATROL self
-- @param #number Delay The delay in seconds.
--- OnLeave Transition Handler for State Patrolling.
-- @function [parent=#AI_A2A_PATROL] OnLeavePatrolling
-- @param #AI_A2A_PATROL 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 Patrolling.
-- @function [parent=#AI_A2A_PATROL] OnEnterPatrolling
-- @param #AI_A2A_PATROL 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( "Patrolling", "Route", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_PATROL.
--- OnBefore Transition Handler for Event Route.
-- @function [parent=#AI_A2A_PATROL] OnBeforeRoute
-- @param #AI_A2A_PATROL 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 Route.
-- @function [parent=#AI_A2A_PATROL] OnAfterRoute
-- @param #AI_A2A_PATROL 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 Route.
-- @function [parent=#AI_A2A_PATROL] Route
-- @param #AI_A2A_PATROL self
--- Asynchronous Event Trigger for Event Route.
-- @function [parent=#AI_A2A_PATROL] __Route
-- @param #AI_A2A_PATROL self
-- @param #number Delay The delay in seconds.
self:AddTransition( "*", "Reset", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_PATROL.
return self
end
--- Sets (modifies) the minimum and maximum speed of the patrol.
-- @param #AI_A2A_PATROL self
-- @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.
-- @return #AI_A2A_PATROL self
function AI_A2A_PATROL:SetSpeed( PatrolMinSpeed, PatrolMaxSpeed )
self:F2( { PatrolMinSpeed, PatrolMaxSpeed } )
self.PatrolMinSpeed = PatrolMinSpeed
self.PatrolMaxSpeed = PatrolMaxSpeed
end
--- Sets the floor and ceiling altitude of the patrol.
-- @param #AI_A2A_PATROL self
-- @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.
-- @return #AI_A2A_PATROL self
function AI_A2A_PATROL:SetAltitude( PatrolFloorAltitude, PatrolCeilingAltitude )
self:F2( { PatrolFloorAltitude, PatrolCeilingAltitude } )
self.PatrolFloorAltitude = PatrolFloorAltitude
self.PatrolCeilingAltitude = PatrolCeilingAltitude
end
--- Defines a new patrol route using the @{Process_PatrolZone} parameters and settings.
-- @param #AI_A2A_PATROL self
-- @return #AI_A2A_PATROL 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_A2A_PATROL:onafterPatrol( Controllable, From, Event, To )
self:F2()
self:ClearTargetDistance()
self:__Route( 1 )
self.Controllable:OnReSpawn(
function( PatrolGroup )
self:E( "ReSpawn" )
self:__Reset( 1 )
self:__Route( 5 )
end
)
end
--- @param Wrapper.Group#GROUP AIGroup
-- This statis method is called from the route path within the last task at the last waaypoint of the Controllable.
-- Note that this method is required, as triggers the next route when patrolling for the Controllable.
function AI_A2A_PATROL.PatrolRoute( AIGroup )
local _AI_A2A_Patrol = AIGroup:GetState( AIGroup, "AI_A2A_PATROL" ) -- #AI_A2A_PATROL
_AI_A2A_Patrol:Route()
end
--- Defines a new patrol route using the @{Process_PatrolZone} parameters and settings.
-- @param #AI_A2A_PATROL self
-- @param Wrapper.Group#GROUP AIGroup The AIGroup managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_A2A_PATROL:onafterRoute( AIGroup, From, Event, To )
self:F2()
-- When RTB, don't allow anymore the routing.
if From == "RTB" then
return
end
if AIGroup:IsAlive() then
local PatrolRoute = {}
--- Calculate the target route point.
local CurrentCoord = AIGroup:GetCoordinate()
local ToTargetCoord = self.PatrolZone:GetRandomPointVec2()
ToTargetCoord:SetAlt(math.random( self.PatrolFloorAltitude,self.PatrolCeilingAltitude ) )
self:SetTargetDistance( ToTargetCoord ) -- For RTB status check
local ToTargetSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed )
--- Create a route point of type air.
local ToPatrolRoutePoint = ToTargetCoord:RoutePointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
ToTargetSpeed,
true
)
PatrolRoute[#PatrolRoute+1] = ToPatrolRoutePoint
--- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable...
AIGroup:WayPointInitialize( PatrolRoute )
local Tasks = {}
Tasks[#Tasks+1] = AIGroup:TaskFunction( 1, 1, "AI_A2A_PATROL.PatrolRoute" )
PatrolRoute[1].task = AIGroup:TaskCombo( Tasks )
--- Do a trick, link the NewPatrolRoute function of the PATROLGROUP object to the AIControllable in a temporary variable ...
AIGroup:SetState( AIGroup, "AI_A2A_PATROL", self )
--- NOW ROUTE THE GROUP!
AIGroup:WayPointExecute( 1, 2 )
end
end

View File

@@ -0,0 +1,659 @@
--- **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)
--
-- ====
--
-- ### Author: **Sven Van de Velde (FlightControl)**
--
-- ### Contributions:
--
-- * **[Gunterlund](http://forums.eagle.ru:8080/member.php?u=75036)**: Test case revision.
--
-- ====
--
-- @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

@@ -1,14 +1,46 @@
--- Single-Player:**No** / Multi-Player:**Yes** / AI:**Yes** / Human:**No** / Types:**All** -- **AI Balancing will replace in multi player missions
--- **AI** -- **AI Balancing will replace in multi player missions
-- non-occupied human slots with AI groups, in order to provide an engaging simulation environment,
-- even when there are hardly any players in the mission.**
--
-- ![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
-- ### [AI_BALANCER Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/AIB%20-%20AI%20Balancing)
--
-- ### [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)
--
-- ====
--
-- # YouTube Channel
--
-- ### [AI_BALANCER YouTube Channel](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl2CJVIrL1TdAumuVS8n64B7)
--
-- ====
--
-- ### Author: **Sven Van de Velde (FlightControl)**
-- ### Contributions:
--
-- * **[Dutch_Baron](https://forums.eagle.ru/member.php?u=112075)**: Working together with James has resulted in the creation of the AI_BALANCER class. James has shared his ideas on balancing AI with air units, and together we made a first design which you can use now :-)
--
-- ====
--
-- @module 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.
--
@@ -18,17 +50,17 @@
--
-- 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.
-- * @{#AI_BALANCER.OnAfterSpawned}( AISet, From, Event, To, AIGroup ): Define to add extra logic when an AI is spawned.
--
-- ## 1.1) AI_BALANCER construction
-- ## 1. AI_BALANCER construction
--
-- Create a new AI_BALANCER object with the @{#AI_BALANCER.New}() method:
--
-- ## 1.2) AI_BALANCER is a FSM
-- ## 2. AI_BALANCER is a FSM
--
-- ![Process](..\Presentations\AI_Balancer\Dia13.JPG)
--
-- ### 1.2.1) AI_BALANCER States
-- ### 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.
@@ -36,7 +68,7 @@
-- * **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
-- ### 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.
@@ -44,11 +76,11 @@
-- * **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
-- ## 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
-- ## 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.
@@ -59,43 +91,8 @@
--
-- 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**
--
-- 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-17: There is still a problem with AI being destroyed, but not respawned. Need to check further upon that.
--
-- 2017-01-08: AI_BALANCER:**InitSpawnInterval( Earliest, Latest )** added.
--
-- ===
--
-- # **AUTHORS and CONTRIBUTIONS**
--
-- ### Contributions:
--
-- * **[Dutch_Baron](https://forums.eagle.ru/member.php?u=112075)**: Working together with James has resulted in the creation of the AI_BALANCER class. James has shared his ideas on balancing AI with air units, and together we made a first design which you can use now :-)
-- * **SNAFU**: Had a couple of mails with the guys to validate, if the same concept in the GCI/CAP script could be reworked within MOOSE. None of the script code has been used however within the new AI_BALANCER moose class.
--
-- ### Authors:
--
-- * FlightControl: Framework Design & Programming and Documentation.
--
-- @module AI_Balancer
--- AI_BALANCER class
-- @type AI_BALANCER
-- @field Core.Set#SET_CLIENT SetClient
-- @field Functional.Spawn#SPAWN SpawnAI
-- @field Wrapper.Group#GROUP Test
-- @extends Core.Fsm#FSM_SET
-- @field #AI_BALANCER
AI_BALANCER = {
ClassName = "AI_BALANCER",
PatrolZones = {},

View File

@@ -1,12 +1,57 @@
--- 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}
-- AI CAP classes makes AI Controllables execute a Combat Air Patrol.
--
-- There are the following types of CAP classes defined:
--
-- * @{#AI_CAP_ZONE}: Perform a CAP in a zone.
--
-- ====
--
-- # Demo Missions
--
-- ### [AI_CAP Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/CAP%20-%20Combat%20Air%20Patrol)
--
-- ### [AI_CAP Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/CAP%20-%20Combat%20Air%20Patrol)
--
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
--
-- ====
--
-- # YouTube Channel
--
-- ### [AI_CAP YouTube Channel](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl1YCyPxJgoZn-CfhwyeW65L)
--
-- ====
--
-- ### Author: **Sven Van de Velde (FlightControl)**
--
-- ### 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.
-- * **[Whisper](http://forums.eagle.ru/member.php?u=3829): Testing.
-- * **[Delta99](https://forums.eagle.ru/member.php?u=125166): Testing.
--
-- ====
--
-- @module AI_Cap
--- @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)
@@ -37,32 +82,35 @@
--
-- ![Process](..\Presentations\AI_CAP\Dia13.JPG)
--
-- ## 1.1) AI_CAP_ZONE constructor
-- ## 1. AI_CAP_ZONE constructor
--
-- * @{#AI_CAP_ZONE.New}(): Creates a new AI_CAP_ZONE object.
--
-- ## 1.2) AI_CAP_ZONE is a FSM
-- ## 2. AI_CAP_ZONE is a FSM
--
-- ![Process](..\Presentations\AI_CAP\Dia2.JPG)
--
-- ### 1.2.1) AI_CAP_ZONE States
-- ### 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
-- ### 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.
-- * **@{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.
--
-- ## 1.3) Set the Range of Engagement
-- ## 3. Set the Range of Engagement
--
-- ![Range](..\Presentations\AI_CAP\Dia11.JPG)
--
@@ -72,51 +120,17 @@
-- 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
-- ## 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.
--
-- ====
--
-- # **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.
-- * **[Whisper](http://forums.eagle.ru/member.php?u=3829): Testing.
-- * **[Delta99](https://forums.eagle.ru/member.php?u=125166): Testing.
--
-- ### Authors:
--
-- * **FlightControl**: Concept, Design & Programming.
--
-- @module AI_Cap
--- AI_CAP_ZONE class
-- @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
--
-- @field #AI_CAP_ZONE
AI_CAP_ZONE = {
ClassName = "AI_CAP_ZONE",
}
@@ -340,9 +354,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 )
@@ -385,13 +402,25 @@ function AI_CAP_ZONE:onafterDetected( Controllable, From, Event, To )
end
if Engage == true then
self:E( 'Detected -> Engaging' )
self:F( 'Detected -> Engaging' )
self:__Engage( 1 )
end
end
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.
@@ -456,13 +485,13 @@ function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To )
if DetectedUnit:IsAlive() and DetectedUnit:IsAir() then
if self.EngageZone then
if DetectedUnit:IsInZone( self.EngageZone ) then
self:E( {"Within Zone and Engaging ", DetectedUnit } )
self:F( {"Within Zone and Engaging ", DetectedUnit } )
AttackTasks[#AttackTasks+1] = Controllable:TaskAttackUnit( DetectedUnit )
end
else
if self.EngageRange then
if DetectedUnit:GetPointVec3():Get2DDistance(Controllable:GetPointVec3() ) <= self.EngageRange then
self:E( {"Within Range and Engaging", DetectedUnit } )
self:F( {"Within Range and Engaging", DetectedUnit } )
AttackTasks[#AttackTasks+1] = Controllable:TaskAttackUnit( DetectedUnit )
end
else
@@ -479,7 +508,7 @@ function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To )
if #AttackTasks == 0 then
self:E("No targets found -> Going back to Patrolling")
self:F("No targets found -> Going back to Patrolling")
self:__Abort( 1 )
self:__Route( 1 )
self:SetDetectionActivated()
@@ -505,14 +534,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 +544,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,58 @@
--- 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)
--
-- ====
--
--
-- ### Author: **Sven Van de Velde (FlightControl)**
--
-- ### 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.
--
-- ====
--
-- @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 +106,37 @@
--
-- ![Engage Event](..\Presentations\AI_CAS\Dia12.JPG)
--
-- # 1.1) AI_CAS_ZONE constructor
-- ## AI_CAS_ZONE constructor
--
-- * @{#AI_CAS_ZONE.New}(): Creates a new AI_CAS_ZONE object.
--
-- ## 1.2) AI_CAS_ZONE is a FSM
-- ## 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 +172,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 +182,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 +368,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 +378,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 +413,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 +428,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 +444,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 +463,6 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To, EngageSpeed,
self.EngageDirection = EngageDirection
if Controllable:IsAlive() then
local EngageRoute = {}
@@ -451,29 +483,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 +523,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 +568,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,13 +1,58 @@
--- 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:
--
-- * @{#AI_PATROL_ZONE}: Perform a PATROL in a zone.
--
-- ====
--
-- # Demo Missions
--
-- ### [AI_PATROL Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/PAT%20-%20Patrolling)
--
-- ### [AI_PATROL Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/PAT%20-%20Patrolling)
--
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
--
-- ====
--
-- # YouTube Channel
--
-- ### [AI_PATROL YouTube Channel](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl35HvYZKA6G22WMt7iI3zky)
--
-- ====
--
-- ### Author: **Sven Van de Velde (FlightControl)**
-- ### Contributions:
--
-- * **[Dutch_Baron](https://forums.eagle.ru/member.php?u=112075)**: Working together with James has resulted in the creation of the AI_BALANCER class. James has shared his ideas on balancing AI with air units, and together we made a first design which you can use now :-)
-- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Testing and API concept review.
--
-- ====
--
-- @module AI_Patrol
--- AI_PATROL_ZONE class
-- @type AI_PATROL_ZONE
-- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling.
-- @field Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed.
-- @field Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @field Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @field Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h.
-- @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)
--
@@ -38,40 +83,43 @@
--
-- ![Process](..\Presentations\AI_PATROL\Dia11.JPG)
--
-- ## 1.1) AI_PATROL_ZONE constructor
-- ## 1. AI_PATROL_ZONE constructor
--
-- * @{#AI_PATROL_ZONE.New}(): Creates a new AI_PATROL_ZONE object.
--
-- ## 1.2) AI_PATROL_ZONE is a FSM
-- ## 2. AI_PATROL_ZONE is a FSM
--
-- ![Process](..\Presentations\AI_PATROL\Dia2.JPG)
--
-- ### 1.2.1) AI_PATROL_ZONE States
-- ### 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..
-- * **Returning** ( Group ): The AI is returning to Base.
-- * **Stopped** ( Group ): The process is stopped.
-- * **Crashed** ( Group ): The AI has crashed or is dead.
--
-- ### 1.2.2) AI_PATROL_ZONE Events
-- ### 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.
--
-- ## 1.3) Set or Get the AI controllable
-- ## 3. Set or Get the AI controllable
--
-- * @{#AI_PATROL_ZONE.SetControllable}(): Set the AIControllable.
-- * @{#AI_PATROL_ZONE.GetControllable}(): Get the AIControllable.
--
-- ## 1.4) Set the Speed and Altitude boundaries of the AI controllable
-- ## 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.
--
-- ## 1.5) Manage the detection process of the AI controllable
-- ## 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.
@@ -88,7 +136,7 @@
-- 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
-- ## 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.
@@ -97,65 +145,15 @@
-- 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
-- ## 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.
--
-- ====
--
-- # **OPEN ISSUES**
--
-- 2017-01-17: When Spawned AI is located at an airbase, it will be routed first back to the airbase after take-off.
--
-- 2016-01-17:
-- -- Fixed problem with AI returning to base too early and unexpected.
-- -- ReSpawning of AI will reset the AI_PATROL and derived classes.
-- -- Checked the correct workings of SCHEDULER, and it DOES work correctly.
--
-- ====
--
-- # **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-17: Rename of class: **AI\_PATROL\_ZONE** is the new name for the old _AI\_PATROLZONE_.
--
-- 2017-01-15: Complete revision. AI_PATROL_ZONE is the base class for other AI_PATROL like classes.
--
-- 2016-09-01: Initial class and API.
--
-- ===
--
-- # **AUTHORS and CONTRIBUTIONS**
--
-- ### Contributions:
--
-- * **[Dutch_Baron](https://forums.eagle.ru/member.php?u=112075)**: Working together with James has resulted in the creation of the AI_BALANCER class. James has shared his ideas on balancing AI with air units, and together we made a first design which you can use now :-)
-- * **[Pikey](https://forums.eagle.ru/member.php?u=62835)**: Testing and API concept review.
--
-- ### Authors:
--
-- * **FlightControl**: Design & Programming.
--
-- @module AI_Patrol
--- AI_PATROL_ZONE class
-- @type AI_PATROL_ZONE
-- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling.
-- @field Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed.
-- @field Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @field Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @field Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h.
-- @field Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h.
-- @field Functional.Spawn#SPAWN CoordTest
-- @extends Core.Fsm#FSM_CONTROLLABLE
-- @field #AI_PATROL_ZONE
AI_PATROL_ZONE = {
ClassName = "AI_PATROL_ZONE",
}
@@ -201,6 +199,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
@@ -91,19 +70,20 @@ do -- ACT_ACCOUNT
-- Inherits from BASE
local self = BASE:Inherit( self, FSM_PROCESS:New() ) -- Core.Fsm#FSM_PROCESS
self:AddTransition( "Assigned", "Start", "Waiting")
self:AddTransition( "*", "Wait", "Waiting")
self:AddTransition( "*", "Report", "Report")
self:AddTransition( "*", "Event", "Account")
self:AddTransition( "Account", "More", "Wait")
self:AddTransition( "Account", "NoMore", "Accounted")
self:AddTransition( "*", "Fail", "Failed")
self:AddTransition( "Assigned", "Start", "Waiting" )
self:AddTransition( "*", "Wait", "Waiting" )
self:AddTransition( "*", "Report", "Report" )
self:AddTransition( "*", "Event", "Account" )
self:AddTransition( "Account", "Player", "AccountForPlayer" )
self:AddTransition( "Account", "Other", "AccountForOther" )
self:AddTransition( { "Account", "AccountForPlayer", "AccountForOther" }, "More", "Wait" )
self:AddTransition( { "Account", "AccountForPlayer", "AccountForOther" }, "NoMore", "Accounted" )
self:AddTransition( "*", "Fail", "Failed" )
self:AddEndState( "Accounted" )
self:AddEndState( "Failed" )
self:SetStartState( "Assigned" )
return self
end
@@ -118,6 +98,8 @@ do -- ACT_ACCOUNT
function ACT_ACCOUNT:onafterStart( ProcessUnit, From, Event, To )
self:HandleEvent( EVENTS.Dead, self.onfuncEventDead )
self:HandleEvent( EVENTS.Crash, self.onfuncEventCrash )
self:HandleEvent( EVENTS.Hit )
self:__Wait( 1 )
end
@@ -156,7 +138,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 +185,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 +193,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." )
@@ -218,48 +202,107 @@ do -- ACT_ACCOUNT_DEADS
--- StateMachine callback function
-- @param #ACT_ACCOUNT_DEADS self
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
-- @param #string Event
-- @param Wrapper.Client#CLIENT ProcessClient
-- @param Tasking.Task#TASK Task
-- @param #string From
-- @param #string Event
-- @param #string To
function ACT_ACCOUNT_DEADS:onenterAccount( ProcessUnit, From, Event, To, EventData )
self:T( { ProcessUnit, EventData, From, Event, To } )
self:T({self.Controllable})
self.TargetSetUnit:Flush()
-- @param Core.Event#EVENTDATA EventData
function ACT_ACCOUNT_DEADS:onafterEvent( ProcessClient, Task, From, Event, To, EventData )
self:T( { ProcessClient:GetName(), Task:GetName(), From, Event, To, EventData } )
if self.TargetSetUnit:FindUnit( EventData.IniUnitName ) then
local TaskGroup = ProcessUnit:GetGroup()
self.TargetSetUnit:RemoveUnitsByName( 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." )
local PlayerName = ProcessClient:GetPlayerName()
local PlayerHit = self.PlayerHits and self.PlayerHits[EventData.IniUnitName]
if PlayerHit == PlayerName then
self:Player( EventData )
else
self:Other( EventData )
end
end
end
--- StateMachine callback function
-- @param #ACT_ACCOUNT_DEADS self
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
-- @param #string Event
-- @param Wrapper.Client#CLIENT ProcessClient
-- @param Tasking.Task#TASK Task
-- @param #string From
-- @param #string Event
-- @param #string To
function ACT_ACCOUNT_DEADS:onafterEvent( ProcessUnit, From, Event, To, EventData )
-- @param Core.Event#EVENTDATA EventData
function ACT_ACCOUNT_DEADS:onenterAccountForPlayer( ProcessClient, Task, From, Event, To, EventData )
self:T( { ProcessClient:GetName(), Task:GetName(), From, Event, To, EventData } )
local TaskGroup = ProcessClient:GetGroup()
self.TargetSetUnit:Remove( EventData.IniUnitName )
self:Message( "You have destroyed a target. Your group assigned with task " .. self.TaskName .. " has " .. self.TargetSetUnit:Count() .. " targets ( " .. self.TargetSetUnit:GetUnitTypesText() .. " ) left to be destroyed." )
local PlayerName = ProcessClient:GetPlayerName()
Task:AddProgress( PlayerName, "Destroyed " .. EventData.IniTypeName, timer.getTime(), 1 )
if self.TargetSetUnit:Count() > 0 then
self:__More( 1 )
else
self:__NoMore( 1 )
end
end
--- StateMachine callback function
-- @param #ACT_ACCOUNT_DEADS self
-- @param Wrapper.Client#CLIENT ProcessClient
-- @param Tasking.Task#TASK Task
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Core.Event#EVENTDATA EventData
function ACT_ACCOUNT_DEADS:onenterAccountForOther( ProcessClient, Task, From, Event, To, EventData )
self:T( { ProcessClient:GetName(), Task:GetName(), From, Event, To, EventData } )
local TaskGroup = ProcessClient:GetGroup()
self.TargetSetUnit:Remove( EventData.IniUnitName )
self:Message( "One of the task targets has been destroyed. Your group assigned with task " .. self.TaskName .. " has " .. self.TargetSetUnit:Count() .. " targets ( " .. self.TargetSetUnit:GetUnitTypesText() .. " ) left to be destroyed." )
if self.TargetSetUnit:Count() > 0 then
self:__More( 1 )
else
self:__NoMore( 1 )
end
end
--- DCS Events
--- @param #ACT_ACCOUNT_DEADS self
-- @param Core.Event#EVENTDATA EventData
function ACT_ACCOUNT_DEADS:OnEventHit( EventData )
self:T( { "EventDead", EventData } )
if EventData.IniPlayerName and EventData.TgtDCSUnitName then
self.PlayerHits = self.PlayerHits or {}
self.PlayerHits[EventData.TgtDCSUnitName] = EventData.IniPlayerName
end
end
--- @param #ACT_ACCOUNT_DEADS self
-- @param Event#EVENTDATA EventData
function ACT_ACCOUNT_DEADS:onfuncEventDead( EventData )
self:T( { "EventDead", EventData } )
if EventData.IniDCSUnit then
self:__Event( 1, EventData )
self:Event( EventData )
end
end
--- DCS Events
--- @param #ACT_ACCOUNT_DEADS self
-- @param Event#EVENTDATA EventData
function ACT_ACCOUNT_DEADS:onfuncEventCrash( EventData )
self:T( { "EventDead", EventData } )
if EventData.IniDCSUnit then
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,104 @@ 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
-- @param Wrapper.Controllable#CONTROLLABLE Controllable
-- @return #string
function ACT_ROUTE:GetRouteText( Controllable )
self:E()
local RouteText = ""
local Coordinate = nil -- Core.Point#COORDINATE
if self.Coordinate then
Coordinate = self.Coordinate
end
if self.Zone then
Coordinate = self.Zone:GetPointVec3( self.Altitude )
Coordinate:SetHeading( self.Heading )
end
local CC = self:GetTask():GetMission():GetCommandCenter()
if CC then
if CC:IsModeWWII() then
-- Find closest reference point to the target.
local ShortestDistance = 0
local ShortestReferencePoint = nil
local ShortestReferenceName = ""
self:E( { CC.ReferencePoints } )
for ZoneName, Zone in pairs( CC.ReferencePoints ) do
self:E( { ZoneName = ZoneName } )
local Zone = Zone -- Core.Zone#ZONE
local ZoneCoord = Zone:GetCoordinate()
local ZoneDistance = ZoneCoord:Get2DDistance( self.Coordinate )
self:E( { ShortestDistance, ShortestReferenceName } )
if ShortestDistance == 0 or ZoneDistance < ShortestDistance then
ShortestDistance = ZoneDistance
ShortestReferencePoint = ZoneCoord
ShortestReferenceName = CC.ReferenceNames[ZoneName]
end
end
if ShortestReferencePoint then
RouteText = Coordinate:ToStringFromRP( ShortestReferencePoint, ShortestReferenceName, Controllable )
end
else
RouteText = Coordinate:ToString( Controllable )
end
end
return RouteText
end
function ACT_ROUTE:MenuCancel()
self:Cancel()
end
--- Task Events
@@ -177,6 +272,118 @@ 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 RouteText = self:GetRouteText( ProcessUnit )
self:Message( RouteText )
end
end -- ACT_ROUTE_POINT
do -- ACT_ROUTE_ZONE
@@ -184,7 +391,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 +400,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 +416,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 +424,36 @@ 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.
-- @param #number Altitude
-- @param #number Heading
function ACT_ROUTE_ZONE:SetZone( Zone, Altitude, Heading ) -- R2.2 Added altitude and heading
self.Zone = Zone
self.Altitude = Altitude
self.Heading = Heading
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 +464,10 @@ 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 )
self:E( { ProcessUnit = ProcessUnit } )
local ZoneVec2 = self.TargetZone:GetVec2()
local ZonePointVec2 = POINT_VEC2: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 RouteText = self:GetRouteText( ProcessUnit )
self:Message( RouteText )
end

View File

@@ -1,34 +1,88 @@
--- 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.
-- ### Author: **Sven Van de Velde (FlightControl)**
-- ### Contributions:
--
-- Note: Normally you would not use the BASE class unless you are extending the MOOSE framework with new classes.
-- ====
--
-- @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 +105,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 +119,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 +169,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,115 +182,65 @@
-- 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 = {},
_ = {},
}
--- @field #BASE.__
BASE.__ = {}
--- 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 = {}
setmetatable( self, MetaTable )
self.__index = self
_ClassID = _ClassID + 1
self.ClassID = _ClassID
-- This is for "private" methods...
-- When a __ is passed to a method as "self", the __index will search for the method on the public method list too!
-- if rawget( self, "__" ) then
--setmetatable( self, { __index = self.__ } )
-- end
return self
end
function BASE:_Destructor()
--self:E("_Destructor")
--self:EventRemoveAll()
end
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)
proxyMeta.__gc = function ()
env.info("In __gc for " .. self:GetClassNameAndID() )
if self._Destructor then
self:_Destructor()
end
end
-- keep the userdata from newproxy reachable until the object
-- 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.
-- @param #BASE self
-- @param Child is the Child class that inherits.
@@ -241,25 +248,39 @@ end
-- @return #BASE Child
function BASE:Inherit( Child, Parent )
local Child = routines.utils.deepCopy( Child )
--local Parent = routines.utils.deepCopy( Parent )
--local Parent = Parent
if Child ~= nil then
setmetatable( Child, Parent )
Child.__index = Child
Child:_SetDestructor()
-- This is for "private" methods...
-- When a __ is passed to a method as "self", the __index will search for the method on the public method list of the same object too!
if rawget( Child, "__" ) then
setmetatable( Child, { __index = Child.__ } )
setmetatable( Child.__, { __index = Parent } )
else
setmetatable( Child, { __index = Parent } )
end
--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
function BASE:GetParent( Child )
local Parent = getmetatable( Child )
-- env.info('Inherited class of ' .. Child.ClassName .. ' is ' .. Parent.ClassName )
local Parent
if rawget( Child, "__" ) then
Parent = getmetatable( Child.__ ).__index
else
Parent = getmetatable( Child ).__index
end
return Parent
end
@@ -302,7 +323,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 +333,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
@@ -343,7 +364,7 @@ do -- Event Handling
-- @return #BASE
function BASE:UnHandleEvent( Event )
self:EventDispatcher():Remove( self, Event )
self:EventDispatcher():RemoveEvent( self, Event )
return self
end
@@ -395,6 +416,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
@@ -575,7 +602,6 @@ end
-- @param #BASE self
-- @param Object The object that holds the Value set by the Key.
-- @param Key The key that is used to retrieve the value. Note that the key can be a #string, but it can also be any other type!
-- @param Value The value to is stored in the Object.
-- @return The Value retrieved.
function BASE:GetState( Object, Key )
@@ -854,3 +880,35 @@ end
--- old stuff
--function BASE:_Destructor()
-- --self:E("_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)
--
-- proxyMeta.__gc = function ()
-- env.info("In __gc for " .. self:GetClassNameAndID() )
-- if self._Destructor then
-- self:_Destructor()
-- end
-- end
--
-- -- keep the userdata from newproxy reachable until the object
-- -- table is about to be garbage-collected - then the __gc hook
-- -- will be invoked and the destructor called
-- rawset( self, '__proxy', proxy )
--
--end

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
--- This module contains the DATABASE class, managing the database of mission objects.
--- **Core** -- DATABASE manages the database of mission objects.
--
-- ====
--
@@ -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.
--
@@ -33,8 +35,13 @@
--
-- ===
--
--
-- ### Author: **Sven Van de Velde (FlightControl)**
-- ### Contributions:
--
-- ====
-- @module Database
-- @author FlightControl
--- DATABASE class
-- @type DATABASE
@@ -44,17 +51,25 @@ DATABASE = {
Templates = {
Units = {},
Groups = {},
Statics = {},
ClientsByName = {},
ClientsByID = {},
},
UNITS = {},
UNITS_Index = {},
STATICS = {},
GROUPS = {},
PLAYERS = {},
PLAYERSJOINED = {},
PLAYERUNITS = {},
CLIENTS = {},
CARGOS = {},
AIRBASES = {},
COUNTRY_ID = {},
COUNTRY_NAME = {},
NavPoints = {},
PLAYERSETTINGS = {},
ZONENAMES = {},
}
local _DATABASECoalition =
@@ -82,13 +97,15 @@ 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 )
@@ -98,8 +115,41 @@ function DATABASE:New()
self:_RegisterGroupsAndUnits()
self:_RegisterClients()
self:_RegisterStatics()
self:_RegisterPlayers()
--self:_RegisterPlayers()
self:_RegisterAirbases()
self.UNITS_Position = 0
--- @param #DATABASE self
local function CheckPlayers( self )
local CoalitionsData = { AlivePlayersRed = coalition.getPlayers( coalition.side.RED ), AlivePlayersBlue = coalition.getPlayers( coalition.side.BLUE ) }
for CoalitionId, CoalitionData in pairs( CoalitionsData ) do
--self:E( { "CoalitionData:", CoalitionData } )
for UnitId, UnitData in pairs( CoalitionData ) do
if UnitData and UnitData:isExist() then
local UnitName = UnitData:getName()
local PlayerName = UnitData:getPlayerName()
local PlayerUnit = UNIT:Find( UnitData )
--self:T( { "UnitData:", UnitData, UnitName, PlayerName, PlayerUnit } )
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 )
local Settings = SETTINGS:Set( PlayerName )
Settings:SetPlayerMenu( PlayerUnit )
end
end
end
end
end
end
self:E( "Scheduling" )
PlayerCheckSchedule = SCHEDULER:New( nil, CheckPlayers, { self }, 1, 1 )
return self
end
@@ -122,6 +172,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 +184,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.
@@ -162,23 +214,6 @@ function DATABASE:FindStatic( StaticName )
return StaticFound
end
--- Adds a Airbase based on the Airbase Name in the DATABASE.
-- @param #DATABASE self
function DATABASE:AddAirbase( DCSAirbaseName )
if not self.AIRBASES[DCSAirbaseName] then
self.AIRBASES[DCSAirbaseName] = AIRBASE:Register( DCSAirbaseName )
end
end
--- Deletes a Airbase from the DATABASE based on the Airbase Name.
-- @param #DATABASE self
function DATABASE:DeleteAirbase( DCSAirbaseName )
--self.AIRBASES[DCSAirbaseName] = nil
end
--- Finds a AIRBASE based on the AirbaseName.
-- @param #DATABASE self
-- @param #string AirbaseName
@@ -189,6 +224,64 @@ function DATABASE:FindAirbase( AirbaseName )
return AirbaseFound
end
--- Adds a Airbase based on the Airbase Name in the DATABASE.
-- @param #DATABASE self
-- @param #string AirbaseName The name of the airbase
function DATABASE:AddAirbase( AirbaseName )
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
-- @param #string AirbaseName The name of the airbase
function DATABASE:DeleteAirbase( AirbaseName )
self.AIRBASES[AirbaseName] = nil
end
--- Finds an AIRBASE based on the AirbaseName.
-- @param #DATABASE self
-- @param #string AirbaseName
-- @return Wrapper.Airbase#AIRBASE The found AIRBASE.
function DATABASE:FindAirbase( AirbaseName )
local AirbaseFound = self.AIRBASES[AirbaseName]
return AirbaseFound
end
--- 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,18 +335,20 @@ 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.PLAYERUNITS[UnitName] = PlayerName
self.PLAYERSJOINED[PlayerName] = PlayerName
end
end
--- Deletes a player from the DATABASE based on the Player Name.
-- @param #DATABASE self
function DATABASE:DeletePlayer( PlayerName )
function DATABASE:DeletePlayer( UnitName, PlayerName )
if PlayerName then
self:E( { "Clean player:", PlayerName } )
self.PLAYERS[PlayerName] = nil
self.PLAYERUNITS[UnitName] = PlayerName
end
end
@@ -280,7 +375,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 )
@@ -290,7 +385,12 @@ function DATABASE:Spawn( SpawnTemplate )
SpawnTemplate.CountryID = SpawnCountryID
SpawnTemplate.CategoryID = SpawnCategoryID
-- Ensure that for the spawned group and its units, there are GROUP and UNIT objects created in the DATABASE.
local SpawnGroup = self:AddGroup( SpawnTemplate.name )
for UnitID, UnitData in pairs( SpawnTemplate.units ) do
self:AddUnit( UnitData.name )
end
return SpawnGroup
end
@@ -316,7 +416,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 +494,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,9 +690,15 @@ function DATABASE:_EventOnBirth( Event )
self:F2( { Event } )
if Event.IniDCSUnit then
self:AddUnit( Event.IniDCSUnitName )
self:AddGroup( Event.IniDCSGroupName )
self:_EventOnPlayerEnterUnit( Event )
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 +710,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 +732,15 @@ 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
local Settings = SETTINGS:Set( PlayerName )
Settings:SetPlayerMenu( Event.IniUnit )
end
end
end
@@ -588,9 +753,13 @@ 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
local Settings = SETTINGS:Set( PlayerName )
Settings:RemovePlayerMenu( Event.IniUnit )
self:DeletePlayer( Event.IniUnit, PlayerName )
end
end
end
end
@@ -644,9 +813,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,14 +838,15 @@ 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, ... )
function DATABASE:ForEachGroup( IteratorFunction, FinalizeFunction, ... )
self:F2( arg )
self:ForEach( IteratorFunction, arg, self.GROUPS )
self:ForEach( IteratorFunction, FinalizeFunction, arg, self.GROUPS )
return self
end
@@ -671,12 +854,12 @@ 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, ... )
function DATABASE:ForEachPlayer( IteratorFunction, FinalizeFunction, ... )
self:F2( arg )
self:ForEach( IteratorFunction, arg, self.PLAYERS )
self:ForEach( IteratorFunction, FinalizeFunction, arg, self.PLAYERS )
return self
end
@@ -684,19 +867,32 @@ 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, ... )
function DATABASE:ForEachPlayerJoined( IteratorFunction, FinalizeFunction, ... )
self:F2( arg )
self:ForEach( IteratorFunction, arg, self.PLAYERSJOINED )
self:ForEach( IteratorFunction, FinalizeFunction, arg, self.PLAYERSJOINED )
return self
end
--- Iterate the DATABASE and call an iterator function for each **ALIVE** player UNIT, providing the player UNIT 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 the player name.
-- @return #DATABASE self
function DATABASE:ForEachPlayerUnit( IteratorFunction, FinalizeFunction, ... )
self:F2( arg )
self:ForEach( IteratorFunction, FinalizeFunction, arg, self.PLAYERUNITS )
return self
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 +902,67 @@ 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
--- Gets the player settings
-- @param #DATABASE self
-- @param #string PlayerName
-- @return Core.Settings#SETTINGS
function DATABASE:GetPlayerSettings( PlayerName )
self:E({PlayerName})
return self.PLAYERSETTINGS[PlayerName]
end
--- Sets the player settings
-- @param #DATABASE self
-- @param #string PlayerName
-- @param Core.Settings#SETTINGS Settings
-- @return Core.Settings#SETTINGS
function DATABASE:SetPlayerSettings( PlayerName, Settings )
self:E({PlayerName, Settings})
self.PLAYERSETTINGS[PlayerName] = Settings
end
--- @param #DATABASE self
function DATABASE:_RegisterTemplates()
self:F2()
@@ -744,6 +1000,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 +1018,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
@@ -779,6 +1045,11 @@ function DATABASE:_RegisterTemplates()
end --if coa_name == 'red' or coa_name == 'blue' and type(coa_data) == 'table' then
end --for coa_name, coa_data in pairs(mission.coalition) do
for ZoneID, ZoneData in pairs( env.mission.triggers.zones ) do
local ZoneName = ZoneData.name
self.ZONENAMES[ZoneName] = ZoneName
end
return self
end

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,262 +48,288 @@
-- 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**
--
-- 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:
--
-- * 2016-12-18: Released.
--
-- ===
--
-- # **AUTHORS and CONTRIBUTIONS**
--
-- ### Author: **Sven Van de Velde (FlightControl)**
-- ### Contributions:
--
-- * [**Pikey**](https://forums.eagle.ru/member.php?u=62835): Review of documentation & advice for improvements.
--
-- ### Authors:
--
-- * [**FlightControl**](https://forums.eagle.ru/member.php?u=89536): Design & Programming & documentation.
-- ====
--
-- @module Fsm
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",
}
@@ -368,7 +396,7 @@ do -- FSM
Transition.Event = Event
Transition.To = To
self:T( Transition )
self:T2( Transition )
self._Transitions[Transition] = Transition
self:_eventmap( self.Events, Transition )
@@ -390,7 +418,7 @@ do -- FSM
-- @param #table ReturnEvents A table indicating for which returned events of the SubFSM which Event must be triggered in the FSM.
-- @return Core.Fsm#FSM_PROCESS The SubFSM.
function FSM:AddProcess( From, Event, Process, ReturnEvents )
self:T( { From, Event, Process, ReturnEvents } )
self:T( { From, Event } )
local Sub = {}
Sub.From = From
@@ -420,7 +448,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 +476,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 +494,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
@@ -506,14 +534,14 @@ do -- FSM
local __Event = "__" .. EventStructure.Event
self[Event] = self[Event] or self:_create_transition(Event)
self[__Event] = self[__Event] or self:_delayed_transition(Event)
self:T( "Added methods: " .. Event .. ", " .. __Event )
self:T2( "Added methods: " .. Event .. ", " .. __Event )
Events[Event] = self.Events[Event] or { map = {} }
self:_add_to_map( Events[Event].map, EventStructure )
end
function FSM:_submap( subs, sub, name )
self:F( { sub = sub, name = name } )
--self:F( { sub = sub, name = name } )
subs[sub.From] = subs[sub.From] or {}
subs[sub.From][sub.Event] = subs[sub.From][sub.Event] or {}
@@ -530,10 +558,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:T2( "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 +748,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,15 +778,73 @@ 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
-- @param Wrapper.Controllable#CONTROLLABLE FSMControllable
-- @return #FSM_CONTROLLABLE
function FSM_CONTROLLABLE:SetControllable( FSMControllable )
self:F( FSMControllable )
--self:F( FSMControllable:GetName() )
self.Controllable = FSMControllable
end
@@ -776,10 +880,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",
}
@@ -791,7 +904,7 @@ do -- FSM_PROCESS
local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- Core.Fsm#FSM_PROCESS
self:F( Controllable, Task )
--self:F( Controllable )
self:Assign( Controllable, Task )
@@ -801,12 +914,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 +960,7 @@ do -- FSM_PROCESS
-- Copy Processes
for ProcessID, Process in pairs( self:GetProcesses() ) do
self:T( { Process} )
--self:E( { Process:GetName() } )
local FsmProcess = NewFsm:AddProcess( Process.From, Process.Event, Process.fsm:Copy( Controllable, Task ), Process.ReturnEvents )
end
@@ -843,6 +978,26 @@ 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
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
@@ -907,7 +1062,7 @@ end
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @return #FSM_PROCESS self
function FSM_PROCESS:Assign( ProcessUnit, Task )
self:T( { Task, ProcessUnit } )
--self:T( { Task:GetName(), ProcessUnit:GetName() } )
self:SetControllable( ProcessUnit )
self:SetTask( Task )
@@ -928,12 +1083,7 @@ end
self.Task:Fail()
end
function FSM_PROCESS:onenterSuccess( ProcessUnit )
self:T( "Success" )
self.Task:Success()
end
--- StateMachine callback function for a FSM_PROCESS
-- @param #FSM_PROCESS self
@@ -941,14 +1091,14 @@ end
-- @param #string Event
-- @param #string From
-- @param #string To
function FSM_PROCESS:onstatechange( ProcessUnit, From, Event, To, Dummy )
self:T( { ProcessUnit, From, Event, To, Dummy, self:IsTrace() } )
function FSM_PROCESS:onstatechange( ProcessUnit, Task, From, Event, To, Dummy )
self:T( { ProcessUnit:GetName(), 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 +1117,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 +1162,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.
--
-- ===
--
@@ -26,110 +26,24 @@
-- * @{Menu#MENU_CLIENT_COMMAND}: Manages command menus for CLIENTs. This manages menus for units with the skill level "Client".
--
-- ===
---
-- ### Author: **Sven Van de Velde (FlightControl)**
-- ### Contributions:
--
-- The above menus classes **are derived** from 2 main **abstract** classes defined within the MOOSE framework (so don't use these):
--
-- 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.
--
-- 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
--
-- ====
--
-- @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 +52,8 @@ do -- MENU_BASE
}
--- Consructor
-- @param #MENU_BASE
-- @return #MENU_BASE
function MENU_BASE:New( MenuText, ParentMenu )
local MenuParentPath = {}
@@ -150,18 +66,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 +124,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 +143,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 +160,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 +187,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 +216,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 +273,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 +319,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 +358,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 +387,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 +408,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 +453,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 +511,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 +602,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 +695,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 +755,7 @@ do
-- end
-- end, {}, 10, 10 )
--
-- @field #MENU_GROUP
MENU_GROUP = {
ClassName = "MENU_GROUP"
}
@@ -767,9 +777,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 +790,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 +804,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 +872,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 +893,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,52 @@
--- 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}().
--
-- ====
--
-- ### Author: **Sven Van de Velde (FlightControl)**
-- ### Contributions:
--
-- ====
--
-- @field #MESSAGE
MESSAGE = {
ClassName = "MESSAGE",
MessageCategory = 0,
@@ -65,7 +87,7 @@ function MESSAGE:New( MessageText, MessageDuration, MessageCategory )
self.MessageDuration = MessageDuration or 5
self.MessageTime = timer.getTime()
self.MessageText = MessageText
self.MessageText = MessageText:gsub("^\n","",1):gsub("\n$","",1)
self.MessageSent = false
self.MessageGroup = false
@@ -229,80 +251,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 )
--

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,588 @@
--- **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 (default true)
-- @extends Core.Base#BASE
RADIO = {
ClassName = "RADIO",
FileName = "",
Frequency = 0,
Modulation = radio.modulation.AM,
Subtitle = "",
SubtitleDuration = 0,
Power = 100,
Loop = true,
}
--- Create a new RADIO Object. This doesn't broadcast a transmission, though, use @{#RADIO.Broadcast} to actually broadcast
-- If you want to create a RADIO, you probably should use @{Positionable#POSITIONABLE.GetRadio}() instead
-- @param #RADIO self
-- @param Wrapper.Positionable#POSITIONABLE Positionable The @{Positionable} that will receive radio capabilities.
-- @return #RADIO Radio
-- @return #nil If Positionable is invalid
function RADIO:New(Positionable)
local self = BASE:Inherit( self, BASE:New() ) -- Core.Radio#RADIO
self.Loop = true -- default Loop to true (not sure the above RADIO definition actually is working)
self:F(Positionable)
if Positionable:GetPointVec2() then -- It's stupid, but the only way I found to make sure positionable is valid
self.Positionable = Positionable
return self
end
self:E({"The passed positionable is invalid, no RADIO created", Positionable})
return nil
end
--- Check validity of the filename passed and sets RADIO.FileName
-- @param #RADIO self
-- @param #string FileName File name of the sound file (i.e. "Noise.ogg")
-- @return #RADIO self
function RADIO:SetFileName(FileName)
self:F2(FileName)
if type(FileName) == "string" then
if FileName:find(".ogg") or FileName:find(".wav") then
if not FileName:find("l10n/DEFAULT/") then
FileName = "l10n/DEFAULT/" .. FileName
end
self.FileName = FileName
return self
end
end
self:E({"File name invalid. Maybe something wrong with the extension ?", self.FileName})
return self
end
--- Check validity of the frequency passed and sets RADIO.Frequency
-- @param #RADIO self
-- @param #number Frequency in MHz (Ranges allowed for radio transmissions in DCS : 30-88 / 108-152 / 225-400MHz)
-- @return #RADIO self
function RADIO:SetFrequency(Frequency)
self:F2(Frequency)
if type(Frequency) == "number" then
-- If frequency is in range
if (Frequency >= 30 and Frequency < 88) or (Frequency >= 108 and Frequency < 152) or (Frequency >= 225 and Frequency < 400) then
self.Frequency = Frequency * 1000000 -- Conversion in Hz
-- If the RADIO is attached to a UNIT or a GROUP, we need to send the DCS Command "SetFrequency" to change the UNIT or GROUP frequency
if self.Positionable.ClassName == "UNIT" or self.Positionable.ClassName == "GROUP" then
self.Positionable:SetCommand({
id = "SetFrequency",
params = {
frequency = self.Frequency,
modulation = self.Modulation,
}
})
end
return self
end
end
self:E({"Frequency is outside of DCS Frequency ranges (30-80, 108-152, 225-400). Frequency unchanged.", self.Frequency})
return self
end
--- Check validity of the frequency passed and sets RADIO.Modulation
-- @param #RADIO self
-- @param #number Modulation either radio.modulation.AM or radio.modulation.FM
-- @return #RADIO self
function RADIO:SetModulation(Modulation)
self:F2(Modulation)
if type(Modulation) == "number" then
if Modulation == radio.modulation.AM or Modulation == radio.modulation.FM then --TODO Maybe make this future proof if ED decides to add an other modulation ?
self.Modulation = Modulation
return self
end
end
self:E({"Modulation is invalid. Use DCS's enum radio.modulation. Modulation unchanged.", self.Modulation})
return self
end
--- Check validity of the power passed and sets RADIO.Power
-- @param #RADIO self
-- @param #number Power in W
-- @return #RADIO self
function RADIO:SetPower(Power)
self:F2(Power)
if type(Power) == "number" then
self.Power = math.floor(math.abs(Power)) --TODO Find what is the maximum power allowed by DCS and limit power to that
return self
end
self:E({"Power is invalid. Power unchanged.", self.Power})
return self
end
--- Check validity of the loop passed and sets RADIO.Loop
-- @param #RADIO self
-- @param #boolean Loop
-- @return #RADIO self
-- @usage
function RADIO:SetLoop(Loop)
self:F2(Loop)
if type(Loop) == "boolean" then
self.Loop = Loop
return self
end
self:E({"Loop is invalid. Loop unchanged.", self.Loop})
return self
end
--- Check validity of the subtitle and the subtitleDuration passed and sets RADIO.subtitle and RADIO.subtitleDuration
-- Both parameters are mandatory, since it wouldn't make much sense to change the Subtitle and not its duration
-- @param #RADIO self
-- @param #string Subtitle
-- @param #number SubtitleDuration in s
-- @return #RADIO self
-- @usage
-- -- create the broadcaster and attaches it a RADIO
-- local MyUnit = UNIT:FindByName("MyUnit")
-- local MyUnitRadio = MyUnit:GetRadio()
--
-- -- add a subtitle for the next transmission, which will be up for 10s
-- MyUnitRadio:SetSubtitle("My Subtitle, 10)
function RADIO:SetSubtitle(Subtitle, SubtitleDuration)
self:F2({Subtitle, SubtitleDuration})
if type(Subtitle) == "string" then
self.Subtitle = Subtitle
else
self.Subtitle = ""
self:E({"Subtitle is invalid. Subtitle reset.", self.Subtitle})
end
if type(SubtitleDuration) == "number" then
if math.floor(math.abs(SubtitleDuration)) == SubtitleDuration then
self.SubtitleDuration = SubtitleDuration
return self
end
end
self.SubtitleDuration = 0
self:E({"SubtitleDuration is invalid. SubtitleDuration reset.", self.SubtitleDuration})
end
--- Create a new transmission, that is to say, populate the RADIO with relevant data
-- In this function the data is especially relevant if the broadcaster is anything but a UNIT or a GROUP,
-- but it will work with a UNIT or a GROUP anyway.
-- Only the #RADIO and the Filename are mandatory
-- @param #RADIO self
-- @param #string FileName
-- @param #number Frequency in MHz
-- @param #number Modulation either radio.modulation.AM or radio.modulation.FM
-- @param #number Power in W
-- @return #RADIO self
function RADIO:NewGenericTransmission(FileName, Frequency, Modulation, Power, Loop)
self:F({FileName, Frequency, Modulation, Power})
self:SetFileName(FileName)
if Frequency then self:SetFrequency(Frequency) end
if Modulation then self:SetModulation(Modulation) end
if Power then self:SetPower(Power) end
if Loop then self:SetLoop(Loop) end
return self
end
--- Create a new transmission, that is to say, populate the RADIO with relevant data
-- In this function the data is especially relevant if the broadcaster is a UNIT or a GROUP,
-- but it will work for any @{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

@@ -1,4 +1,4 @@
--- This module defines the SCHEDULEDISPATCHER class, which is used by a central object called _SCHEDULEDISPATCHER.
--- **Core** -- SCHEDULEDISPATCHER dispatches the different schedules.
--
-- ===
--
@@ -27,8 +27,6 @@
--
-- ===
--
-- ===
--
-- ### Contributions: -
-- ### Authors: FlightControl : Design & Programming
--
@@ -64,7 +62,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" } )
if Scheduler.MasterObject then
self.ObjectSchedulers[self.CallID] = Scheduler
@@ -103,13 +101,13 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
Scheduler = self.PersistentSchedulers[CallID]
end
self:T3( { Scheduler = Scheduler } )
--self:T3( { Scheduler = Scheduler } )
if Scheduler then
local Schedule = self.Schedule[Scheduler][CallID]
self:T3( { Schedule = Schedule } )
--self:T3( { Schedule = Schedule } )
local ScheduleObject = Scheduler.SchedulerObject
--local ScheduleObjectName = Scheduler.SchedulerObject:GetNameAndClassID()
@@ -147,7 +145,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
( Randomize * Repeat / 2 )
) +
0.01
self:T3( { Repeat = CallID, CurrentTime, ScheduleTime, ScheduleArguments } )
--self:T3( { Repeat = CallID, CurrentTime, ScheduleTime, ScheduleArguments } )
return ScheduleTime -- returns the next time the function needs to be called.
else
self:Stop( Scheduler, CallID )
@@ -156,7 +154,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
self:Stop( Scheduler, CallID )
end
else
self:E( "Scheduled obscolete call for CallID: " .. CallID )
self:E( "Scheduled obsolete call for CallID: " .. CallID )
end
return nil
@@ -181,13 +179,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 +200,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,460 @@
--- **Core** -- **SETTINGS** classe defines the format settings management for measurement.
--
-- ![Banner Image](..\Presentations\SETTINGS\Dia1.JPG)
--
-- ====
--
-- # Demo Missions
--
-- ### [SETTINGS Demo Missions source code]()
--
-- ### [SETTINGS Demo Missions, only for beta testers]()
--
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
--
-- ====
--
-- # YouTube Channel
--
-- ### [SETTINGS YouTube Channel]()
--
-- ===
--
-- ### Author: **Sven Van de Velde (FlightControl)**
-- ### Contributions:
--
-- ====
--
-- @module Settings
--- @type SETTINGS
-- @field #number LL_Accuracy
-- @field #boolean LL_DMS
-- @field #number MGRS_Accuracy
-- @field #string A2GSystem
-- @field #string A2ASystem
-- @extends Core.Base#BASE
--- # SETTINGS class, extends @{Base#BASE}
--
-- @field #SETTINGS
SETTINGS = {
ClassName = "SETTINGS",
}
do -- SETTINGS
--- SETTINGS constructor.
-- @param #SETTINGS self
-- @return #SETTINGS
function SETTINGS:Set( PlayerName )
if PlayerName == nil then
local self = BASE:Inherit( self, BASE:New() ) -- #SETTINGS
self:SetMetric() -- Defaults
self:SetA2G_BR() -- Defaults
self:SetA2A_BRAA() -- Defaults
self:SetLL_Accuracy( 2 ) -- Defaults
self:SetLL_DMS( true ) -- Defaults
self:SetMGRS_Accuracy( 5 ) -- Defaults
return self
else
local Settings = _DATABASE:GetPlayerSettings( PlayerName )
if not Settings then
Settings = BASE:Inherit( self, BASE:New() ) -- #SETTINGS
_DATABASE:SetPlayerSettings( PlayerName, Settings )
end
return Settings
end
end
--- Sets the SETTINGS metric.
-- @param #SETTINGS self
function SETTINGS:SetMetric()
self.Metric = true
end
--- Gets if the SETTINGS is metric.
-- @param #SETTINGS self
-- @return #boolean true if metric.
function SETTINGS:IsMetric()
self:E( {Metric = ( self.Metric ~= nil and self.Metric == true ) or ( self.Metric == nil and _SETTINGS:IsMetric() ) } )
return ( self.Metric ~= nil and self.Metric == true ) or ( self.Metric == nil and _SETTINGS:IsMetric() )
end
--- Sets the SETTINGS imperial.
-- @param #SETTINGS self
function SETTINGS:SetImperial()
self.Metric = false
end
--- Gets if the SETTINGS is imperial.
-- @param #SETTINGS self
-- @return #boolean true if imperial.
function SETTINGS:IsImperial()
self:E( {Metric = ( self.Metric ~= nil and self.Metric == false ) or ( self.Metric == nil and _SETTINGS:IsMetric() ) } )
return ( self.Metric ~= nil and self.Metric == false ) or ( self.Metric == nil and _SETTINGS:IsMetric() )
end
--- Sets the SETTINGS LL accuracy.
-- @param #SETTINGS self
-- @param #number LL_Accuracy
-- @return #SETTINGS
function SETTINGS:SetLL_Accuracy( LL_Accuracy )
self.LL_Accuracy = LL_Accuracy
end
--- Gets the SETTINGS LL accuracy.
-- @param #SETTINGS self
-- @return #number
function SETTINGS:GetLL_Accuracy()
return self.LL_Accuracy or _SETTINGS:GetLL_Accuracy()
end
--- Sets the SETTINGS LL DMS.
-- @param #SETTINGS self
-- @param #number LL_DMS
-- @return #SETTINGS
function SETTINGS:SetLL_DMS( LL_DMS )
self.LL_DMS = LL_DMS
end
--- Gets the SETTINGS LL DMS.
-- @param #SETTINGS self
-- @return #number
function SETTINGS:GetLL_DMS()
return self.LL_DMS or _SETTINGS:GetLL_DMS()
end
--- Sets the SETTINGS MGRS accuracy.
-- @param #SETTINGS self
-- @param #number MGRS_Accuracy
-- @return #SETTINGS
function SETTINGS:SetMGRS_Accuracy( MGRS_Accuracy )
self.MGRS_Accuracy = MGRS_Accuracy
end
--- Gets the SETTINGS MGRS accuracy.
-- @param #SETTINGS self
-- @return #number
function SETTINGS:GetMGRS_Accuracy()
return self.MGRS_Accuracy or _SETTINGS:GetMGRS_Accuracy()
end
--- Sets A2G LL
-- @param #SETTINGS self
-- @return #SETTINGS
function SETTINGS:SetA2G_LL()
self.A2GSystem = "LL"
end
--- Is LL
-- @param #SETTINGS self
-- @return #boolean true if LL
function SETTINGS:IsA2G_LL()
return ( self.A2GSystem and self.A2GSystem == "LL" ) or ( not self.A2GSystem and _SETTINGS:IsA2G_LL() )
end
--- Sets A2G MGRS
-- @param #SETTINGS self
-- @return #SETTINGS
function SETTINGS:SetA2G_MGRS()
self.A2GSystem = "MGRS"
end
--- Is MGRS
-- @param #SETTINGS self
-- @return #boolean true if MGRS
function SETTINGS:IsA2G_MGRS()
return ( self.A2GSystem and self.A2GSystem == "MGRS" ) or ( not self.A2GSystem and _SETTINGS:IsA2G_MGRS() )
end
--- Sets A2G BRA
-- @param #SETTINGS self
-- @return #SETTINGS
function SETTINGS:SetA2G_BR()
self.A2GSystem = "BR"
end
--- Is BRA
-- @param #SETTINGS self
-- @return #boolean true if BRA
function SETTINGS:IsA2G_BR()
self:E( { BRA = ( self.A2GSystem and self.A2GSystem == "BR" ) or ( not self.A2GSystem and _SETTINGS:IsA2G_BR() ) } )
return ( self.A2GSystem and self.A2GSystem == "BR" ) or ( not self.A2GSystem and _SETTINGS:IsA2G_BR() )
end
--- Sets A2A BRA
-- @param #SETTINGS self
-- @return #SETTINGS
function SETTINGS:SetA2A_BRAA()
self.A2ASystem = "BRAA"
end
--- Is BRA
-- @param #SETTINGS self
-- @return #boolean true if BRA
function SETTINGS:IsA2A_BRAA()
self:E( { BRA = ( self.A2ASystem and self.A2ASystem == "BRAA" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_BRAA() ) } )
return ( self.A2ASystem and self.A2ASystem == "BRAA" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_BRAA() )
end
--- Sets A2A BULLS
-- @param #SETTINGS self
-- @return #SETTINGS
function SETTINGS:SetA2A_BULLS()
self.A2ASystem = "BULLS"
end
--- Is BULLS
-- @param #SETTINGS self
-- @return #boolean true if BULLS
function SETTINGS:IsA2A_BULLS()
return ( self.A2ASystem and self.A2ASystem == "BULLS" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_BULLS() )
end
--- @param #SETTINGS self
-- @return #SETTINGS
function SETTINGS:SetSystemMenu( MenuGroup, RootMenu )
local MenuText = "System Settings"
local MenuTime = timer.getTime()
local SettingsMenu = MENU_GROUP:New( MenuGroup, MenuText, RootMenu ):SetTime( MenuTime )
local A2GCoordinateMenu = MENU_GROUP:New( MenuGroup, "A2G Coordinate System", SettingsMenu ):SetTime( MenuTime )
if self:IsA2G_LL() then
MENU_GROUP_COMMAND:New( MenuGroup, "Bearing, Range (BR)", A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "BR" ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "Military Grid (MGRS)", A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "MGRS" ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "Lattitude Longitude (LL) Accuracy 1", A2GCoordinateMenu, self.MenuLL_Accuracy, self, MenuGroup, RootMenu, 1 ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "Lattitude Longitude (LL) Accuracy 2", A2GCoordinateMenu, self.MenuLL_Accuracy, self, MenuGroup, RootMenu, 2 ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "Lattitude Longitude (LL) Accuracy 3", A2GCoordinateMenu, self.MenuLL_Accuracy, self, MenuGroup, RootMenu, 3 ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "Lattitude Longitude (LL) Decimal On", A2GCoordinateMenu, self.MenuLL_DMS, self, MenuGroup, RootMenu, true ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "Lattitude Longitude (LL) Decimal Off", A2GCoordinateMenu, self.MenuLL_DMS, self, MenuGroup, RootMenu, false ):SetTime( MenuTime )
end
if self:IsA2G_MGRS() then
MENU_GROUP_COMMAND:New( MenuGroup, "Bearing, Range (BR)", A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "BR" ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "Lattitude Longitude (LL)", A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "LL" ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "Military Grid (MGRS) Accuracy 1", A2GCoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 1 ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "Military Grid (MGRS) Accuracy 2", A2GCoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 2 ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "Military Grid (MGRS) Accuracy 3", A2GCoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 3 ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "Military Grid (MGRS) Accuracy 4", A2GCoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 4 ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "Military Grid (MGRS) Accuracy 5", A2GCoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 5 ):SetTime( MenuTime )
end
if self:IsA2G_BR() then
MENU_GROUP_COMMAND:New( MenuGroup, "Military Grid (MGRS)", A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "MGRS" ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "Lattitude Longitude (LL)", A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "LL" ):SetTime( MenuTime )
end
local A2ACoordinateMenu = MENU_GROUP:New( MenuGroup, "A2A Coordinate System", SettingsMenu ):SetTime( MenuTime )
if self:IsA2A_BULLS() then
MENU_GROUP_COMMAND:New( MenuGroup, "Bearing Range Altitude Aspect (BRAA)", A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "BRAA" ):SetTime( MenuTime )
end
if self:IsA2A_BRAA() then
MENU_GROUP_COMMAND:New( MenuGroup, "Bullseye (BULLS)", A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "BULLS" ):SetTime( MenuTime )
end
local MetricsMenu = MENU_GROUP:New( MenuGroup, "Measures and Weights System", SettingsMenu ):SetTime( MenuTime )
if self:IsMetric() then
MENU_GROUP_COMMAND:New( MenuGroup, "Imperial (Miles,Feet)", MetricsMenu, self.MenuMWSystem, self, MenuGroup, RootMenu, false ):SetTime( MenuTime )
end
if self:IsImperial() then
MENU_GROUP_COMMAND:New( MenuGroup, "Metric (Kilometers,Meters)", MetricsMenu, self.MenuMWSystem, self, MenuGroup, RootMenu, true ):SetTime( MenuTime )
end
SettingsMenu:Remove( MenuTime )
return self
end
--- @param #SETTINGS self
-- @param RootMenu
-- @param Wrapper.Client#CLIENT PlayerUnit
-- @param #string MenuText
-- @return #SETTINGS
function SETTINGS:SetPlayerMenu( PlayerUnit )
local PlayerGroup = PlayerUnit:GetGroup()
local PlayerName = PlayerUnit:GetPlayerName()
local PlayerNames = PlayerGroup:GetPlayerNames()
local PlayerMenu = MENU_GROUP:New( PlayerGroup, 'Settings "' .. PlayerName .. '"' )
self.PlayerMenu = PlayerMenu
local A2GCoordinateMenu = MENU_GROUP:New( PlayerGroup, "A2G Coordinate System", PlayerMenu )
if self:IsA2G_LL() then
MENU_GROUP_COMMAND:New( PlayerGroup, "Bearing, Range (BR)", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "BR" )
MENU_GROUP_COMMAND:New( PlayerGroup, "Military Grid (MGRS)", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "MGRS" )
MENU_GROUP_COMMAND:New( PlayerGroup, "Lattitude Longitude (LL) Accuracy 1", A2GCoordinateMenu, self.MenuGroupLL_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 1 )
MENU_GROUP_COMMAND:New( PlayerGroup, "Lattitude Longitude (LL) Accuracy 2", A2GCoordinateMenu, self.MenuGroupLL_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 2 )
MENU_GROUP_COMMAND:New( PlayerGroup, "Lattitude Longitude (LL) Accuracy 3", A2GCoordinateMenu, self.MenuGroupLL_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 3 )
MENU_GROUP_COMMAND:New( PlayerGroup, "Lattitude Longitude (LL) Decimal On", A2GCoordinateMenu, self.MenuGroupLL_DMSSystem, self, PlayerUnit, PlayerGroup, PlayerName, true )
MENU_GROUP_COMMAND:New( PlayerGroup, "Lattitude Longitude (LL) Decimal Off", A2GCoordinateMenu, self.MenuGroupLL_DMSSystem, self, PlayerUnit, PlayerGroup, PlayerName, false )
end
if self:IsA2G_MGRS() then
MENU_GROUP_COMMAND:New( PlayerGroup, "Bearing Range (BR)", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "BR" )
MENU_GROUP_COMMAND:New( PlayerGroup, "Lattitude Longitude (LL)", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL" )
MENU_GROUP_COMMAND:New( PlayerGroup, "Military Grid (MGRS) Accuracy 1", A2GCoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 1 )
MENU_GROUP_COMMAND:New( PlayerGroup, "Military Grid (MGRS) Accuracy 2", A2GCoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 2 )
MENU_GROUP_COMMAND:New( PlayerGroup, "Military Grid (MGRS) Accuracy 3", A2GCoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 3 )
MENU_GROUP_COMMAND:New( PlayerGroup, "Military Grid (MGRS) Accuracy 4", A2GCoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 4 )
MENU_GROUP_COMMAND:New( PlayerGroup, "Military Grid (MGRS) Accuracy 5", A2GCoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 5 )
end
if self:IsA2G_BR() then
MENU_GROUP_COMMAND:New( PlayerGroup, "Military Grid (MGRS)", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "MGRS" )
MENU_GROUP_COMMAND:New( PlayerGroup, "Lattitude Longitude (LL)", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL" )
end
local A2ACoordinateMenu = MENU_GROUP:New( PlayerGroup, "A2A Coordinate System", PlayerMenu )
if self:IsA2A_BULLS() then
MENU_GROUP_COMMAND:New( PlayerGroup, "Bearing Range Altitude Aspect (BRAA)", A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "BRAA" )
end
if self:IsA2A_BRAA() then
MENU_GROUP_COMMAND:New( PlayerGroup, "Bullseye (BULLS)", A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "BULLS" )
end
local MetricsMenu = MENU_GROUP:New( PlayerGroup, "Measures and Weights System", PlayerMenu )
if self:IsMetric() then
MENU_GROUP_COMMAND:New( PlayerGroup, "Imperial (Miles,Feet)", MetricsMenu, self.MenuGroupMWSystem, self, PlayerUnit, PlayerGroup, PlayerName, false )
end
if self:IsImperial() then
MENU_GROUP_COMMAND:New( PlayerGroup, "Metric (Kilometers,Meters)", MetricsMenu, self.MenuGroupMWSystem, self, PlayerUnit, PlayerGroup, PlayerName, true )
end
return self
end
--- @param #SETTINGS self
-- @param RootMenu
-- @param Wrapper.Client#CLIENT PlayerUnit
-- @return #SETTINGS
function SETTINGS:RemovePlayerMenu( PlayerUnit )
if self.PlayerMenu then
self.PlayerMenu:Remove()
end
return self
end
--- @param #SETTINGS self
function SETTINGS:A2GMenuSystem( MenuGroup, RootMenu, A2GSystem )
self.A2GSystem = A2GSystem
MESSAGE:New( string.format("Settings: Default A2G coordinate system set to %s for all players!.", A2GSystem ), 5 ):ToAll()
self:SetSystemMenu( MenuGroup, RootMenu )
end
--- @param #SETTINGS self
function SETTINGS:A2AMenuSystem( MenuGroup, RootMenu, A2ASystem )
self.A2ASystem = A2ASystem
MESSAGE:New( string.format("Settings: Default A2A coordinate system set to %s for all players!.", A2ASystem ), 5 ):ToAll()
self:SetSystemMenu( MenuGroup, RootMenu )
end
--- @param #SETTINGS self
function SETTINGS:MenuLL_Accuracy( MenuGroup, RootMenu, LL_Accuracy )
self.LL_Accuracy = LL_Accuracy
MESSAGE:New( string.format("Settings: Default LL accuracy set to %s for all players!.", LL_Accuracy ), 5 ):ToAll()
self:SetSystemMenu( MenuGroup, RootMenu )
end
--- @param #SETTINGS self
function SETTINGS:MenuLL_DMS( MenuGroup, RootMenu, LL_DMS )
self.LL_DMS = LL_DMS
MESSAGE:New( string.format("Settings: Default LL format set to %s for all players!.", LL_DMS or "Decimal" or "HMS" ), 5 ):ToAll()
self:SetSystemMenu( MenuGroup, RootMenu )
end
--- @param #SETTINGS self
function SETTINGS:MenuMGRS_Accuracy( MenuGroup, RootMenu, MGRS_Accuracy )
self.MGRS_Accuracy = MGRS_Accuracy
MESSAGE:New( string.format("Settings: Default MGRS accuracy set to %s for all players!.", MGRS_Accuracy ), 5 ):ToAll()
self:SetSystemMenu( MenuGroup, RootMenu )
end
--- @param #SETTINGS self
function SETTINGS:MenuMWSystem( MenuGroup, RootMenu, MW )
self.Metric = MW
MESSAGE:New( string.format("Settings: Default measurement format set to %s for all players!.", MW and "Metric" or "Imperial" ), 5 ):ToAll()
self:SetSystemMenu( MenuGroup, RootMenu )
end
do
--- @param #SETTINGS self
function SETTINGS:MenuGroupA2GSystem( PlayerUnit, PlayerGroup, PlayerName, A2GSystem )
BASE:E( {self, PlayerUnit:GetName(), A2GSystem} )
self.A2GSystem = A2GSystem
MESSAGE:New( string.format("Settings: A2G format set to %s for player %s.", A2GSystem, PlayerName ), 5 ):ToGroup( PlayerGroup )
self:RemovePlayerMenu(PlayerUnit)
self:SetPlayerMenu(PlayerUnit)
end
--- @param #SETTINGS self
function SETTINGS:MenuGroupA2ASystem( PlayerUnit, PlayerGroup, PlayerName, A2ASystem )
self.A2ASystem = A2ASystem
MESSAGE:New( string.format("Settings: A2A format set to %s for player %s.", A2ASystem, PlayerName ), 5 ):ToGroup( PlayerGroup )
self:RemovePlayerMenu(PlayerUnit)
self:SetPlayerMenu(PlayerUnit)
end
--- @param #SETTINGS self
function SETTINGS:MenuGroupLL_AccuracySystem( PlayerUnit, PlayerGroup, PlayerName, LL_Accuracy )
self.LL_Accuracy = LL_Accuracy
MESSAGE:New( string.format("Settings: A2G LL format accuracy set to %d for player %s.", LL_Accuracy, PlayerName ), 5 ):ToGroup( PlayerGroup )
self:RemovePlayerMenu(PlayerUnit)
self:SetPlayerMenu(PlayerUnit)
end
--- @param #SETTINGS self
function SETTINGS:MenuGroupLL_DMSSystem( PlayerUnit, PlayerGroup, PlayerName, LL_DMS )
self.LL_DMS = LL_DMS
MESSAGE:New( string.format("Settings: A2G LL format mode set to %s for player %s.", LL_DMS and "Decimal" or "HMS", PlayerName ), 5 ):ToGroup( PlayerGroup )
self:RemovePlayerMenu(PlayerUnit)
self:SetPlayerMenu(PlayerUnit)
end
--- @param #SETTINGS self
function SETTINGS:MenuGroupMGRS_AccuracySystem( PlayerUnit, PlayerGroup, PlayerName, MGRS_Accuracy )
self.MGRS_Accuracy = MGRS_Accuracy
MESSAGE:New( string.format("Settings: A2G MGRS format accuracy set to %d for player %s.", MGRS_Accuracy, PlayerName ), 5 ):ToGroup( PlayerGroup )
self:RemovePlayerMenu(PlayerUnit)
self:SetPlayerMenu(PlayerUnit)
end
--- @param #SETTINGS self
function SETTINGS:MenuGroupMWSystem( PlayerUnit, PlayerGroup, PlayerName, MW )
self.Metric = MW
MESSAGE:New( string.format("Settings: Measurement format set to %s for player %s.", MW and "Metric" or "Imperial", PlayerName ), 5 ):ToGroup( PlayerGroup )
self:RemovePlayerMenu(PlayerUnit)
self:SetPlayerMenu(PlayerUnit)
end
end
end

View File

@@ -0,0 +1,163 @@
--- **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]()
--
-- ====
--
-- ### Author: **Sven Van de Velde (FlightControl)**
-- ### Contributions:
--
-- ====
--
-- @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,291 @@
--- **Core** -- 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]()
--
-- ====
--
-- ### Author: **Sven Van de Velde (FlightControl)**
-- ### 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
--
-- ====
--
-- @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,169 +20,70 @@
--
-- 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.
--
-- ====
--
-- ===
-- ### Author: **Sven Van de Velde (FlightControl)**
-- ### Contributions:
--
-- # 1) @{Zone#ZONE_BASE} class, extends @{Base#BASE}
-- ====
--
-- @module Zone
--- @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.
--
-- ## 1.1) Each zone has a name:
-- ## 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}:
-- ## 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.
-- * @{#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.
--
-- ## 1.3) A zone has a probability factor that can be set to randomize a selection between zones:
-- ## 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.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.
--
-- ## 1.4) A zone manages Vectors:
-- ## 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.
-- * @{#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.
--
-- ## 1.5) A zone has a bounding square:
-- ## 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:
-- ## 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**
-- ======================
--
-- 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-02-28: ZONE\_BASE:**IsVec2InZone()** replaces ZONE\_BASE:_IsPointVec2InZone()_.
-- 2017-02-28: ZONE\_BASE:**IsVec3InZone()** replaces ZONE\_BASE:_IsPointVec3InZone()_.
-- 2017-02-28: ZONE\_RADIUS:**IsVec2InZone()** replaces ZONE\_RADIUS:_IsPointVec2InZone()_.
-- 2017-02-28: ZONE\_RADIUS:**IsVec3InZone()** replaces ZONE\_RADIUS:_IsPointVec3InZone()_.
-- 2017-02-28: ZONE\_POLYGON:**IsVec2InZone()** replaces ZONE\_POLYGON:_IsPointVec2InZone()_.
-- 2017-02-28: ZONE\_POLYGON:**IsVec3InZone()** replaces ZONE\_POLYGON:_IsPointVec3InZone()_.
--
-- 2017-02-18: ZONE\_POLYGON_BASE:**GetRandomPointVec2()** added.
--
-- 2017-02-18: ZONE\_POLYGON_BASE:**GetRandomPointVec3()** added.
--
-- 2017-02-18: ZONE\_RADIUS:**GetRandomPointVec3( inner, outer )** added.
--
-- 2017-02-18: ZONE\_RADIUS:**GetRandomPointVec2( inner, outer )** added.
--
-- 2016-08-15: ZONE\_BASE:**GetName()** added.
--
-- 2016-08-15: ZONE\_BASE:**SetZoneProbability( ZoneProbability )** added.
--
-- 2016-08-15: ZONE\_BASE:**GetZoneProbability()** added.
--
-- 2016-08-15: ZONE\_BASE:**GetZoneMaybe()** added.
--
-- ===
--
-- @module Zone
-- @author FlightControl
--- The ZONE_BASE class
-- @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
-- @field #ZONE_BASE
ZONE_BASE = {
ClassName = "ZONE_BASE",
ZoneName = "",
@@ -215,20 +120,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 +143,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 +177,90 @@ 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
-- @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 = Height and Height or land.getHeight( self:GetVec2() ), 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 +275,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 +290,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 +344,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 +386,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 +395,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 +646,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 +683,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 +718,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 +728,7 @@ function ZONE_UNIT:GetVec2()
return self.LastVec2
end
self:T( { ZoneVec2 } )
self:T2( { ZoneVec2 } )
return nil
end
@@ -656,10 +773,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 +797,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 +809,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 +823,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 +836,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 +876,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 +970,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 +1006,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 +1082,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 +1100,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 +1125,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

@@ -1,4 +1,4 @@
--- This module contains the AIRBASEPOLICE classes.
--- **Functional** -- This module monitors airbases traffic.
--
-- ===
--

View File

@@ -1,141 +1,234 @@
--- The CLEANUP class keeps an area clean of crashing or colliding airplanes. It also prevents airplanes from firing within this area.
--- **Functional** -- The CLEANUP_AIRBASE class keeps an area clean of crashing or colliding airplanes. It also prevents airplanes from firing within this area.
--
-- ===
--
-- ### Author: **Sven Van de Velde (FlightControl)**
-- ### Contributions:
--
-- ====
--
-- @module CleanUp
-- @author Flightcontrol
--- The CLEANUP class.
-- @type CLEANUP
--- @type CLEANUP_AIRBASE.__ Methods which are not intended for mission designers, but which are used interally by the moose designer :-)
-- @field #map<#string,Wrapper.Airbase#AIRBASE> Airbases Map of Airbases.
-- @extends Core.Base#BASE
CLEANUP = {
ClassName = "CLEANUP",
ZoneNames = {},
TimeInterval = 300,
--- @type CLEANUP_AIRBASE
-- @extends #CLEANUP_AIRBASE.__
--- # CLEANUP_AIRBASE, extends @{Base#BASE}
--
-- ![Banner Image](..\Presentations\CLEANUP_AIRBASE\Dia1.JPG)
--
-- The CLEANUP_AIRBASE class keeps airbases clean, and tries to guarantee continuous airbase operations, even under combat.
-- Specific airbases need to be provided that need to be guarded. Each airbase registered, will be guarded within a zone of 8 km around the airbase.
-- Any unit that fires a missile, or shoots within the zone of an airbase, will be monitored by CLEANUP_AIRBASE.
-- Within the 8km zone, units cannot fire any missile, which prevents the airbase runway to receive missile or bomb hits.
-- Any airborne or ground unit that is on the runway below 30 meters (default value) will be automatically removed if it is damaged.
--
-- This is not a full 100% secure implementation. It is still possible that CLEANUP_AIRBASE cannot prevent (in-time) to keep the airbase clean.
-- The following situations may happen that will still stop the runway of an airbase:
--
-- * A damaged unit is not removed on time when above the runway, and crashes on the runway.
-- * A bomb or missile is still able to dropped on the runway.
-- * Units collide on the airbase, and could not be removed on time.
--
-- When a unit is within the airbase zone and needs to be monitored,
-- its status will be checked every 0.25 seconds! This is required to ensure that the airbase is kept clean.
-- But as a result, there is more CPU overload.
--
-- So as an advise, I suggest you use the CLEANUP_AIRBASE class with care:
--
-- * Only monitor airbases that really need to be monitored!
-- * Try not to monitor airbases that are likely to be invaded by enemy troops.
-- For these airbases, there is little use to keep them clean, as they will be invaded anyway...
--
-- By following the above guidelines, you can add airbase cleanup with acceptable CPU overhead.
--
-- ## 1. CLEANUP_AIRBASE Constructor
--
-- Creates the main object which is preventing the airbase to get polluted with debris on the runway, which halts the airbase.
--
-- -- Clean these Zones.
-- CleanUpAirports = CLEANUP_AIRBASE:New( { AIRBASE.Caucasus.Tbilisi, AIRBASE.Caucasus.Kutaisi )
--
-- -- or
-- CleanUpTbilisi = CLEANUP_AIRBASE:New( AIRBASE.Caucasus.Tbilisi )
-- CleanUpKutaisi = CLEANUP_AIRBASE:New( AIRBASE.Caucasus.Kutaisi )
--
-- ## 2. Add or Remove airbases
--
-- The method @{#CLEANUP_AIRBASE.AddAirbase}() to add an airbase to the cleanup validation process.
-- The method @{#CLEANUP_AIRBASE.RemoveAirbase}() removes an airbase from the cleanup validation process.
--
-- ## 3. Clean missiles and bombs within the airbase zone.
--
-- When missiles or bombs hit the runway, the airbase operations stop.
-- Use the method @{#CLEANUP_AIRBASE.SetCleanMissiles}() to control the cleaning of missiles, which will prevent airbases to stop.
-- Note that this method will not allow anymore airbases to be attacked, so there is a trade-off here to do.
--
-- @field #CLEANUP_AIRBASE
CLEANUP_AIRBASE = {
ClassName = "CLEANUP_AIRBASE",
TimeInterval = 0.2,
CleanUpList = {},
}
-- @field #CLEANUP_AIRBASE.__
CLEANUP_AIRBASE.__ = {}
--- @field #CLEANUP_AIRBASE.__.Airbases
CLEANUP_AIRBASE.__.Airbases = {}
--- Creates the main object which is handling the cleaning of the debris within the given Zone Names.
-- @param #CLEANUP self
-- @param #table ZoneNames Is a table of zone names where the debris should be cleaned. Also a single string can be passed with one zone name.
-- @param #number TimeInterval The interval in seconds when the clean activity takes place. The default is 300 seconds, thus every 5 minutes.
-- @return #CLEANUP
-- @param #CLEANUP_AIRBASE self
-- @param #list<#string> AirbaseNames Is a table of airbase names where the debris should be cleaned. Also a single string can be passed with one airbase name.
-- @return #CLEANUP_AIRBASE
-- @usage
-- -- Clean these Zones.
-- CleanUpAirports = CLEANUP:New( { 'CLEAN Tbilisi', 'CLEAN Kutaisi' }, 150 )
-- CleanUpAirports = CLEANUP_AIRBASE:New( { AIRBASE.Caucasus.Tbilisi, AIRBASE.Caucasus.Kutaisi )
-- 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() )
self:F( { ZoneNames, TimeInterval } )
-- CleanUpTbilisi = CLEANUP_AIRBASE:New( AIRBASE.Caucasus.Tbilisi )
-- CleanUpKutaisi = CLEANUP_AIRBASE:New( AIRBASE.Caucasus.Kutaisi )
function CLEANUP_AIRBASE:New( AirbaseNames )
local self = BASE:Inherit( self, BASE:New() ) -- #CLEANUP_AIRBASE
self:F( { AirbaseNames } )
if type( ZoneNames ) == 'table' then
self.ZoneNames = ZoneNames
if type( AirbaseNames ) == 'table' then
for AirbaseID, AirbaseName in pairs( AirbaseNames ) do
self:AddAirbase( AirbaseName )
end
else
self.ZoneNames = { ZoneNames }
end
if TimeInterval then
self.TimeInterval = TimeInterval
local AirbaseName = AirbaseNames
self:AddAirbase( AirbaseName )
end
_EVENTDISPATCHER:OnBirth( self._OnEventBirth, self )
self:HandleEvent( EVENTS.Birth, self.__.OnEventBirth )
self.CleanUpScheduler = SCHEDULER:New( self, self._CleanUpScheduler, {}, 1, TimeInterval )
self.__.CleanUpScheduler = SCHEDULER:New( self, self.__.CleanUpSchedule, {}, 1, self.TimeInterval )
self:HandleEvent( EVENTS.EngineShutdown , self.__.EventAddForCleanUp )
self:HandleEvent( EVENTS.EngineStartup, self.__.EventAddForCleanUp )
self:HandleEvent( EVENTS.Hit, self.__.EventAddForCleanUp )
self:HandleEvent( EVENTS.PilotDead, self.__.OnEventCrash )
self:HandleEvent( EVENTS.Dead, self.__.OnEventCrash )
self:HandleEvent( EVENTS.Crash, self.__.OnEventCrash )
return self
end
--- Destroys a group from the simulator, but checks first if it is still existing!
-- @param #CLEANUP self
-- @param Dcs.DCSWrapper.Group#Group GroupObject The object to be destroyed.
-- @param #string CleanUpGroupName The groupname...
function CLEANUP:_DestroyGroup( GroupObject, CleanUpGroupName )
self:F( { GroupObject, CleanUpGroupName } )
if GroupObject then -- and GroupObject:isExist() then
trigger.action.deactivateGroup(GroupObject)
self:T( { "GroupObject Destroyed", GroupObject } )
end
--- Adds an airbase to the airbase validation list.
-- @param #CLEANUP_AIRBASE self
-- @param #string AirbaseName
-- @return #CLEANUP_AIRBASE
function CLEANUP_AIRBASE:AddAirbase( AirbaseName )
self.__.Airbases[AirbaseName] = AIRBASE:FindByName( AirbaseName )
self:F({"Airbase:", AirbaseName, self.__.Airbases[AirbaseName]:GetDesc()})
return self
end
--- Destroys a @{DCSWrapper.Unit#Unit} from the simulator, but checks first if it is still existing!
-- @param #CLEANUP self
-- @param Dcs.DCSWrapper.Unit#Unit CleanUpUnit The object to be destroyed.
-- @param #string CleanUpUnitName The Unit name ...
function CLEANUP:_DestroyUnit( CleanUpUnit, CleanUpUnitName )
self:F( { CleanUpUnit, CleanUpUnitName } )
--- Removes an airbase from the airbase validation list.
-- @param #CLEANUP_AIRBASE self
-- @param #string AirbaseName
-- @return #CLEANUP_AIRBASE
function CLEANUP_AIRBASE:RemoveAirbase( AirbaseName )
self.__.Airbases[AirbaseName] = nil
return self
end
--- Enables or disables the cleaning of missiles within the airbase zones.
-- Airbase operations stop when a missile or bomb is dropped at a runway.
-- Note that when this method is used, the airbase operations won't stop if
-- the missile or bomb was cleaned within the airbase zone, which is 8km from the center of the airbase.
-- However, there is a trade-off to make. Attacks on airbases won't be possible anymore if this method is used.
-- Note, one can also use the method @{#CLEANUP_AIRBASE.RemoveAirbase}() to remove the airbase from the control process as a whole,
-- when an enemy unit is near. That is also an option...
-- @param #CLEANUP_AIRBASE self
-- @param #string CleanMissiles (Default=true) If true, missiles fired are immediately destroyed. If false missiles are not controlled.
-- @return #CLEANUP_AIRBASE
function CLEANUP_AIRBASE:SetCleanMissiles( CleanMissiles )
if CleanMissiles then
self:HandleEvent( EVENTS.Shot, self.__.OnEventShot )
else
self:UnHandleEvent( EVENTS.Shot )
end
end
function CLEANUP_AIRBASE.__:IsInAirbase( Vec2 )
local InAirbase = false
for AirbaseName, Airbase in pairs( self.__.Airbases ) do
local Airbase = Airbase -- Wrapper.Airbase#AIRBASE
if Airbase:GetZone():IsVec2InZone( Vec2 ) then
InAirbase = true
break;
end
end
return InAirbase
end
--- Destroys a @{Unit} from the simulator, but checks first if it is still existing!
-- @param #CLEANUP_AIRBASE self
-- @param Wrapper.Unit#UNIT CleanUpUnit The object to be destroyed.
function CLEANUP_AIRBASE.__:DestroyUnit( CleanUpUnit )
self:F( { CleanUpUnit } )
if CleanUpUnit then
local CleanUpGroup = Unit.getGroup(CleanUpUnit)
local CleanUpUnitName = CleanUpUnit:GetName()
local CleanUpGroup = CleanUpUnit:GetGroup()
-- TODO Client bug in 1.5.3
if CleanUpGroup and CleanUpGroup:isExist() then
local CleanUpGroupUnits = CleanUpGroup:getUnits()
if CleanUpGroup:IsAlive() then
local CleanUpGroupUnits = CleanUpGroup:GetUnits()
if #CleanUpGroupUnits == 1 then
local CleanUpGroupName = CleanUpGroup:getName()
--self:CreateEventCrash( timer.getTime(), CleanUpUnit )
CleanUpGroup:destroy()
self:T( { "Destroyed Group:", CleanUpGroupName } )
local CleanUpGroupName = CleanUpGroup:GetName()
CleanUpGroup:Destroy()
else
CleanUpUnit:destroy()
self:T( { "Destroyed Unit:", CleanUpUnitName } )
CleanUpUnit:Destroy()
end
self.CleanUpList[CleanUpUnitName] = nil -- Cleaning from the list
CleanUpUnit = nil
self.CleanUpList[CleanUpUnitName] = nil
end
end
end
-- TODO check Dcs.DCSTypes#Weapon
--- Destroys a missile from the simulator, but checks first if it is still existing!
-- @param #CLEANUP self
-- @param Dcs.DCSTypes#Weapon MissileObject
function CLEANUP:_DestroyMissile( MissileObject )
self:F( { MissileObject } )
--- Destroys a missile from the simulator, but checks first if it is still existing!
-- @param #CLEANUP_AIRBASE self
-- @param Dcs.DCSTypes#Weapon MissileObject
function CLEANUP_AIRBASE.__:DestroyMissile( MissileObject )
self:F( { MissileObject } )
if MissileObject and MissileObject:isExist() then
MissileObject:destroy()
self:T( "MissileObject Destroyed")
end
end
function CLEANUP:_OnEventBirth( Event )
self:F( { Event } )
--- @param #CLEANUP_AIRBASE self
-- @param Core.Event#EVENTDATA EventData
function CLEANUP_AIRBASE.__: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.IniUnit
self.CleanUpList[EventData.IniDCSUnitName].CleanUpGroup = EventData.IniGroup
self.CleanUpList[EventData.IniDCSUnitName].CleanUpGroupName = EventData.IniDCSGroupName
self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnitName = EventData.IniDCSUnitName
end
--- Detects if a crash event occurs.
-- Crashed units go into a CleanUpList for removal.
-- @param #CLEANUP self
-- @param Dcs.DCSTypes#Event event
function CLEANUP:_EventCrash( Event )
-- @param #CLEANUP_AIRBASE self
-- @param Core.Event#EVENTDATA Event
function CLEANUP_AIRBASE.__:OnEventCrash( Event )
self:F( { Event } )
--TODO: This stuff is not working due to a DCS bug. Burning units cannot be destroyed.
@@ -146,171 +239,164 @@ function CLEANUP:_EventCrash( Event )
-- self:T("after deactivateGroup")
-- event.initiator:destroy()
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
if Event.IniDCSUnitName and Event.IniCategory == Object.Category.UNIT then
self.CleanUpList[Event.IniDCSUnitName] = {}
self.CleanUpList[Event.IniDCSUnitName].CleanUpUnit = Event.IniUnit
self.CleanUpList[Event.IniDCSUnitName].CleanUpGroup = Event.IniGroup
self.CleanUpList[Event.IniDCSUnitName].CleanUpGroupName = Event.IniDCSGroupName
self.CleanUpList[Event.IniDCSUnitName].CleanUpUnitName = Event.IniDCSUnitName
end
end
--- Detects if a unit shoots a missile.
-- If this occurs within one of the zones, then the weapon used must be destroyed.
-- @param #CLEANUP self
-- @param Dcs.DCSTypes#Event event
function CLEANUP:_EventShot( Event )
-- If this occurs within one of the airbases, then the weapon used must be destroyed.
-- @param #CLEANUP_AIRBASE self
-- @param Core.Event#EVENTDATA Event
function CLEANUP_AIRBASE.__:OnEventShot( Event )
self:F( { Event } )
-- Test if the missile was fired within one of the CLEANUP.ZoneNames.
local CurrentLandingZoneID = 0
CurrentLandingZoneID = routines.IsUnitInZones( Event.IniDCSUnit, self.ZoneNames )
if ( CurrentLandingZoneID ) then
-- Okay, the missile was fired within the CLEANUP.ZoneNames, destroy the fired weapon.
--_SEADmissile:destroy()
SCHEDULER:New( self, CLEANUP._DestroyMissile, { Event.Weapon }, 0.1 )
-- Test if the missile was fired within one of the CLEANUP_AIRBASE.AirbaseNames.
if self:IsInAirbase( Event.IniUnit:GetVec2() ) then
-- Okay, the missile was fired within the CLEANUP_AIRBASE.AirbaseNames, destroy the fired weapon.
self:DestroyMissile( Event.Weapon )
end
end
--- Detects if the Unit has an S_EVENT_HIT within the given ZoneNames. If this is the case, destroy the unit.
-- @param #CLEANUP self
-- @param Dcs.DCSTypes#Event event
function CLEANUP:_EventHitCleanUp( Event )
--- Detects if the Unit has an S_EVENT_HIT within the given AirbaseNames. If this is the case, destroy the unit.
-- @param #CLEANUP_AIRBASE self
-- @param Core.Event#EVENTDATA Event
function CLEANUP_AIRBASE.__:OnEventHit( Event )
self:F( { Event } )
if Event.IniDCSUnit then
if routines.IsUnitInZones( Event.IniDCSUnit, self.ZoneNames ) ~= nil then
self:T( { "Life: ", Event.IniDCSUnitName, ' = ', Event.IniDCSUnit:getLife(), "/", Event.IniDCSUnit:getLife0() } )
if Event.IniDCSUnit:getLife() < Event.IniDCSUnit:getLife0() then
if Event.IniUnit then
if self:IsInAirbase( Event.IniUnit:GetVec2() ) then
self:T( { "Life: ", Event.IniDCSUnitName, ' = ', Event.IniUnit:GetLife(), "/", Event.IniUnit:GetLife0() } )
if Event.IniUnit:GetLife() < Event.IniUnit:GetLife0() then
self:T( "CleanUp: Destroy: " .. Event.IniDCSUnitName )
SCHEDULER:New( self, CLEANUP._DestroyUnit, { Event.IniDCSUnit }, 0.1 )
CLEANUP_AIRBASE.__:DestroyUnit( Event.IniUnit )
end
end
end
if Event.TgtDCSUnit then
if routines.IsUnitInZones( Event.TgtDCSUnit, self.ZoneNames ) ~= nil then
self:T( { "Life: ", Event.TgtDCSUnitName, ' = ', Event.TgtDCSUnit:getLife(), "/", Event.TgtDCSUnit:getLife0() } )
if Event.TgtDCSUnit:getLife() < Event.TgtDCSUnit:getLife0() then
if Event.TgtUnit then
if self:IsInAirbase( Event.TgtUnit:GetVec2() ) then
self:T( { "Life: ", Event.TgtDCSUnitName, ' = ', Event.TgtUnit:GetLife(), "/", Event.TgtUnit:GetLife0() } )
if Event.TgtUnit:GetLife() < Event.TgtUnit:GetLife0() then
self:T( "CleanUp: Destroy: " .. Event.TgtDCSUnitName )
SCHEDULER:New( self, CLEANUP._DestroyUnit, { Event.TgtDCSUnit }, 0.1 )
CLEANUP_AIRBASE.__:DestroyUnit( Event.TgtUnit )
end
end
end
end
--- Add the @{DCSWrapper.Unit#Unit} to the CleanUpList for CleanUp.
function CLEANUP:_AddForCleanUp( CleanUpUnit, CleanUpUnitName )
-- @param #CLEANUP_AIRBASE self
-- @param Wrapper.Unit#UNIT CleanUpUnit
-- @oaram #string CleanUpUnitName
function CLEANUP_AIRBASE.__:AddForCleanUp( CleanUpUnit, CleanUpUnitName )
self:F( { CleanUpUnit, CleanUpUnitName } )
self.CleanUpList[CleanUpUnitName] = {}
self.CleanUpList[CleanUpUnitName].CleanUpUnit = CleanUpUnit
self.CleanUpList[CleanUpUnitName].CleanUpUnitName = CleanUpUnitName
self.CleanUpList[CleanUpUnitName].CleanUpGroup = Unit.getGroup(CleanUpUnit)
self.CleanUpList[CleanUpUnitName].CleanUpGroupName = Unit.getGroup(CleanUpUnit):getName()
local CleanUpGroup = CleanUpUnit:GetGroup()
self.CleanUpList[CleanUpUnitName].CleanUpGroup = CleanUpGroup
self.CleanUpList[CleanUpUnitName].CleanUpGroupName = CleanUpGroup:GetName()
self.CleanUpList[CleanUpUnitName].CleanUpTime = timer.getTime()
self.CleanUpList[CleanUpUnitName].CleanUpMoved = false
self:T( { "CleanUp: Add to CleanUpList: ", Unit.getGroup(CleanUpUnit):getName(), CleanUpUnitName } )
self:T( { "CleanUp: Add to CleanUpList: ", CleanUpGroup:GetName(), CleanUpUnitName } )
end
--- Detects if the Unit has an S_EVENT_ENGINE_SHUTDOWN or an S_EVENT_HIT within the given ZoneNames. If this is the case, add the Group to the CLEANUP List.
-- @param #CLEANUP self
-- @param Dcs.DCSTypes#Event event
function CLEANUP:_EventAddForCleanUp( Event )
--- Detects if the Unit has an S_EVENT_ENGINE_SHUTDOWN or an S_EVENT_HIT within the given AirbaseNames. If this is the case, add the Group to the CLEANUP_AIRBASE List.
-- @param #CLEANUP_AIRBASE.__ self
-- @param Core.Event#EVENTDATA Event
function CLEANUP_AIRBASE.__:EventAddForCleanUp( Event )
if Event.IniDCSUnit then
self:F({Event})
if Event.IniDCSUnit and Event.IniCategory == Object.Category.UNIT then
if self.CleanUpList[Event.IniDCSUnitName] == nil then
if routines.IsUnitInZones( Event.IniDCSUnit, self.ZoneNames ) ~= nil then
self:_AddForCleanUp( Event.IniDCSUnit, Event.IniDCSUnitName )
if self:IsInAirbase( Event.IniUnit:GetVec2() ) then
self:AddForCleanUp( Event.IniUnit, Event.IniDCSUnitName )
end
end
end
if Event.TgtDCSUnit then
if Event.TgtDCSUnit and Event.TgtCategory == Object.Category.UNIT then
if self.CleanUpList[Event.TgtDCSUnitName] == nil then
if routines.IsUnitInZones( Event.TgtDCSUnit, self.ZoneNames ) ~= nil then
self:_AddForCleanUp( Event.TgtDCSUnit, Event.TgtDCSUnitName )
if self:IsInAirbase( Event.TgtUnit:GetVec2() ) then
self:AddForCleanUp( Event.TgtUnit, Event.TgtDCSUnitName )
end
end
end
end
local CleanUpSurfaceTypeText = {
"LAND",
"SHALLOW_WATER",
"WATER",
"ROAD",
"RUNWAY"
}
--- At the defined time interval, CleanUp the Groups within the CleanUpList.
-- @param #CLEANUP self
function CLEANUP:_CleanUpScheduler()
self:F( { "CleanUp Scheduler" } )
-- @param #CLEANUP_AIRBASE self
function CLEANUP_AIRBASE.__:CleanUpSchedule()
local CleanUpCount = 0
for CleanUpUnitName, UnitData in pairs( self.CleanUpList ) do
for CleanUpUnitName, CleanUpListData in pairs( self.CleanUpList ) do
CleanUpCount = CleanUpCount + 1
self:T( { CleanUpUnitName, UnitData } )
local CleanUpUnit = Unit.getByName(UnitData.CleanUpUnitName)
local CleanUpGroupName = UnitData.CleanUpGroupName
local CleanUpUnitName = UnitData.CleanUpUnitName
if CleanUpUnit then
self:T( { "CleanUp Scheduler", "Checking:", CleanUpUnitName } )
local CleanUpUnit = CleanUpListData.CleanUpUnit -- Wrapper.Unit#UNIT
local CleanUpGroupName = CleanUpListData.CleanUpGroupName
if CleanUpUnit:IsAlive() ~= nil then
if _DATABASE:GetStatusGroup( CleanUpGroupName ) ~= "ReSpawn" then
local CleanUpUnitVec3 = CleanUpUnit:getPoint()
--self:T( CleanUpUnitVec3 )
local CleanUpUnitVec2 = {}
CleanUpUnitVec2.x = CleanUpUnitVec3.x
CleanUpUnitVec2.y = CleanUpUnitVec3.z
--self:T( CleanUpUnitVec2 )
local CleanUpSurfaceType = land.getSurfaceType(CleanUpUnitVec2)
--self:T( CleanUpSurfaceType )
if CleanUpUnit and CleanUpUnit:getLife() <= CleanUpUnit:getLife0() * 0.95 then
if CleanUpSurfaceType == land.SurfaceType.RUNWAY then
if CleanUpUnit:inAir() then
local CleanUpLandHeight = land.getHeight(CleanUpUnitVec2)
local CleanUpUnitHeight = CleanUpUnitVec3.y - CleanUpLandHeight
self:T( { "CleanUp Scheduler", "Height = " .. CleanUpUnitHeight } )
if CleanUpUnitHeight < 30 then
local CleanUpCoordinate = CleanUpUnit:GetCoordinate()
self:T( { "CleanUp Scheduler", CleanUpUnitName } )
if CleanUpUnit:GetLife() <= CleanUpUnit:GetLife0() * 0.95 then
if CleanUpUnit:IsAboveRunway() then
if CleanUpUnit:InAir() then
local CleanUpLandHeight = CleanUpCoordinate:GetLandHeight()
local CleanUpUnitHeight = CleanUpCoordinate.y - CleanUpLandHeight
if CleanUpUnitHeight < 100 then
self:T( { "CleanUp Scheduler", "Destroy " .. CleanUpUnitName .. " because below safe height and damaged." } )
self:_DestroyUnit(CleanUpUnit, CleanUpUnitName)
self:DestroyUnit( CleanUpUnit )
end
else
self:T( { "CleanUp Scheduler", "Destroy " .. CleanUpUnitName .. " because on runway and damaged." } )
self:_DestroyUnit(CleanUpUnit, CleanUpUnitName)
self:DestroyUnit( CleanUpUnit )
end
end
end
-- Clean Units which are waiting for a very long time in the CleanUpZone.
if CleanUpUnit then
local CleanUpUnitVelocity = CleanUpUnit:getVelocity()
local CleanUpUnitVelocityTotal = math.abs(CleanUpUnitVelocity.x) + math.abs(CleanUpUnitVelocity.y) + math.abs(CleanUpUnitVelocity.z)
if CleanUpUnitVelocityTotal < 1 then
if UnitData.CleanUpMoved then
if UnitData.CleanUpTime + 180 <= timer.getTime() then
local CleanUpUnitVelocity = CleanUpUnit:GetVelocityKMH()
if CleanUpUnitVelocity < 1 then
if CleanUpListData.CleanUpMoved then
if CleanUpListData.CleanUpTime + 180 <= timer.getTime() then
self:T( { "CleanUp Scheduler", "Destroy due to not moving anymore " .. CleanUpUnitName } )
self:_DestroyUnit(CleanUpUnit, CleanUpUnitName)
self:DestroyUnit( CleanUpUnit )
end
end
else
UnitData.CleanUpTime = timer.getTime()
UnitData.CleanUpMoved = true
CleanUpListData.CleanUpTime = timer.getTime()
CleanUpListData.CleanUpMoved = true
end
end
else
-- Do nothing ...
self.CleanUpList[CleanUpUnitName] = nil -- Not anymore in the DCSRTE
self.CleanUpList[CleanUpUnitName] = nil
end
else
self:T( "CleanUp: Group " .. CleanUpUnitName .. " cannot be found in DCS RTE, removing ..." )
self.CleanUpList[CleanUpUnitName] = nil -- Not anymore in the DCSRTE
self.CleanUpList[CleanUpUnitName] = nil
end
end
self:T(CleanUpCount)

View File

@@ -0,0 +1,924 @@
--- **Functional** -- 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, AttackGroup )
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, AttackGroup )
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

@@ -1,4 +1,6 @@
--- Taking the lead of AI escorting your flight.
--- **Functional** -- Taking the lead of AI escorting your flight.
--
-- ====
--
-- @{#ESCORT} class
-- ================
@@ -84,7 +86,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 +130,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 +179,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 +204,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 +229,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 +310,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 +368,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 +488,8 @@ function ESCORT:MenuScanForTargets( Height, Seconds, MenuTextFormat )
MenuText,
self.EscortMenuScan,
ESCORT._ScanTargets,
{ ParamSelf = self,
ParamScanDuration = 30
}
self,
30
)
end
@@ -493,11 +519,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 +552,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 +582,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 +621,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 +650,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 +684,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 +724,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 +742,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 +751,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 +787,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 +805,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 +846,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 +1124,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 +1140,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, EscortGroupData )
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

@@ -1,4 +1,4 @@
--- This module contains the MISSILETRAINER class.
--- **Functional** -- MISSILETRAINER helps you to train missile avoidance.
--
-- ===
--
@@ -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 )
@@ -570,72 +570,76 @@ function MISSILETRAINER:_TrackMissiles()
for ClientDataID, ClientData in pairs( self.TrackingMissiles ) do
local Client = ClientData.Client
self:T2( { Client:GetName() } )
if Client and Client:IsAlive() then
for MissileDataID, MissileData in pairs( ClientData.MissileData ) do
self:T3( MissileDataID )
local TrainerSourceUnit = MissileData.TrainerSourceUnit
local TrainerWeapon = MissileData.TrainerWeapon
local TrainerTargetUnit = MissileData.TrainerTargetUnit
local TrainerWeaponTypeName = MissileData.TrainerWeaponTypeName
local TrainerWeaponLaunched = MissileData.TrainerWeaponLaunched
for MissileDataID, MissileData in pairs( ClientData.MissileData ) do
self:T3( MissileDataID )
if Client and Client:IsAlive() and TrainerSourceUnit and TrainerSourceUnit:IsAlive() and TrainerWeapon and TrainerWeapon:isExist() and TrainerTargetUnit and TrainerTargetUnit:IsAlive() then
local PositionMissile = TrainerWeapon:getPosition().p
local TargetVec3 = Client:GetVec3()
local Distance = ( ( PositionMissile.x - TargetVec3.x )^2 +
( PositionMissile.y - TargetVec3.y )^2 +
( PositionMissile.z - TargetVec3.z )^2
) ^ 0.5 / 1000
if Distance <= self.Distance then
-- Hit alert
TrainerWeapon:destroy()
if self.MessagesOnOff == true and self.AlertsHitsOnOff == true then
self:T( "killed" )
local Message = MESSAGE:New(
string.format( "%s launched by %s killed %s",
TrainerWeapon:getTypeName(),
TrainerSourceUnit:GetTypeName(),
TrainerTargetUnit:GetPlayerName()
), 15, "Hit Alert" )
if self.AlertsToAll == true then
Message:ToAll()
else
Message:ToClient( Client )
local TrainerSourceUnit = MissileData.TrainerSourceUnit
local TrainerWeapon = MissileData.TrainerWeapon
local TrainerTargetUnit = MissileData.TrainerTargetUnit
local TrainerWeaponTypeName = MissileData.TrainerWeaponTypeName
local TrainerWeaponLaunched = MissileData.TrainerWeaponLaunched
if Client and Client:IsAlive() and TrainerSourceUnit and TrainerSourceUnit:IsAlive() and TrainerWeapon and TrainerWeapon:isExist() and TrainerTargetUnit and TrainerTargetUnit:IsAlive() then
local PositionMissile = TrainerWeapon:getPosition().p
local TargetVec3 = Client:GetVec3()
local Distance = ( ( PositionMissile.x - TargetVec3.x )^2 +
( PositionMissile.y - TargetVec3.y )^2 +
( PositionMissile.z - TargetVec3.z )^2
) ^ 0.5 / 1000
if Distance <= self.Distance then
-- Hit alert
TrainerWeapon:destroy()
if self.MessagesOnOff == true and self.AlertsHitsOnOff == true then
self:T( "killed" )
local Message = MESSAGE:New(
string.format( "%s launched by %s killed %s",
TrainerWeapon:getTypeName(),
TrainerSourceUnit:GetTypeName(),
TrainerTargetUnit:GetPlayerName()
), 15, "Hit Alert" )
if self.AlertsToAll == true then
Message:ToAll()
else
Message:ToClient( Client )
end
MissileData = nil
table.remove( ClientData.MissileData, MissileDataID )
self:T(ClientData.MissileData)
end
end
else
if not ( TrainerWeapon and TrainerWeapon:isExist() ) then
if self.MessagesOnOff == true and self.AlertsLaunchesOnOff == true then
-- Weapon does not exist anymore. Delete from Table
local Message = MESSAGE:New(
string.format( "%s launched by %s self destructed!",
TrainerWeaponTypeName,
TrainerSourceUnit:GetTypeName()
), 5, "Tracking" )
if self.AlertsToAll == true then
Message:ToAll()
else
Message:ToClient( Client )
end
end
MissileData = nil
table.remove( ClientData.MissileData, MissileDataID )
self:T(ClientData.MissileData)
self:T( ClientData.MissileData )
end
end
else
if not ( TrainerWeapon and TrainerWeapon:isExist() ) then
if self.MessagesOnOff == true and self.AlertsLaunchesOnOff == true then
-- Weapon does not exist anymore. Delete from Table
local Message = MESSAGE:New(
string.format( "%s launched by %s self destructed!",
TrainerWeaponTypeName,
TrainerSourceUnit:GetTypeName()
), 5, "Tracking" )
if self.AlertsToAll == true then
Message:ToAll()
else
Message:ToClient( Client )
end
end
MissileData = nil
table.remove( ClientData.MissileData, MissileDataID )
self:T( ClientData.MissileData )
end
end
else
self.TrackingMissiles[ClientDataID] = nil
end
end
@@ -651,7 +655,7 @@ function MISSILETRAINER:_TrackMissiles()
for ClientDataID, ClientData in pairs( self.TrackingMissiles ) do
local Client = ClientData.Client
self:T2( { Client:GetName() } )
--self:T2( { Client:GetName() } )
ClientData.MessageToClient = ""
@@ -661,7 +665,7 @@ function MISSILETRAINER:_TrackMissiles()
for TrackingDataID, TrackingData in pairs( self.TrackingMissiles ) do
for MissileDataID, MissileData in pairs( TrackingData.MissileData ) do
self:T3( MissileDataID )
--self:T3( MissileDataID )
local TrainerSourceUnit = MissileData.TrainerSourceUnit
local TrainerWeapon = MissileData.TrainerWeapon

View File

@@ -1,12 +1,17 @@
--- Limit the simultaneous movement of Groups within a running Mission.
--- **Functional** -- Limit the MOVEMENT of simulaneous moving ground vehicles.
--
-- ====
--
-- Limit the simultaneous movement of Groups within a running Mission.
-- This module is defined to improve the performance in missions, and to bring additional realism for GROUND vehicles.
-- 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 +25,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 +38,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 +65,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

@@ -1,12 +1,10 @@
--- Single-Player:**Yes** / Multi-Player:**Yes** / Core:**Yes** -- **Administer the scoring of player achievements,
--- **Functional** -- **Administer the SCORING of player achievements,
-- and create a CSV file logging the scoring events for use at team or squadron websites.**
--
-- ![Banner Image](..\Presentations\SCORING\Dia1.JPG)
--
-- ===
--
-- # 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.
--
@@ -127,6 +125,32 @@
--
-- The above documents that 2 Scoring objects are created, ScoringFirstMission and ScoringSecondMission.
--
-- ### **IMPORTANT!!!*
-- In order to allow DCS world to write CSV files, you need to adapt a configuration file in your DCS world installation **on the server**.
-- For this, browse to the **missionscripting.lua** file in your DCS world installation folder.
-- For me, this installation folder is in _D:\\Program Files\\Eagle Dynamics\\DCS World\Scripts_.
--
-- Edit a few code lines in the MissionScripting.lua file. Comment out the lines **os**, **io** and **lfs**:
--
-- do
-- --sanitizeModule('os')
-- --sanitizeModule('io')
-- --sanitizeModule('lfs')
-- require = nil
-- loadlib = nil
-- end
--
-- When these lines are not sanitized, functions become available to check the time, and to write files to your system at the above specified location.
-- Note that the MissionScripting.lua file provides a warning. So please beware of this warning as outlined by Eagle Dynamics!
--
-- --Sanitize Mission Scripting environment
-- --This makes unavailable some unsecure functions.
-- --Mission downloaded from server to client may contain potentialy harmful lua code that may use these functions.
-- --You can remove the code below and make availble these functions at your own risk.
--
-- The MOOSE designer cannot take any responsibility of any damage inflicted as a result of the de-sanitization.
-- That being said, I hope that the SCORING class provides you with a great add-on to score your squad mates achievements.
--
-- ## 1.9) Configure messages.
--
-- When players hit or destroy targets, messages are sent.
@@ -249,6 +273,8 @@ function SCORING:New( GameName )
-- Default penalty when a player changes coalition.
self:SetCoalitionChangePenalty( self.ScaleDestroyPenalty )
self:SetDisplayMessagePrefix()
-- Event handlers
self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash )
self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash )
@@ -263,15 +289,23 @@ function SCORING:New( GameName )
end
--- Set a prefix string that will be displayed at each scoring message sent.
-- @param #SCORING self
-- @param #string DisplayMessagePrefix (Default="Scoring: ") The scoring prefix string.
-- @return #SCORING
function SCORING:SetDisplayMessagePrefix( DisplayMessagePrefix )
self.DisplayMessagePrefix = DisplayMessagePrefix or ""
return self
end
--- Set the scale for scoring valid destroys (enemy destroys).
-- A default calculated score is a value between 1 and 10.
-- The scale magnifies the scores given to the players.
-- @param #SCORING self
-- @param #number Scale The scale of the score given.
function SCORING:SetScaleDestroyScore( Scale )
self.ScaleDestroyScore = Scale
return self
end
@@ -546,6 +580,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 } )
@@ -573,11 +608,11 @@ function SCORING:_AddPlayerFromUnit( UnitData )
if self.Players[PlayerName].UnitCoalition ~= UnitCoalition then
self.Players[PlayerName].Penalty = self.Players[PlayerName].Penalty + 50
self.Players[PlayerName].PenaltyCoalition = self.Players[PlayerName].PenaltyCoalition + 1
MESSAGE:New( "Player '" .. PlayerName .. "' changed coalition from " .. _SCORINGCoalition[self.Players[PlayerName].UnitCoalition] .. " to " .. _SCORINGCoalition[UnitCoalition] ..
MESSAGE:New( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' changed coalition from " .. _SCORINGCoalition[self.Players[PlayerName].UnitCoalition] .. " to " .. _SCORINGCoalition[UnitCoalition] ..
"(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,10 +621,12 @@ 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
MESSAGE:New( "Player '" .. PlayerName .. "': WARNING! If you continue to commit FRATRICIDE and have a PENALTY score higher than " .. self.Fratricide .. ", you will be COURT MARTIALED and DISMISSED from this mission! \nYour total penalty is: " .. self.Players[PlayerName].Penalty,
MESSAGE:New( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "': WARNING! If you continue to commit FRATRICIDE and have a PENALTY score higher than " .. self.Fratricide .. ", you will be COURT MARTIALED and DISMISSED from this mission! \nYour total penalty is: " .. self.Players[PlayerName].Penalty,
30
):ToAll()
self.Players[PlayerName].PenaltyWarning = self.Players[PlayerName].PenaltyWarning + 1
@@ -597,10 +634,10 @@ function SCORING:_AddPlayerFromUnit( UnitData )
end
if self.Players[PlayerName].Penalty > self.Fratricide then
UnitData:Destroy()
MESSAGE:New( "Player '" .. PlayerName .. "' committed FRATRICIDE, he will be COURT MARTIALED and is DISMISSED from this mission!",
MESSAGE:New( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' committed FRATRICIDE, he will be COURT MARTIALED and is DISMISSED from this mission!",
10
):ToAll()
UnitData:GetGroup():Destroy()
end
end
@@ -631,9 +668,9 @@ function SCORING:AddGoalScore( PlayerUnit, GoalTag, Text, Score )
PlayerData.Goals[GoalTag].Score = PlayerData.Goals[GoalTag].Score + Score
PlayerData.Score = PlayerData.Score + Score
MESSAGE:New( Text, 30 ):ToAll()
MESSAGE:New( self.DisplayMessagePrefix .. 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
@@ -667,11 +704,9 @@ function SCORING:_AddMissionTaskScore( Mission, PlayerUnit, Text, Score )
PlayerData.Score = self.Players[PlayerName].Score + Score
PlayerData.Mission[MissionName].ScoreTask = self.Players[PlayerName].Mission[MissionName].ScoreTask + Score
MESSAGE:New( "Player '" .. PlayerName .. "' has " .. Text .. " in Mission '" .. MissionName .. "'. " ..
Score .. " task score!",
30 ):ToAll()
MESSAGE:New( self.DisplayMessagePrefix .. MissionName .. " : " .. Text .. " Score: " .. 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
@@ -697,11 +732,11 @@ function SCORING:_AddMissionScore( Mission, Text, Score )
PlayerData.Score = PlayerData.Score + Score
PlayerData.Mission[MissionName].ScoreMission = PlayerData.Mission[MissionName].ScoreMission + Score
MESSAGE:New( "Player '" .. PlayerName .. "' has " .. Text .. " in Mission '" .. MissionName .. "'. " ..
MESSAGE:New( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' has " .. Text .. " in Mission '" .. MissionName .. "'. " ..
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 +763,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 +880,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
@@ -865,7 +902,7 @@ function SCORING:_EventOnHit( Event )
if TargetPlayerName ~= nil then -- It is a player hitting another player ...
MESSAGE
:New( "Player '" .. InitPlayerName .. "' hit friendly player '" .. TargetPlayerName .. "' " ..
:New( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit friendly player '" .. TargetPlayerName .. "' " ..
TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " ..
"Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty,
2
@@ -874,7 +911,7 @@ function SCORING:_EventOnHit( Event )
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
else
MESSAGE
:New( "Player '" .. InitPlayerName .. "' hit a friendly target " ..
:New( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit friendly target " ..
TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " ..
"Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty,
2
@@ -882,14 +919,14 @@ 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
PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1
if TargetPlayerName ~= nil then -- It is a player hitting another player ...
MESSAGE
:New( "Player '" .. InitPlayerName .. "' hit enemy player '" .. TargetPlayerName .. "' " ..
:New( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit enemy player '" .. TargetPlayerName .. "' " ..
TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " ..
"Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty,
2
@@ -898,7 +935,7 @@ function SCORING:_EventOnHit( Event )
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
else
MESSAGE
:New( "Player '" .. InitPlayerName .. "' hit an enemy target " ..
:New( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit enemy target " ..
TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " ..
"Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty,
2
@@ -906,16 +943,16 @@ 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
:New( "Player '" .. InitPlayerName .. "' hit a scenery object.",
:New( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit scenery object.",
2
)
: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 +960,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( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit friendly target " ..
TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
"Penalty: -" .. PlayerHit.Penalty .. " = " .. 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( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit enemy target " ..
TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
"Score: +" .. PlayerHit.Score .. " = " .. 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( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit 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 +1099,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 +1119,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 } )
@@ -1005,28 +1131,31 @@ function SCORING:_EventOnDeadOrCrash( Event )
if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player
MESSAGE
:New( "Player '" .. PlayerName .. "' destroyed friendly player '" .. TargetPlayerName .. "' " ..
TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetDestroy.PenaltyDestroy .. " times. " ..
"Penalty: -" .. TargetDestroy.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty,
:New( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly player '" .. TargetPlayerName .. "' " ..
TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
"Penalty: -" .. TargetDestroy.Penalty .. " = " .. Player.Score - Player.Penalty,
15
)
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
else
MESSAGE
:New( "Player '" .. PlayerName .. "' destroyed a friendly target " ..
TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetDestroy.PenaltyDestroy .. " times. " ..
"Penalty: -" .. TargetDestroy.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty,
:New( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly target " ..
TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
"Penalty: -" .. TargetDestroy.Penalty .. " = " .. Player.Score - Player.Penalty,
15
)
: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 } )
@@ -1036,24 +1165,25 @@ function SCORING:_EventOnDeadOrCrash( Event )
TargetDestroy.ScoreDestroy = TargetDestroy.ScoreDestroy + 1
if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player
MESSAGE
:New( "Player '" .. PlayerName .. "' destroyed enemy player '" .. TargetPlayerName .. "' " ..
TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetDestroy.ScoreDestroy .. " times. " ..
"Score: " .. TargetDestroy.Score .. ". Score Total:" .. Player.Score - Player.Penalty,
:New( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy player '" .. TargetPlayerName .. "' " ..
TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
"Score: +" .. TargetDestroy.Score .. " = " .. Player.Score - Player.Penalty,
15
)
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
else
MESSAGE
:New( "Player '" .. PlayerName .. "' destroyed an enemy " ..
TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetDestroy.ScoreDestroy .. " times. " ..
"Score: " .. TargetDestroy.Score .. ". Total:" .. Player.Score - Player.Penalty,
:New( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy " ..
TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
"Score: +" .. TargetDestroy.Score .. " = " .. Player.Score - Player.Penalty,
15
)
: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]
@@ -1061,13 +1191,14 @@ function SCORING:_EventOnDeadOrCrash( Event )
Player.Score = Player.Score + Score
TargetDestroy.Score = TargetDestroy.Score + Score
MESSAGE
:New( "Special target '" .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. " destroyed! " ..
:New( self.DisplayMessagePrefix .. "Special target '" .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. " destroyed! " ..
"Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! Total: " .. Player.Score - Player.Penalty,
15
)
: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.
@@ -1079,13 +1210,14 @@ function SCORING:_EventOnDeadOrCrash( Event )
Player.Score = Player.Score + Score
TargetDestroy.Score = TargetDestroy.Score + Score
MESSAGE
:New( "Target destroyed in zone '" .. ScoreZone:GetName() .. "'." ..
:New( self.DisplayMessagePrefix .. "Target destroyed in zone '" .. ScoreZone:GetName() .. "'." ..
"Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! " ..
"Total: " .. Player.Score - Player.Penalty,
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
@@ -1100,17 +1232,25 @@ function SCORING:_EventOnDeadOrCrash( Event )
Player.Score = Player.Score + Score
TargetDestroy.Score = TargetDestroy.Score + Score
MESSAGE
:New( "Scenery destroyed in zone '" .. ScoreZone:GetName() .. "'." ..
:New( self.DisplayMessagePrefix .. "Scenery destroyed in zone '" .. ScoreZone:GetName() .. "'." ..
"Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! " ..
"Total: " .. Player.Score - Player.Penalty,
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, "", "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 +1669,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 +1685,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 +1698,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 +1746,7 @@ function SCORING:ScoreCSV( PlayerName, ScoreType, ScoreTimes, ScoreAmount, Playe
'"' .. self.RunTime .. '"' .. ',' ..
'' .. ScoreTime .. '' .. ',' ..
'"' .. PlayerName .. '"' .. ',' ..
'"' .. TargetPlayerName .. '"' .. ',' ..
'"' .. ScoreType .. '"' .. ',' ..
'"' .. PlayerUnitCoalition .. '"' .. ',' ..
'"' .. PlayerUnitCategory .. '"' .. ',' ..

View File

@@ -1,7 +1,8 @@
--- Provides defensive behaviour to a set of SAM sites within a running Mission.
--- **Functional** -- Provides defensive behaviour to a set of SAM sites within a running Mission.
--
-- ====
--
-- @module Sead
-- @author to be searched on the forum
-- @author (co) Flightcontrol (Modified and enriched with functionality)
--- The SEAD class
-- @type SEAD
@@ -36,25 +37,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()

File diff suppressed because it is too large Load Diff

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
@@ -71,7 +7,7 @@ _EVENTDISPATCHER = EVENT:New() -- Core.Event#EVENT
_SCHEDULEDISPATCHER = SCHEDULEDISPATCHER:New() -- Core.Timer#SCHEDULEDISPATCHER
--- Declare the main database object, which is used internally by the MOOSE classes.
_DATABASE = DATABASE:New() -- Database#DATABASE
_DATABASE = DATABASE:New() -- Core.Database#DATABASE
_SETTINGS = SETTINGS:Set()

View File

@@ -1,5 +1,14 @@
--- A COMMANDCENTER is the owner of multiple missions within MOOSE.
--- **Tasking** -- A COMMANDCENTER is the owner of multiple missions within MOOSE.
-- A COMMANDCENTER governs multiple missions, the tasking and the reporting.
--
-- ===
--
-- ### Author: **Sven Van de Velde (FlightControl)**
--
-- ### Contributions:
--
-- ====
--
-- @module CommandCenter
@@ -9,6 +18,7 @@
-- @extends Core.Base#BASE
REPORT = {
ClassName = "REPORT",
Title = "",
}
--- Create a new REPORT.
@@ -17,25 +27,65 @@ 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
self:SetIndent( 3 )
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
@@ -44,13 +94,64 @@ end
-- @field Dcs.DCSCoalitionWrapper.Object#coalition CommandCenterCoalition
-- @list<Tasking.Mission#MISSION> Missions
-- @extends Core.Base#BASE
--- # COMMANDCENTER class, extends @{Base#BASE}
--
-- The COMMANDCENTER class governs multiple missions, the tasking and the reporting.
--
-- The commandcenter communicates important messages between the various groups of human players executing tasks in missions.
--
-- ## COMMANDCENTER constructor
--
-- * @{#COMMANDCENTER.New}(): Creates a new COMMANDCENTER object.
--
-- ## Mission Management
--
-- * @{#COMMANDCENTER.AddMission}(): Adds a mission to the commandcenter control.
-- * @{#COMMANDCENTER.RemoveMission}(): Removes a mission to the commandcenter control.
-- * @{#COMMANDCENTER.GetMissions}(): Retrieves the missions table controlled by the commandcenter.
--
-- ## Reference Zones
--
-- Command Centers may be aware of certain Reference Zones within the battleground. These Reference Zones can refer to
-- known areas, recognizable buildings or sites, or any other point of interest.
-- Command Centers will use these Reference Zones to help pilots with defining coordinates in terms of navigation
-- during the WWII era.
-- The Reference Zones are related to the WWII mode that the Command Center will operate in.
-- Use the method @{#COMMANDCENTER.SetModeWWII}() to set the mode of communication to the WWII mode.
--
-- In WWII mode, the Command Center will receive detected targets, and will select for each target the closest
-- nearby Reference Zone. This allows pilots to navigate easier through the battle field readying for combat.
--
-- The Reference Zones need to be set by the Mission Designer in the Mission Editor.
-- Reference Zones are set by normal trigger zones. One can color the zones in a specific color,
-- and the radius of the zones doesn't matter, only the point is important. Place the center of these Reference Zones at
-- specific scenery objects or points of interest (like cities, rivers, hills, crossing etc).
-- The trigger zones indicating a Reference Zone need to follow a specific syntax.
-- The name of each trigger zone expressing a Reference Zone need to start with a classification name of the object,
-- followed by a #, followed by a symbolic name of the Reference Zone.
-- A few examples:
--
-- * A church at Tskinvali would be indicated as: *Church#Tskinvali*
-- * A train station near Kobuleti would be indicated as: *Station#Kobuleti*
--
-- The COMMANDCENTER class contains a method to indicate which trigger zones need to be used as Reference Zones.
-- This is done by using the method @{#COMMANDCENTER.SetReferenceZones}().
-- For the moment, only one Reference Zone class can be specified, but in the future, more classes will become possible.
--
-- @field #COMMANDCENTER
COMMANDCENTER = {
ClassName = "COMMANDCENTER",
CommandCenterName = "",
CommandCenterCoalition = nil,
CommandCenterPositionable = nil,
Name = "",
ReferencePoints = {},
ReferenceNames = {},
CommunicationMode = "80",
}
--- The constructor takes an IDENTIFIABLE as the HQ command center.
-- @param #COMMANDCENTER self
-- @param Wrapper.Positionable#POSITIONABLE CommandCenterPositionable
@@ -68,22 +169,24 @@ 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 )
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 )
end
self:SetMenu()
_DATABASE:PlayerSettingsMenu( PlayerUnit )
end
end
end
@@ -103,7 +206,21 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName )
local Mission = Mission -- Tasking.Mission#MISSION
local PlayerGroup = EventData.IniGroup -- The GROUP object should be filled!
Mission:JoinUnit( PlayerUnit, PlayerGroup )
Mission:ReportDetails()
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...
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
)
@@ -118,7 +235,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 +251,17 @@ 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()
_SETTINGS:SetSystemMenu( CommandCenterPositionable )
return self
end
@@ -186,6 +312,66 @@ function COMMANDCENTER:RemoveMission( Mission )
return Mission
end
--- Set special Reference Zones known by the Command Center to guide airborne pilots during WWII.
--
-- These Reference Zones are normal trigger zones, with a special naming.
-- The Reference Zones need to be set by the Mission Designer in the Mission Editor.
-- Reference Zones are set by normal trigger zones. One can color the zones in a specific color,
-- and the radius of the zones doesn't matter, only the center of the zone is important. Place the center of these Reference Zones at
-- specific scenery objects or points of interest (like cities, rivers, hills, crossing etc).
-- The trigger zones indicating a Reference Zone need to follow a specific syntax.
-- The name of each trigger zone expressing a Reference Zone need to start with a classification name of the object,
-- followed by a #, followed by a symbolic name of the Reference Zone.
-- A few examples:
--
-- * A church at Tskinvali would be indicated as: *Church#Tskinvali*
-- * A train station near Kobuleti would be indicated as: *Station#Kobuleti*
--
-- Taking the above example, this is how this method would be used:
--
-- CC:SetReferenceZones( "Church" )
-- CC:SetReferenceZones( "Station" )
--
--
-- @param #COMMANDCENTER self
-- @param #string ReferenceZonePrefix The name before the #-mark indicating the class of the Reference Zones.
-- @return #COMMANDCENTER
function COMMANDCENTER:SetReferenceZones( ReferenceZonePrefix )
local MatchPattern = "(.*)#(.*)"
self:F( { MatchPattern = MatchPattern } )
for ReferenceZoneName in pairs( _DATABASE.ZONENAMES ) do
local ZoneName, ReferenceName = string.match( ReferenceZoneName, MatchPattern )
self:F( { ZoneName = ZoneName, ReferenceName = ReferenceName } )
if ZoneName and ReferenceName and ZoneName == ReferenceZonePrefix then
self.ReferencePoints[ReferenceZoneName] = ZONE:New( ReferenceZoneName )
self.ReferenceNames[ReferenceZoneName] = ReferenceName
end
end
return self
end
--- Set the commandcenter operations in WWII mode
-- This will disable LL, MGRS, BRA, BULLS navigatin messages sent by the Command Center,
-- and will be replaced by a navigation using Reference Zones.
-- It will also disable the settings at the settings menu for these.
-- @param #COMMANDCENTER self
-- @return #COMMANDCENTER
function COMMANDCENTER:SetModeWWII()
self.CommunicationMode = "WWII"
return self
end
--- Returns if the commandcenter operations is in WWII mode
-- @param #COMMANDCENTER self
-- @return #boolean true if in WWII mode.
function COMMANDCENTER:IsModeWWII()
return self.CommunicationMode == "WWII"
end
--- Sets the menu structure of the Missions governed by the HQ command center.
-- @param #COMMANDCENTER self
function COMMANDCENTER:SetMenu()
@@ -193,17 +379,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 +418,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 +443,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 +498,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,91 @@ 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" )
--- Start Handler OnBefore for DETECTION_MANAGER
-- @function [parent=#DETECTION_MANAGER] OnBeforeStart
-- @param #DETECTION_MANAGER self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #boolean
--- Start Handler OnAfter for DETECTION_MANAGER
-- @function [parent=#DETECTION_MANAGER] OnAfterStart
-- @param #DETECTION_MANAGER self
-- @param #string From
-- @param #string Event
-- @param #string To
--- Start Trigger for DETECTION_MANAGER
-- @function [parent=#DETECTION_MANAGER] Start
-- @param #DETECTION_MANAGER self
--- Start Asynchronous Trigger for DETECTION_MANAGER
-- @function [parent=#DETECTION_MANAGER] __Start
-- @param #DETECTION_MANAGER self
-- @param #number Delay
self:AddTransition( "Started", "Stop", "Stopped" )
--- Stop Handler OnBefore for DETECTION_MANAGER
-- @function [parent=#DETECTION_MANAGER] OnBeforeStop
-- @param #DETECTION_MANAGER self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #boolean
--- Stop Handler OnAfter for DETECTION_MANAGER
-- @function [parent=#DETECTION_MANAGER] OnAfterStop
-- @param #DETECTION_MANAGER self
-- @param #string From
-- @param #string Event
-- @param #string To
--- Stop Trigger for DETECTION_MANAGER
-- @function [parent=#DETECTION_MANAGER] Stop
-- @param #DETECTION_MANAGER self
--- Stop Asynchronous Trigger for DETECTION_MANAGER
-- @function [parent=#DETECTION_MANAGER] __Stop
-- @param #DETECTION_MANAGER self
-- @param #number Delay
self:AddTransition( "Started", "Report", "Started" )
self:SetReportInterval( 30 )
self:SetReportDisplayTime( 25 )
self:E( { Detection = Detection } )
Detection:__Start( 3 )
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 +178,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 +270,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

@@ -0,0 +1,668 @@
--- **Tasking** - The TASK_A2A models tasks for players in Air to Air engagements.
--
-- ![Banner Image](..\Presentations\TASK_A2A\Dia1.JPG)
--
-- ====
--
-- ### Author: **Sven Van de Velde (FlightControl)**
--
-- ### Contributions:
--
-- ====
--
-- @module Task_A2A
do -- TASK_A2A
--- The TASK_A2A class
-- @type TASK_A2A
-- @field Set#SET_UNIT TargetSetUnit
-- @extends Tasking.Task#TASK
--- # TASK_A2A class, extends @{Task#TASK}
--
-- The TASK_A2A class defines Air To Air tasks for a @{Set} of Target Units,
-- based on the tasking capabilities defined in @{Task#TASK}.
-- The TASK_A2A is implemented using a @{Fsm#FSM_TASK}, and has the following statuses:
--
-- * **None**: Start of the process
-- * **Planned**: The A2A task is planned.
-- * **Assigned**: The A2A task is assigned to a @{Group#GROUP}.
-- * **Success**: The A2A task is successfully completed.
-- * **Failed**: The A2A 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 A2A attack.
--
-- Scoring or penalties can be given in the following circumstances:
--
-- * @{#TASK_A2A.SetScoreOnDestroy}(): Set a score when a target in scope of the A2A attack, has been destroyed.
-- * @{#TASK_A2A.SetScoreOnSuccess}(): Set a score when all the targets in scope of the A2A attack, have been destroyed.
-- * @{#TASK_A2A.SetPenaltyOnFailed}(): Set a penalty when the A2A attack has failed.
--
-- @field #TASK_A2A
TASK_A2A = {
ClassName = "TASK_A2A",
}
--- Instantiates a new TASK_A2A.
-- @param #TASK_A2A self
-- @param Tasking.Mission#MISSION Mission
-- @param Set#SET_GROUP SetAttack 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 #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_A2A self
function TASK_A2A:New( Mission, SetAttack, TaskName, TargetSetUnit, TaskType, TaskBriefing )
local self = BASE:Inherit( self, TASK:New( Mission, SetAttack, TaskName, TaskType, TaskBriefing ) ) -- Tasking.Task#TASK_A2A
self:F()
self.TargetSetUnit = TargetSetUnit
self.TaskType = TaskType
local Fsm = self:GetUnitProcess()
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 ), {} )
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_A2A#TASK_A2A 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_A2A 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_A2A 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_A2A#TASK_A2A 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_A2A#TASK_A2A 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
return self
end
--- @param #TASK_A2A self
function TASK_A2A:GetPlannedMenuText()
return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )"
end
--- @param #TASK_A2A 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_A2A: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_A2A 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_A2A: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_A2A 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_A2A: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_A2A self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return Core.Zone#ZONE_BASE The Zone object where the RendezVous is located on the map.
function TASK_A2A: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_A2A self
-- @param Core.Point#COORDINATE TargetCoordinate The Coordinate object where the Target is located on the map.
-- @param Wrapper.Unit#UNIT TaskUnit
function TASK_A2A: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_A2A self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return Core.Point#COORDINATE The Coordinate object where the Target is located on the map.
function TASK_A2A: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_A2A 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_A2A:SetTargetZone( TargetZone, Altitude, Heading, TaskUnit )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
ActRouteTarget:SetZone( TargetZone, Altitude, Heading )
end
--- @param #TASK_A2A self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return Core.Zone#ZONE_BASE The Zone object where the Target is located on the map.
function TASK_A2A:GetTargetZone( TaskUnit )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
local ActRouteTarget = ProcessUnit:GetProcess( "Engaging", "RouteToTargetZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
return ActRouteTarget:GetZone()
end
function TASK_A2A:SetGoalTotal()
self.GoalTotal = self.TargetSetUnit:Count()
end
function TASK_A2A:GetGoalTotal()
return self.GoalTotal
end
end
do -- TASK_A2A_INTERCEPT
--- The TASK_A2A_INTERCEPT class
-- @type TASK_A2A_INTERCEPT
-- @field Set#SET_UNIT TargetSetUnit
-- @extends Tasking.Task#TASK
--- # TASK_A2A_INTERCEPT class, extends @{Task_A2A#TASK_A2A}
--
-- The TASK_A2A_INTERCEPT class defines an intercept task for a human player to be executed.
-- When enemy planes need to be intercepted by human players, use this task type to urgen the players to get out there!
--
-- The TASK_A2A_INTERCEPT is used by the @{Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create intercept tasks
-- based on detected airborne enemy targets intruding friendly airspace.
--
-- The task is defined for a @{Mission#MISSION}, where a friendly @{Set#SET_GROUP} consisting of GROUPs with one human players each, is intercepting the targets.
-- The task is given a name and a briefing, that is used in the menu structure and in the reporting.
--
-- @field #TASK_A2A_INTERCEPT
TASK_A2A_INTERCEPT = {
ClassName = "TASK_A2A_INTERCEPT",
}
--- Instantiates a new TASK_A2A_INTERCEPT.
-- @param #TASK_A2A_INTERCEPT 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_A2A_INTERCEPT
function TASK_A2A_INTERCEPT:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing )
local self = BASE:Inherit( self, TASK_A2A:New( Mission, SetGroup, TaskName, TargetSetUnit, "INTERCEPT", TaskBriefing ) ) -- #TASK_A2A_INTERCEPT
self:F()
Mission:AddTask( self )
--TODO: Add BR, Altitude, type of planes...
self:SetBriefing(
TaskBriefing or
"Intercept incoming intruders.\n"
)
local TargetCoordinate = TargetSetUnit:GetFirst():GetCoordinate()
self:SetInfo( "Coordinates", TargetCoordinate, 10 )
self:SetInfo( "Threat", "[" .. string.rep( "", TargetSetUnit:CalculateThreatLevelA2G() ) .. "]", 11 )
local DetectedItemsCount = TargetSetUnit:Count()
local DetectedItemsTypes = TargetSetUnit:GetTypeNames()
self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, DetectedItemsTypes ), 0 )
return self
end
--- @param #TASK_A2A_INTERCEPT self
-- @param Wrapper.Group#GROUP ReportGroup
function TASK_A2A_INTERCEPT:ReportOrder( ReportGroup )
self:F( { TaskInfo = self.TaskInfo } )
local Coordinate = self.TaskInfo.Coordinates.TaskInfoText
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
return Distance
end
--- @param #TASK_A2A_INTERCEPT self
function TASK_A2A_INTERCEPT:onafterGoal( TaskUnit, From, Event, To )
local TargetSetUnit = self.TargetSetUnit -- Core.Set#SET_UNIT
if TargetSetUnit:Count() == 0 then
self:Success()
end
self:__Goal( -10 )
end
--- Set a score when a target in scope of the A2A attack, has been destroyed .
-- @param #TASK_A2A_INTERCEPT self
-- @param #string PlayerName The name of the player.
-- @param #number Score The score in points to be granted when task process has been achieved.
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return #TASK_A2A_INTERCEPT
function TASK_A2A_INTERCEPT:SetScoreOnProgress( PlayerName, Score, TaskUnit )
self:F( { PlayerName, Score, TaskUnit } )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has intercepted a target.", Score )
return self
end
--- Set a score when all the targets in scope of the A2A attack, have been destroyed.
-- @param #TASK_A2A_INTERCEPT self
-- @param #string PlayerName The name of the player.
-- @param #number Score The score in points.
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return #TASK_A2A_INTERCEPT
function TASK_A2A_INTERCEPT:SetScoreOnSuccess( PlayerName, Score, TaskUnit )
self:F( { PlayerName, Score, TaskUnit } )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScore( "Success", "All targets have been successfully intercepted!", Score )
return self
end
--- Set a penalty when the A2A attack has failed.
-- @param #TASK_A2A_INTERCEPT self
-- @param #string PlayerName The name of the player.
-- @param #number Penalty The penalty in points, must be a negative value!
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return #TASK_A2A_INTERCEPT
function TASK_A2A_INTERCEPT:SetScoreOnFail( PlayerName, Penalty, TaskUnit )
self:F( { PlayerName, Penalty, TaskUnit } )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScore( "Failed", "The intercept has failed!", Penalty )
return self
end
end
do -- TASK_A2A_SWEEP
--- The TASK_A2A_SWEEP class
-- @type TASK_A2A_SWEEP
-- @field Set#SET_UNIT TargetSetUnit
-- @extends Tasking.Task#TASK
--- # TASK_A2A_SWEEP class, extends @{Task_A2A#TASK_A2A}
--
-- The TASK_A2A_SWEEP class defines a sweep task for a human player to be executed.
-- A sweep task needs to be given when targets were detected but somehow the detection was lost.
-- Most likely, these enemy planes are hidden in the mountains or are flying under radar.
-- These enemy planes need to be sweeped by human players, and use this task type to urge the players to get out there and find those enemy fighters.
--
-- The TASK_A2A_SWEEP is used by the @{Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create sweep tasks
-- based on detected airborne enemy targets intruding friendly airspace, for which the detection has been lost for more than 60 seconds.
--
-- The task is defined for a @{Mission#MISSION}, where a friendly @{Set#SET_GROUP} consisting of GROUPs with one human players each, is sweeping the targets.
-- The task is given a name and a briefing, that is used in the menu structure and in the reporting.
--
-- @field #TASK_A2A_SWEEP
TASK_A2A_SWEEP = {
ClassName = "TASK_A2A_SWEEP",
}
--- Instantiates a new TASK_A2A_SWEEP.
-- @param #TASK_A2A_SWEEP 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_A2A_SWEEP self
function TASK_A2A_SWEEP:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing )
local self = BASE:Inherit( self, TASK_A2A:New( Mission, SetGroup, TaskName, TargetSetUnit, "SWEEP", TaskBriefing ) ) -- #TASK_A2A_SWEEP
self:F()
Mission:AddTask( self )
--TODO: Add BR, Altitude, type of planes...
self:SetBriefing(
TaskBriefing or
"Perform a fighter sweep. Incoming intruders were detected and could be hiding at the location.\n"
)
local TargetCoordinate = TargetSetUnit:GetFirst():GetCoordinate()
self:SetInfo( "Coordinates", TargetCoordinate, 10 )
self:SetInfo( "Assumed Threat", "[" .. string.rep( "", TargetSetUnit:CalculateThreatLevelA2G() ) .. "]", 11 )
local DetectedItemsCount = TargetSetUnit:Count()
local DetectedItemsTypes = TargetSetUnit:GetTypeNames()
self:SetInfo( "Lost Targets", string.format( "%d of %s", DetectedItemsCount, DetectedItemsTypes ), 0 )
return self
end
function TASK_A2A_SWEEP:ReportOrder( ReportGroup )
local Coordinate = self.TaskInfo.Coordinates.TaskInfoText
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
return Distance
end
--- @param #TASK_A2A_SWEEP self
function TASK_A2A_SWEEP:onafterGoal( TaskUnit, From, Event, To )
local TargetSetUnit = self.TargetSetUnit -- Core.Set#SET_UNIT
if TargetSetUnit:Count() == 0 then
self:Success()
end
self:__Goal( -10 )
end
--- Set a score when a target in scope of the A2A attack, has been destroyed .
-- @param #TASK_A2A_SWEEP self
-- @param #string PlayerName The name of the player.
-- @param #number Score The score in points to be granted when task process has been achieved.
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return #TASK_A2A_SWEEP
function TASK_A2A_SWEEP:SetScoreOnProgress( PlayerName, Score, TaskUnit )
self:F( { PlayerName, Score, TaskUnit } )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has sweeped a target.", Score )
return self
end
--- Set a score when all the targets in scope of the A2A attack, have been destroyed.
-- @param #TASK_A2A_SWEEP self
-- @param #string PlayerName The name of the player.
-- @param #number Score The score in points.
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return #TASK_A2A_SWEEP
function TASK_A2A_SWEEP:SetScoreOnSuccess( PlayerName, Score, TaskUnit )
self:F( { PlayerName, Score, TaskUnit } )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScore( "Success", "All targets have been successfully sweeped!", Score )
return self
end
--- Set a penalty when the A2A attack has failed.
-- @param #TASK_A2A_SWEEP self
-- @param #string PlayerName The name of the player.
-- @param #number Penalty The penalty in points, must be a negative value!
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return #TASK_A2A_SWEEP
function TASK_A2A_SWEEP:SetScoreOnFail( PlayerName, Penalty, TaskUnit )
self:F( { PlayerName, Penalty, TaskUnit } )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScore( "Failed", "The sweep has failed!", Penalty )
return self
end
end
do -- TASK_A2A_ENGAGE
--- The TASK_A2A_ENGAGE class
-- @type TASK_A2A_ENGAGE
-- @field Set#SET_UNIT TargetSetUnit
-- @extends Tasking.Task#TASK
--- # TASK_A2A_ENGAGE class, extends @{Task_A2A#TASK_A2A}
--
-- The TASK_A2A_ENGAGE class defines an engage task for a human player to be executed.
-- When enemy planes are close to human players, use this task type is used urge the players to get out there!
--
-- The TASK_A2A_ENGAGE is used by the @{Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create engage tasks
-- based on detected airborne enemy targets intruding friendly airspace.
--
-- The task is defined for a @{Mission#MISSION}, where a friendly @{Set#SET_GROUP} consisting of GROUPs with one human players each, is engaging the targets.
-- The task is given a name and a briefing, that is used in the menu structure and in the reporting.
--
-- @field #TASK_A2A_ENGAGE
TASK_A2A_ENGAGE = {
ClassName = "TASK_A2A_ENGAGE",
}
--- Instantiates a new TASK_A2A_ENGAGE.
-- @param #TASK_A2A_ENGAGE 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_A2A_ENGAGE self
function TASK_A2A_ENGAGE:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing )
local self = BASE:Inherit( self, TASK_A2A:New( Mission, SetGroup, TaskName, TargetSetUnit, "ENGAGE", TaskBriefing ) ) -- #TASK_A2A_ENGAGE
self:F()
Mission:AddTask( self )
--TODO: Add BR, Altitude, type of planes...
self:SetBriefing(
TaskBriefing or
"Bogeys are nearby! Players close by are ordered to ENGAGE the intruders!\n"
)
local TargetCoordinate = TargetSetUnit:GetFirst():GetCoordinate()
self:SetInfo( "Coordinates", TargetCoordinate, 10 )
self:SetInfo( "Threat", "[" .. string.rep( "", TargetSetUnit:CalculateThreatLevelA2G() ) .. "]", 11 )
local DetectedItemsCount = TargetSetUnit:Count()
local DetectedItemsTypes = TargetSetUnit:GetTypeNames()
self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, DetectedItemsTypes ), 0 )
return self
end
function TASK_A2A_ENGAGE:ReportOrder( ReportGroup )
local Coordinate = self.TaskInfo.Coordinates.TaskInfoText
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
return Distance
end
--- @param #TASK_A2A_ENGAGE self
function TASK_A2A_ENGAGE:onafterGoal( TaskUnit, From, Event, To )
local TargetSetUnit = self.TargetSetUnit -- Core.Set#SET_UNIT
if TargetSetUnit:Count() == 0 then
self:Success()
end
self:__Goal( -10 )
end
--- Set a score when a target in scope of the A2A attack, has been destroyed .
-- @param #TASK_A2A_ENGAGE self
-- @param #string PlayerName The name of the player.
-- @param #number Score The score in points to be granted when task process has been achieved.
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return #TASK_A2A_ENGAGE
function TASK_A2A_ENGAGE:SetScoreOnProgress( PlayerName, Score, TaskUnit )
self:F( { PlayerName, Score, TaskUnit } )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has engaged and destroyed a target.", Score )
return self
end
--- Set a score when all the targets in scope of the A2A attack, have been destroyed.
-- @param #TASK_A2A_ENGAGE self
-- @param #string PlayerName The name of the player.
-- @param #number Score The score in points.
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return #TASK_A2A_ENGAGE
function TASK_A2A_ENGAGE:SetScoreOnSuccess( PlayerName, Score, TaskUnit )
self:F( { PlayerName, Score, TaskUnit } )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScore( "Success", "All targets have been successfully engaged!", Score )
return self
end
--- Set a penalty when the A2A attack has failed.
-- @param #TASK_A2A_ENGAGE self
-- @param #string PlayerName The name of the player.
-- @param #number Penalty The penalty in points, must be a negative value!
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return #TASK_A2A_ENGAGE
function TASK_A2A_ENGAGE:SetScoreOnFail( PlayerName, Penalty, TaskUnit )
self:F( { PlayerName, Penalty, TaskUnit } )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScore( "Failed", "The target engagement has failed!", Penalty )
return self
end
end

View File

@@ -0,0 +1,593 @@
--- **Tasking** - The TASK_A2A_DISPATCHER creates and manages player TASK_A2A tasks based on detected targets.
--
-- The @{#TASK_A2A_DISPATCHER} classes implement the dynamic dispatching of tasks upon groups of detected units determined a @{Set} of EWR installation groups.
--
-- ====
--
-- ### Author: **Sven Van de Velde (FlightControl)**
--
-- ### Contributions:
--
-- ====
--
-- @module Task_A2A_Dispatcher
do -- TASK_A2A_DISPATCHER
--- TASK_A2A_DISPATCHER class.
-- @type TASK_A2A_DISPATCHER
-- @extends Tasking.DetectionManager#DETECTION_MANAGER
--- # TASK_A2A_DISPATCHER class, extends @{Tasking#DETECTION_MANAGER}
--
-- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia1.JPG)
--
-- The @{#TASK_A2A_DISPATCHER} class implements the dynamic dispatching of tasks upon groups of detected units determined a @{Set} of EWR installation groups.
--
-- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia3.JPG)
--
-- The EWR 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:
--
-- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia9.JPG)
--
-- * **INTERCEPT Task**: Is created when the target is known, is detected and within a danger zone, and there is no friendly airborne in range.
-- * **SWEEP Task**: Is created when the target is unknown, was detected and the last position is only known, and within a danger zone, and there is no friendly airborne in range.
-- * **ENGAGE Task**: Is created when the target is known, is detected and within a danger zone, and there is a friendly airborne in range, that will receive this task.
--
-- ## 1. TASK\_A2A\_DISPATCHER constructor:
--
-- The @{#TASK_A2A_DISPATCHER.New}() method creates a new TASK\_A2A\_DISPATCHER instance.
--
-- ### 1.1. Define or set the **Mission**:
--
-- Tasking is executed to accomplish missions. Therefore, a MISSION object needs to be given as the first parameter.
--
-- local HQ = GROUP:FindByName( "HQ", "Bravo" )
-- local CommandCenter = COMMANDCENTER:New( HQ, "Lima" )
-- local Mission = MISSION:New( CommandCenter, "A2A Mission", "High", "Watch the air enemy units being detected.", coalition.side.RED )
--
-- Missions are governed by COMMANDCENTERS, so, ensure you have a COMMANDCENTER object installed and setup within your mission.
-- Create the MISSION object, and hook it under the command center.
--
-- ### 1.2. Build a set of the groups seated by human players:
--
-- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia6.JPG)
--
-- A set or collection of the groups wherein human players can be seated, these can be clients or units that can be joined as a slot or jumping into.
--
-- local AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Defender" ):FilterStart()
--
-- The set is built using the SET_GROUP class. Apply any filter criteria to identify the correct groups for your mission.
-- Only these slots or units will be able to execute the mission and will receive tasks for this mission, once available.
--
-- ### 1.3. Define the **EWR network**:
--
-- As part of the TASK\_A2A\_DISPATCHER constructor, an EWR network must be given as the third parameter.
-- An EWR network, or, Early Warning Radar network, is used to early detect potential airborne targets and to understand the position of patrolling targets of the enemy.
--
-- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia5.JPG)
--
-- Typically EWR networks are setup using 55G6 EWR, 1L13 EWR, Hawk sr and Patriot str ground based radar units.
-- These radars have different ranges and 55G6 EWR and 1L13 EWR radars are Eastern Bloc units (eg Russia, Ukraine, Georgia) while the Hawk and Patriot radars are Western (eg US).
-- Additionally, ANY other radar capable unit can be part of the EWR network! Also AWACS airborne units, planes, helicopters can help to detect targets, as long as they have radar.
-- The position of these units is very important as they need to provide enough coverage
-- to pick up enemy aircraft as they approach so that CAP and GCI flights can be tasked to intercept them.
--
-- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia7.JPG)
--
-- Additionally in a hot war situation where the border is no longer respected the placement of radars has a big effect on how fast the war escalates.
-- For example if they are a long way forward and can detect enemy planes on the ground and taking off
-- they will start to vector CAP and GCI flights to attack them straight away which will immediately draw a response from the other coalition.
-- Having the radars further back will mean a slower escalation because fewer targets will be detected and
-- therefore less CAP and GCI flights will spawn and this will tend to make just the border area active rather than a melee over the whole map.
-- It all depends on what the desired effect is.
--
-- EWR networks are **dynamically constructed**, that is, they form part of the @{Functional#DETECTION_BASE} object that is given as the input parameter of the TASK\_A2A\_DISPATCHER class.
-- By defining in a **smart way the names or name prefixes of the groups** with EWR capable units, these groups will be **automatically added or deleted** from the EWR network,
-- increasing or decreasing the radar coverage of the Early Warning System.
--
-- See the following example to setup an EWR network containing EWR stations and AWACS.
--
-- local EWRSet = SET_GROUP:New():FilterPrefixes( "EWR" ):FilterCoalitions("red"):FilterStart()
--
-- local EWRDetection = DETECTION_AREAS:New( EWRSet, 6000 )
-- EWRDetection:SetFriendliesRange( 10000 )
-- EWRDetection:SetDetectionInterval(30)
--
-- -- Setup the A2A dispatcher, and initialize it.
-- A2ADispatcher = TASK_A2A_DISPATCHER:New( Mission, AttackGroups, EWRDetection )
--
-- The above example creates a SET_GROUP instance, and stores this in the variable (object) **EWRSet**.
-- **EWRSet** is then being configured to filter all active groups with a group name starting with **EWR** to be included in the Set.
-- **EWRSet** is then being ordered to start the dynamic filtering. Note that any destroy or new spawn of a group with the above names will be removed or added to the Set.
-- Then a new **EWRDetection** object is created from the class DETECTION_AREAS. A grouping radius of 6000 is choosen, which is 6km.
-- The **EWRDetection** object is then passed to the @{#TASK_A2A_DISPATCHER.New}() method to indicate the EWR network configuration and setup the A2A tasking and detection mechanism.
--
-- ### 2. Define the detected **target grouping radius**:
--
-- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia8.JPG)
--
-- The target grouping radius is a property of the Detection object, that was passed to the AI\_A2A\_DISPATCHER object, but can be changed.
-- The grouping radius should not be too small, but also depends on the types of planes and the era of the simulation.
-- Fast planes like in the 80s, need a larger radius than WWII planes.
-- Typically I suggest to use 30000 for new generation planes and 10000 for older era aircraft.
--
-- Note that detected targets are constantly re-grouped, that is, when certain detected aircraft are moving further than the group radius, then these aircraft will become a separate
-- group being detected. This may result in additional GCI being started by the dispatcher! So don't make this value too small!
--
-- ## 3. Set the **Engage radius**:
--
-- Define the radius to engage any target by airborne friendlies, which are executing cap or returning from an intercept mission.
--
-- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia11.JPG)
--
-- So, if there is a target area detected and reported,
-- then any friendlies that are airborne near this target area,
-- will be commanded to (re-)engage that target when available (if no other tasks were commanded).
-- For example, if 100000 is given as a value, then any friendly that is airborne within 100km from the detected target,
-- will be considered to receive the command to engage that target area.
-- You need to evaluate the value of this parameter carefully.
-- If too small, more intercept missions may be triggered upon detected target areas.
-- If too large, any airborne cap may not be able to reach the detected target area in time, because it is too far.
--
-- ## 4. Set **Scoring** and **Messages**:
--
-- The TASK\_A2A\_DISPATCHER is a state machine. It triggers the event Assign when a new player joins a @{Task} dispatched by the TASK\_A2A\_DISPATCHER.
-- An _event handler_ can be defined to catch the **Assign** event, and add **additional processing** to set _scoring_ and to _define messages_,
-- when the player reaches certain achievements in the task.
--
-- The prototype to handle the **Assign** event needs to be developed as follows:
--
-- TaskDispatcher = TASK_A2A_DISPATCHER:New( ... )
--
-- --- @param #TaskDispatcher self
-- -- @param #string From Contains the name of the state from where the Event was triggered.
-- -- @param #string Event Contains the name of the event that was triggered. In this case Assign.
-- -- @param #string To Contains the name of the state that will be transitioned to.
-- -- @param Tasking.Task_A2A#TASK_A2A Task The Task object, which is any derived object from TASK_A2A.
-- -- @param Wrapper.Unit#UNIT TaskUnit The Unit or Client that contains the Player.
-- -- @param #string PlayerName The name of the Player that joined the TaskUnit.
-- function TaskDispatcher:OnAfterAssign( From, Event, To, Task, TaskUnit, PlayerName )
-- Task:SetScoreOnProgress( PlayerName, 20, TaskUnit )
-- Task:SetScoreOnSuccess( PlayerName, 200, TaskUnit )
-- Task:SetScoreOnFail( PlayerName, -100, TaskUnit )
-- end
--
-- The **OnAfterAssign** method (function) is added to the TaskDispatcher object.
-- This method will be called when a new player joins a unit in the set of groups in scope of the dispatcher.
-- So, this method will be called only **ONCE** when a player joins a unit in scope of the task.
--
-- The TASK class implements various methods to additional **set scoring** for player achievements:
--
-- * @{Tasking.Task#TASK.SetScoreOnProgress}() will add additional scores when a player achieves **Progress** while executing the task.
-- Examples of **task progress** can be destroying units, arriving at zones etc.
--
-- * @{Tasking.Task#TASK.SetScoreOnSuccess}() will add additional scores when the task goes into **Success** state.
-- This means the **task has been successfully completed**.
--
-- * @{Tasking.Task#TASK.SetScoreOnSuccess}() will add additional (negative) scores when the task goes into **Failed** state.
-- This means the **task has not been successfully completed**, and the scores must be given with a negative value!
--
-- @field #TASK_A2A_DISPATCHER
TASK_A2A_DISPATCHER = {
ClassName = "TASK_A2A_DISPATCHER",
Mission = nil,
Detection = nil,
Tasks = {},
SweepZones = {},
}
--- TASK_A2A_DISPATCHER constructor.
-- @param #TASK_A2A_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_A2A_DISPATCHER self
function TASK_A2A_DISPATCHER:New( Mission, SetGroup, Detection )
-- Inherits from DETECTION_MANAGER
local self = BASE:Inherit( self, DETECTION_MANAGER:New( SetGroup, Detection ) ) -- #TASK_A2A_DISPATCHER
self.Detection = Detection
self.Mission = Mission
-- TODO: Check detection through radar.
self.Detection:FilterCategories( Unit.Category.AIRPLANE, Unit.Category.HELICOPTER )
self.Detection:InitDetectRadar( true )
self.Detection:SetDetectionInterval( 30 )
self:AddTransition( "Started", "Assign", "Started" )
--- OnAfter Transition Handler for Event Assign.
-- @function [parent=#TASK_A2A_DISPATCHER] OnAfterAssign
-- @param #TASK_A2A_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_A2A#TASK_A2A Task
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param #string PlayerName
self:__Start( 5 )
return self
end
--- Define the radius to when an ENGAGE task will be generated for any nearby by airborne friendlies, which are executing cap or returning from an intercept mission.
-- So, if there is a target area detected and reported,
-- then any friendlies that are airborne near this target area,
-- will be commanded to (re-)engage that target when available (if no other tasks were commanded).
-- An ENGAGE task will be created for those pilots.
-- For example, if 100000 is given as a value, then any friendly that is airborne within 100km from the detected target,
-- will be considered to receive the command to engage that target area.
-- You need to evaluate the value of this parameter carefully.
-- If too small, more intercept missions may be triggered upon detected target areas.
-- If too large, any airborne cap may not be able to reach the detected target area in time, because it is too far.
-- @param #TASK_A2A_DISPATCHER self
-- @param #number EngageRadius (Optional, Default = 100000) The radius to report friendlies near the target.
-- @return #TASK_A2A_DISPATCHER
-- @usage
--
-- -- Set 50km as the radius to engage any target by airborne friendlies.
-- TaskA2ADispatcher:SetEngageRadius( 50000 )
--
-- -- Set 100km as the radius to engage any target by airborne friendlies.
-- TaskA2ADispatcher:SetEngageRadius() -- 100000 is the default value.
--
function TASK_A2A_DISPATCHER:SetEngageRadius( EngageRadius )
self.Detection:SetFriendliesRange( EngageRadius or 100000 )
return self
end
--- Creates an INTERCEPT task when there are targets for it.
-- @param #TASK_A2A_DISPATCHER self
-- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem
-- @return Set#SET_UNIT TargetSetUnit: The target set of units.
-- @return #nil If there are no targets to be set.
function TASK_A2A_DISPATCHER:EvaluateINTERCEPT( DetectedItem )
self:F( { DetectedItem.ItemID } )
local DetectedSet = DetectedItem.Set
local DetectedZone = DetectedItem.Zone
-- Check if there is at least one UNIT in the DetectedSet is visible.
if DetectedItem.IsDetected == true then
-- Here we're doing something advanced... We're copying the DetectedSet.
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 an SWEEP task when there are targets for it.
-- @param #TASK_A2A_DISPATCHER self
-- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem
-- @return Set#SET_UNIT TargetSetUnit: The target set of units.
-- @return #nil If there are no targets to be set.
function TASK_A2A_DISPATCHER:EvaluateSWEEP( DetectedItem )
self:F( { DetectedItem.ItemID } )
local DetectedSet = DetectedItem.Set
local DetectedZone = DetectedItem.Zone
if DetectedItem.IsDetected == false then
-- Here we're doing something advanced... We're copying the DetectedSet.
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 an ENGAGE task when there are human friendlies airborne near the targets.
-- @param #TASK_A2A_DISPATCHER self
-- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem
-- @return Set#SET_UNIT TargetSetUnit: The target set of units.
-- @return #nil If there are no targets to be set.
function TASK_A2A_DISPATCHER:EvaluateENGAGE( DetectedItem )
self:F( { DetectedItem.ItemID } )
local DetectedSet = DetectedItem.Set
local DetectedZone = DetectedItem.Zone
local PlayersCount, PlayersReport = self:GetPlayerFriendliesNearBy( DetectedItem )
-- Only allow ENGAGE when there are Players near the zone, and when the Area has detected items since the last run in a 60 seconds time zone.
if PlayersCount > 0 and DetectedItem.IsDetected == true then
-- Here we're doing something advanced... We're copying the DetectedSet.
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_A2A_DISPATCHER self
-- @param Tasking.Mission#MISSION Mission
-- @param Tasking.Task#TASK Task
-- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Detection#DETECTION_BASE} derived object.
-- @param #boolean DetectedItemID
-- @param #boolean DetectedItemChange
-- @return Tasking.Task#TASK
function TASK_A2A_DISPATCHER:EvaluateRemoveTask( Mission, Task, Detection, DetectedItem, DetectedItemIndex, DetectedItemChanged )
if Task then
if Task:IsStatePlanned() then
local TaskName = Task:GetName()
local TaskType = TaskName:match( "(%u+)%.%d+" )
self:T2( { TaskType = TaskType } )
local Remove = false
local IsPlayers = Detection:IsPlayersNearBy( DetectedItem )
if TaskType == "ENGAGE" then
if IsPlayers == false then
Remove = true
end
end
if TaskType == "INTERCEPT" then
if IsPlayers == true then
Remove = true
end
if DetectedItem.IsDetected == false then
Remove = true
end
end
if TaskType == "SWEEP" then
if DetectedItem.IsDetected == true then
Remove = true
end
end
local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT
--DetectedSet:Flush()
--self:E( { DetectedSetCount = DetectedSet:Count() } )
if DetectedSet:Count() == 0 then
Remove = true
end
if DetectedItemChanged == true or Remove then
--self:E( "Removing Tasking: " .. Task:GetTaskName() )
Mission:RemoveTask( Task )
self.Tasks[DetectedItemIndex] = nil
end
end
end
return Task
end
--- Calculates which friendlies are nearby the area
-- @param #TASK_A2A_DISPATCHER self
-- @param DetectedItem
-- @return #number, Core.CommandCenter#REPORT
function TASK_A2A_DISPATCHER:GetFriendliesNearBy( DetectedItem )
local DetectedSet = DetectedItem.Set
local FriendlyUnitsNearBy = self.Detection:GetFriendliesNearBy( DetectedItem )
local FriendlyTypes = {}
local FriendliesCount = 0
if FriendlyUnitsNearBy then
local DetectedTreatLevel = DetectedSet:CalculateThreatLevelA2G()
for FriendlyUnitName, FriendlyUnitData in pairs( FriendlyUnitsNearBy ) do
local FriendlyUnit = FriendlyUnitData -- Wrapper.Unit#UNIT
if FriendlyUnit:IsAirPlane() then
local FriendlyUnitThreatLevel = FriendlyUnit:GetThreatLevel()
FriendliesCount = FriendliesCount + 1
local FriendlyType = FriendlyUnit:GetTypeName()
FriendlyTypes[FriendlyType] = FriendlyTypes[FriendlyType] and ( FriendlyTypes[FriendlyType] + 1 ) or 1
if DetectedTreatLevel < FriendlyUnitThreatLevel + 2 then
end
end
end
end
--self:E( { FriendliesCount = FriendliesCount } )
local FriendlyTypesReport = REPORT:New()
if FriendliesCount > 0 then
for FriendlyType, FriendlyTypeCount in pairs( FriendlyTypes ) do
FriendlyTypesReport:Add( string.format("%d of %s", FriendlyTypeCount, FriendlyType ) )
end
else
FriendlyTypesReport:Add( "-" )
end
return FriendliesCount, FriendlyTypesReport
end
--- Calculates which HUMAN friendlies are nearby the area
-- @param #TASK_A2A_DISPATCHER self
-- @param DetectedItem
-- @return #number, Core.CommandCenter#REPORT
function TASK_A2A_DISPATCHER:GetPlayerFriendliesNearBy( DetectedItem )
local DetectedSet = DetectedItem.Set
local PlayersNearBy = self.Detection:GetPlayersNearBy( DetectedItem )
local PlayerTypes = {}
local PlayersCount = 0
if PlayersNearBy then
local DetectedTreatLevel = DetectedSet:CalculateThreatLevelA2G()
for PlayerUnitName, PlayerUnitData in pairs( PlayersNearBy ) do
local PlayerUnit = PlayerUnitData -- Wrapper.Unit#UNIT
local PlayerName = PlayerUnit:GetPlayerName()
--self:E( { PlayerName = PlayerName, PlayerUnit = PlayerUnit } )
if PlayerUnit:IsAirPlane() and PlayerName ~= nil then
local FriendlyUnitThreatLevel = PlayerUnit:GetThreatLevel()
PlayersCount = PlayersCount + 1
local PlayerType = PlayerUnit:GetTypeName()
PlayerTypes[PlayerName] = PlayerType
if DetectedTreatLevel < FriendlyUnitThreatLevel + 2 then
end
end
end
end
--self:E( { PlayersCount = PlayersCount } )
local PlayerTypesReport = REPORT:New()
if PlayersCount > 0 then
for PlayerName, PlayerType in pairs( PlayerTypes ) do
PlayerTypesReport:Add( string.format('"%s" in %s', PlayerName, PlayerType ) )
end
else
PlayerTypesReport:Add( "-" )
end
return PlayersCount, PlayerTypesReport
end
--- Assigns tasks in relation to the detected items to the @{Set#SET_GROUP}.
-- @param #TASK_A2A_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_A2A_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()
-- Checking the task queue for the dispatcher, and removing any obsolete task!
for TaskIndex, TaskData in pairs( self.Tasks ) do
local Task = TaskData -- Tasking.Task#TASK
if Task:IsStatePlanned() then
local DetectedItem = Detection:GetDetectedItem( TaskIndex )
if not DetectedItem then
local TaskText = Task:GetName()
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
Mission:GetCommandCenter():MessageToGroup( string.format( "Obsolete A2A task %s for %s removed.", TaskText, Mission:GetName() ), TaskGroup )
end
Mission:RemoveTask( Task )
self.Tasks[TaskIndex] = nil
end
end
end
-- Now that all obsolete tasks are removed, loop through 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 DetectedCount = DetectedSet:Count()
local DetectedZone = DetectedItem.Zone
--self:E( { "Targets in DetectedItem", DetectedItem.ItemID, DetectedSet:Count(), tostring( DetectedItem ) } )
--DetectedSet:Flush()
local DetectedID = DetectedItem.ID
local TaskIndex = DetectedItem.Index
local DetectedItemChanged = DetectedItem.Changed
local Task = self.Tasks[TaskIndex]
Task = self:EvaluateRemoveTask( Mission, Task, Detection, DetectedItem, TaskIndex, DetectedItemChanged ) -- Task will be removed if it is planned and changed.
-- Evaluate INTERCEPT
if not Task and DetectedCount > 0 then
local TargetSetUnit = self:EvaluateENGAGE( DetectedItem ) -- Returns a SetUnit if there are targets to be INTERCEPTed...
if TargetSetUnit then
Task = TASK_A2A_ENGAGE:New( Mission, self.SetGroup, string.format( "ENGAGE.%03d", DetectedID ), TargetSetUnit )
else
local TargetSetUnit = self:EvaluateINTERCEPT( DetectedItem ) -- Returns a SetUnit if there are targets to be INTERCEPTed...
if TargetSetUnit then
Task = TASK_A2A_INTERCEPT:New( Mission, self.SetGroup, string.format( "INTERCEPT.%03d", DetectedID ), TargetSetUnit )
else
local TargetSetUnit = self:EvaluateSWEEP( DetectedItem ) -- Returns a SetUnit
if TargetSetUnit then
Task = TASK_A2A_SWEEP:New( Mission, self.SetGroup, string.format( "SWEEP.%03d", DetectedID ), TargetSetUnit )
end
end
end
if Task then
self.Tasks[TaskIndex] = Task
Task:SetTargetZone( DetectedZone, DetectedSet:GetFirst():GetAltitude(), DetectedSet:GetFirst():GetHeading() )
Task:SetDispatcher( self )
Mission:AddTask( Task )
TaskReport:Add( Task:GetName() )
else
self:E("This should not happen")
end
end
if Task then
local FriendliesCount, FriendliesReport = self:GetFriendliesNearBy( DetectedItem )
Task:SetInfo( "Friendlies", string.format( "%d ( %s )", FriendliesCount, FriendliesReport:Text( "," ) ), 30 )
local PlayersCount, PlayersReport = self:GetPlayerFriendliesNearBy( DetectedItem )
Task:SetInfo( "Players", string.format( "%d ( %s )", PlayersCount, PlayersReport:Text( "," ) ), 31 )
end
-- 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

@@ -1,29 +1,45 @@
--- (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}.
-- The TASK_A2G is implemented using a @{Statemachine#FSM_TASK}, and has the following statuses:
-- ![Banner Image](..\Presentations\TASK_A2G\Dia1.JPG)
--
-- * **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.
-- ====
--
-- ===
-- ### Author: **Sven Van de Velde (FlightControl)**
--
-- ### Authors: FlightControl - Design and Programming
-- ### Contributions:
--
-- ====
--
-- @module Task_A2G
do -- TASK_A2G
--- The TASK_A2G class
-- @type TASK_A2G
-- @field Set#SET_UNIT TargetSetUnit
-- @extends Tasking.Task#TASK
--- # 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 @{Fsm#FSM_TASK}, and has the following statuses:
--
-- * **None**: Start of the process
-- * **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.
--
-- ## 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.
--
-- @field #TASK_A2G
TASK_A2G = {
ClassName = "TASK_A2G",
}
@@ -33,52 +49,596 @@ 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
self.TaskType = TaskType
local Fsm = self:GetUnitProcess()
local A2GUnitProcess = self:GetUnitProcess()
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" )
Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "RouteToRendezVous", Rejected = "Reject" } )
function A2GUnitProcess:onenterUpdated( TaskUnit )
self:E( { self } )
self:Account()
self:Smoke()
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 ), {} )
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:GetY(), 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
function TASK_A2G:SetGoalTotal()
self.GoalTotal = self.TargetSetUnit:Count()
end
function TASK_A2G:GetGoalTotal()
return self.GoalTotal
end
end
do -- TASK_A2G_SEAD
--- The TASK_A2G_SEAD class
-- @type TASK_A2G_SEAD
-- @field Set#SET_UNIT TargetSetUnit
-- @extends Tasking.Task#TASK
--- # TASK_A2G_SEAD class, extends @{Task_A2G#TASK_A2G}
--
-- The TASK_A2G_SEAD class defines an Suppression or Extermination of Air Defenses task for a human player to be executed.
-- These tasks are important to be executed as they will help to achieve air superiority at the vicinity.
--
-- The TASK_A2G_SEAD is used by the @{Task_A2G_Dispatcher#TASK_A2G_DISPATCHER} to automatically create SEAD tasks
-- based on detected enemy ground targets.
--
-- @field #TASK_A2G_SEAD
TASK_A2G_SEAD = {
ClassName = "TASK_A2G_SEAD",
}
--- Instantiates a new TASK_A2G_SEAD.
-- @param #TASK_A2G_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_A2G_SEAD self
function TASK_A2G_SEAD:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing )
local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "SEAD", TaskBriefing ) ) -- #TASK_A2G_SEAD
self:F()
Mission:AddTask( self )
self:SetBriefing(
TaskBriefing or
"Execute a Suppression of Enemy Air Defenses.\n"
)
local TargetCoordinate = TargetSetUnit:GetFirst():GetCoordinate()
self:SetInfo( "Coordinates", TargetCoordinate, 10 )
self:SetInfo( "Threat", "[" .. string.rep( "", TargetSetUnit:CalculateThreatLevelA2G() ) .. "]", 11 )
local DetectedItemsCount = TargetSetUnit:Count()
local DetectedItemsTypes = TargetSetUnit:GetTypeNames()
self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, DetectedItemsTypes ), 0 )
return self
end
function TASK_A2G_SEAD:ReportOrder( ReportGroup )
local Coordinate = self.TaskInfo.Coordinates.TaskInfoText
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
return Distance
end
--- @param #TASK_A2G_SEAD self
function TASK_A2G_SEAD:onafterGoal( TaskUnit, From, Event, To )
local TargetSetUnit = self.TargetSetUnit -- Core.Set#SET_UNIT
if TargetSetUnit:Count() == 0 then
self:Success()
end
self:__Goal( -10 )
end
--- Set a score when a target in scope of the A2G attack, has been destroyed .
-- @param #TASK_A2G_SEAD self
-- @param #string PlayerName The name of the player.
-- @param #number Score The score in points to be granted when task process has been achieved.
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return #TASK_A2G_SEAD
function TASK_A2G_SEAD:SetScoreOnProgress( PlayerName, Score, TaskUnit )
self:F( { PlayerName, Score, TaskUnit } )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has SEADed a target.", Score )
return self
end
--- Set a score when all the targets in scope of the A2G attack, have been destroyed.
-- @param #TASK_A2G_SEAD self
-- @param #string PlayerName The name of the player.
-- @param #number Score The score in points.
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return #TASK_A2G_SEAD
function TASK_A2G_SEAD:SetScoreOnSuccess( PlayerName, Score, TaskUnit )
self:F( { PlayerName, Score, TaskUnit } )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScore( "Success", "All radar emitting targets have been successfully SEADed!", Score )
return self
end
--- Set a penalty when the A2G attack has failed.
-- @param #TASK_A2G_SEAD self
-- @param #string PlayerName The name of the player.
-- @param #number Penalty The penalty in points, must be a negative value!
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return #TASK_A2G_SEAD
function TASK_A2G_SEAD:SetScoreOnFail( PlayerName, Penalty, TaskUnit )
self:F( { PlayerName, Penalty, TaskUnit } )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScore( "Failed", "The SEADing has failed!", Penalty )
return self
end
end
do -- TASK_A2G_BAI
--- The TASK_A2G_BAI class
-- @type TASK_A2G_BAI
-- @field Set#SET_UNIT TargetSetUnit
-- @extends Tasking.Task#TASK
--- # TASK_A2G_BAI class, extends @{Task_A2G#TASK_A2G}
--
-- The TASK_A2G_BAI class defines an Battlefield Air Interdiction task for a human player to be executed.
-- These tasks are more strategic in nature and are most of the time further away from friendly forces.
-- BAI tasks can also be used to express the abscence of friendly forces near the vicinity.
--
-- The TASK_A2G_BAI is used by the @{Task_A2G_Dispatcher#TASK_A2G_DISPATCHER} to automatically create BAI tasks
-- based on detected enemy ground targets.
--
-- @field #TASK_A2G_BAI
TASK_A2G_BAI = {
ClassName = "TASK_A2G_BAI",
}
--- Instantiates a new TASK_A2G_BAI.
-- @param #TASK_A2G_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_A2G_BAI self
function TASK_A2G_BAI:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing )
local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "BAI", TaskBriefing ) ) -- #TASK_A2G_BAI
self:F()
Mission:AddTask( self )
self:SetBriefing(
TaskBriefing or
"Execute a Battlefield Air Interdiction of a group of enemy targets.\n"
)
local TargetCoordinate = TargetSetUnit:GetFirst():GetCoordinate()
self:SetInfo( "Coordinates", TargetCoordinate, 10 )
self:SetInfo( "Threat", "[" .. string.rep( "", TargetSetUnit:CalculateThreatLevelA2G() ) .. "]", 11 )
local DetectedItemsCount = TargetSetUnit:Count()
local DetectedItemsTypes = TargetSetUnit:GetTypeNames()
self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, DetectedItemsTypes ), 0 )
return self
end
function TASK_A2G_BAI:ReportOrder( ReportGroup )
local Coordinate = self.TaskInfo.Coordinates.TaskInfoText
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
return Distance
end
--- @param #TASK_A2G_BAI self
function TASK_A2G_BAI:onafterGoal( TaskUnit, From, Event, To )
local TargetSetUnit = self.TargetSetUnit -- Core.Set#SET_UNIT
if TargetSetUnit:Count() == 0 then
self:Success()
end
self:__Goal( -10 )
end
--- Set a score when a target in scope of the A2G attack, has been destroyed .
-- @param #TASK_A2G_BAI self
-- @param #string PlayerName The name of the player.
-- @param #number Score The score in points to be granted when task process has been achieved.
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return #TASK_A2G_BAI
function TASK_A2G_BAI:SetScoreOnProgress( PlayerName, Score, TaskUnit )
self:F( { PlayerName, Score, TaskUnit } )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has destroyed a target in Battlefield Air Interdiction (BAI).", Score )
return self
end
--- Set a score when all the targets in scope of the A2G attack, have been destroyed.
-- @param #TASK_A2G_BAI self
-- @param #string PlayerName The name of the player.
-- @param #number Score The score in points.
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return #TASK_A2G_BAI
function TASK_A2G_BAI:SetScoreOnSuccess( PlayerName, Score, TaskUnit )
self:F( { PlayerName, Score, TaskUnit } )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScore( "Success", "All targets have been successfully destroyed! The Battlefield Air Interdiction (BAI) is a success!", Score )
return self
end
--- Set a penalty when the A2G attack has failed.
-- @param #TASK_A2G_BAI self
-- @param #string PlayerName The name of the player.
-- @param #number Penalty The penalty in points, must be a negative value!
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return #TASK_A2G_BAI
function TASK_A2G_BAI:SetScoreOnFail( PlayerName, Penalty, TaskUnit )
self:F( { PlayerName, Penalty, TaskUnit } )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScore( "Failed", "The Battlefield Air Interdiction (BAI) has failed!", Penalty )
return self
end
end
do -- TASK_A2G_CAS
--- The TASK_A2G_CAS class
-- @type TASK_A2G_CAS
-- @field Set#SET_UNIT TargetSetUnit
-- @extends Tasking.Task#TASK
--- # TASK_A2G_CAS class, extends @{Task_A2G#TASK_A2G}
--
-- The TASK_A2G_CAS class defines an Close Air Support task for a human player to be executed.
-- Friendly forces will be in the vicinity within 6km from the enemy.
--
-- The TASK_A2G_CAS is used by the @{Task_A2G_Dispatcher#TASK_A2G_DISPATCHER} to automatically create CAS tasks
-- based on detected enemy ground targets.
--
-- @field #TASK_A2G_CAS
TASK_A2G_CAS = {
ClassName = "TASK_A2G_CAS",
}
--- Instantiates a new TASK_A2G_CAS.
-- @param #TASK_A2G_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_A2G_CAS self
function TASK_A2G_CAS:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing )
local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "CAS", TaskBriefing ) ) -- #TASK_A2G_CAS
self:F()
Mission:AddTask( self )
self:SetBriefing(
TaskBriefing or
"Execute a Close Air Support for a group of enemy targets.\n" ..
"Beware of friendlies at the vicinity!\n"
)
local TargetCoordinate = TargetSetUnit:GetFirst():GetCoordinate()
self:SetInfo( "Coordinates", TargetCoordinate, 10 )
self:SetInfo( "Threat", "[" .. string.rep( "", TargetSetUnit:CalculateThreatLevelA2G() ) .. "]", 11 )
local DetectedItemsCount = TargetSetUnit:Count()
local DetectedItemsTypes = TargetSetUnit:GetTypeNames()
self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, DetectedItemsTypes ), 0 )
return self
end
function TASK_A2G_CAS:ReportOrder( ReportGroup )
local Coordinate = self.TaskInfo.Coordinates.TaskInfoText
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
return Distance
end
--- @param #TASK_A2G_CAS self
function TASK_A2G_CAS:onafterGoal( TaskUnit, From, Event, To )
local TargetSetUnit = self.TargetSetUnit -- Core.Set#SET_UNIT
if TargetSetUnit:Count() == 0 then
self:Success()
end
self:__Goal( -10 )
end
--- Set a score when a target in scope of the A2G attack, has been destroyed .
-- @param #TASK_A2G_CAS self
-- @param #string PlayerName The name of the player.
-- @param #number Score The score in points to be granted when task process has been achieved.
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return #TASK_A2G_CAS
function TASK_A2G_CAS:SetScoreOnProgress( PlayerName, Score, TaskUnit )
self:F( { PlayerName, Score, TaskUnit } )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScoreProcess( "Engaging", "Account", "AccountForPlayer", "Player " .. PlayerName .. " has destroyed a target in Close Air Support (CAS).", Score )
return self
end
--- Set a score when all the targets in scope of the A2G attack, have been destroyed.
-- @param #TASK_A2G_CAS self
-- @param #string PlayerName The name of the player.
-- @param #number Score The score in points.
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return #TASK_A2G_CAS
function TASK_A2G_CAS:SetScoreOnSuccess( PlayerName, Score, TaskUnit )
self:F( { PlayerName, Score, TaskUnit } )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScore( "Success", "All targets have been successfully destroyed! The Close Air Support (CAS) was a success!", Score )
return self
end
--- Set a penalty when the A2G attack has failed.
-- @param #TASK_A2G_CAS self
-- @param #string PlayerName The name of the player.
-- @param #number Penalty The penalty in points, must be a negative value!
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return #TASK_A2G_CAS
function TASK_A2G_CAS:SetScoreOnFail( PlayerName, Penalty, TaskUnit )
self:F( { PlayerName, Penalty, TaskUnit } )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScore( "Failed", "The Close Air Support (CAS) has failed!", Penalty )
return self
end
end

View File

@@ -0,0 +1,293 @@
--- **Tasking** - The TASK_A2G_DISPATCHER creates and manages player TASK_A2G tasks based on detected targets.
--
-- ====
--
-- ### Author: **Sven Van de Velde (FlightControl)**
--
-- ### Contributions:
--
-- ====
--
-- @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_DISPATCHE} 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...
--
-- ## TASK_A2G_DISPATCHER constructor
--
-- The @{#TASK_A2G_DISPATCHER.New}() method creates a new TASK_A2G_DISPATCHER instance.
--
-- @field #TASK_A2G_DISPATCHER
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, TaskIndex, DetectedItemChanged )
if Task then
if Task:IsStatePlanned() and DetectedItemChanged == true then
--self:E( "Removing Tasking: " .. Task:GetTaskName() )
Mission:RemoveTask( Task )
self.Tasks[TaskIndex] = 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()
-- Checking the task queue for the dispatcher, and removing any obsolete task!
for TaskIndex, TaskData in pairs( self.Tasks ) do
local Task = TaskData -- Tasking.Task#TASK
if Task:IsStatePlanned() then
local DetectedItem = Detection:GetDetectedItem( TaskIndex )
if not DetectedItem then
local TaskText = Task:GetName()
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
Mission:GetCommandCenter():MessageToGroup( string.format( "Obsolete A2G task %s for %s removed.", TaskText, Mission:GetName() ), TaskGroup )
end
Mission:RemoveTask( Task )
self.Tasks[TaskIndex] = nil
end
end
end
--- 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 TaskIndex = DetectedItem.Index
local DetectedItemChanged = DetectedItem.Changed
local Task = self.Tasks[TaskIndex]
Task = self:EvaluateRemoveTask( Mission, Task, TaskIndex, 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_A2G_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_A2G_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_A2G_BAI:New( Mission, self.SetGroup, string.format( "BAI.%03d", DetectedItemID ), TargetSetUnit )
end
end
end
if Task then
self.Tasks[TaskIndex] = Task
Task:SetTargetZone( DetectedZone )
Task:SetDispatcher( self )
Mission:AddTask( Task )
TaskReport:Add( Task:GetName() )
else
self:E("This should not happen")
end
end
-- 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,982 @@
--- **Tasking** -- 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.
--
-- ====
--
-- ### Author: **Sven Van de Velde (FlightControl)**
--
-- ### Contributions:
--
-- ====
--
-- @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.SmokeColor = SMOKECOLOR.Red
self.CargoItemCount = {} -- Map of Carriers having a cargo item count to check the cargo loading limits.
self.CargoLimit = 2
self.DeployZones = {} -- setmetatable( {}, { __mode = "v" } ) -- weak table on value
local Fsm = self:GetUnitProcess()
Fsm:SetStartState( "Planned" )
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 )
local TaskUnitName = TaskUnit:GetName()
self:E( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } )
local MenuTime = timer.getTime()
TaskUnit.Menu = MENU_GROUP:New( TaskUnit:GetGroup(), Task:GetName() .. " @ " .. TaskUnit:GetName() ):SetTime( MenuTime )
local CargoItemCount = TaskUnit:CargoItemCount()
--Task:GetMission():GetCommandCenter():MessageToGroup( "Cargo in carrier: " .. CargoItemCount, TaskUnit:GetGroup() )
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 CargoItemCount < Task.CargoLimit then
if Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then
local NotInDeployZones = true
for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do
if Cargo:IsInZone( DeployZone ) then
NotInDeployZones = false
end
end
if NotInDeployZones then
if not TaskUnit:InAir() then
MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Board cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuBoardCargo, self, Cargo ):SetTime(MenuTime)
end
end
else
MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Route to Pickup cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuRouteToPickup, self, Cargo ):SetTime(MenuTime)
end
end
end
if Cargo:IsLoaded() then
if not TaskUnit:InAir() then
MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Unboard cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuUnBoardCargo, self, Cargo ):SetTime(MenuTime)
end
-- 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 )
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
---
--#TASK_CAROG_TRANSPORT self
--#Wrapper.Unit#UNIT
--- @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
self.Cargo:Smoke( Task:GetSmokeColor(), 15 )
if TaskUnit:IsAir() then
Task:GetMission():GetCommandCenter():MessageToGroup( "Land", TaskUnit:GetGroup() )
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
--- @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
Task:GetMission():GetCommandCenter():MessageToGroup( "Land", TaskUnit:GetGroup() )
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
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 )
local TaskUnitName = TaskUnit:GetName()
self:E( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } )
self.Cargo:MessageToGroup( "Boarded ...", TaskUnit:GetGroup() )
TaskUnit:AddCargo( self.Cargo )
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 )
local TaskUnitName = TaskUnit:GetName()
self:E( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } )
self.Cargo:MessageToGroup( "UnBoarded ...", TaskUnit:GetGroup() )
TaskUnit:RemoveCargo( self.Cargo )
local NotInDeployZones = true
for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do
if self.Cargo:IsInZone( DeployZone ) then
NotInDeployZones = false
end
end
if NotInDeployZones == false then
self.Cargo:SetDeployed( true )
end
-- TODO:I need to find a more decent solution for this.
Task:E( { CargoDeployed = Task.CargoDeployed and "true" or "false" } )
Task:E( { CargoIsAlive = self.Cargo:IsAlive() and "true" or "false" } )
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
--- Set a limit on the amount of cargo items that can be loaded into the Carriers.
-- @param #TASK_CARGO self
-- @param CargoLimit Specifies a number of cargo items that can be loaded in the helicopter.
-- @return #TASK_CARGO
function TASK_CARGO:SetCargoLimit( CargoLimit )
self.CargoLimit = CargoLimit
return self
end
---@param Color Might be SMOKECOLOR.Blue, SMOKECOLOR.Red SMOKECOLOR.Orange, SMOKECOLOR.White or SMOKECOLOR.Green
function TASK_CARGO:SetSmokeColor(SmokeColor)
-- Makes sure Coloe is set
if SmokeColor == nil then
self.SmokeColor = SMOKECOLOR.Red -- Make sure a default color is exist
elseif type(SmokeColor) == "number" then
self:F2(SmokeColor)
if SmokeColor > 0 and SmokeColor <=5 then -- Make sure number is within ragne, assuming first enum is one
self.SmokeColor = SMOKECOLOR.SmokeColor
end
end
end
--@return SmokeColor
function TASK_CARGO:GetSmokeColor()
return self.SmokeColor
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:SetScoreOnProgress( 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:SetScoreOnFail( Text, Penalty, TaskUnit )
self:F( { Text, Score, TaskUnit } )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
ProcessUnit:AddScore( "Failed", Text, Penalty )
return self
end
function TASK_CARGO:SetGoalTotal()
self.GoalTotal = self.SetCargo:Count()
end
function TASK_CARGO:GetGoalTotal()
return self.GoalTotal
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", "*" )
self:E( { CargoDeployed = self.CargoDeployed ~= nil and "true" or "false" } )
--- 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:ToStringMGRS() ) )
end
)
self:SetBriefing(
TaskBriefing or
CargoReport:Text()
)
return self
end
function TASK_CARGO_TRANSPORT:ReportOrder( ReportGroup )
return true
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
if Cargo:IsDeployed() then
-- 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 ) == false then
CargoDeployed = false
end
end
else
CargoDeployed = false
end
end
return CargoDeployed
end
--- @param #TASK_CARGO_TRANSPORT self
function TASK_CARGO_TRANSPORT:onafterGoal( TaskUnit, From, Event, To )
local CargoSet = self.CargoSet
if self:IsAllCargoTransported() then
self:Success()
end
self:__Goal( -10 )
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

@@ -183,6 +183,10 @@ UTILS.KnotsToMps = function(knots)
return knots*1852/3600
end
UTILS.KnotsToKmph = function(knots)
return knots* 1.852
end
UTILS.KmphToMps = function(kmph)
return kmph/3.6
end
@@ -276,6 +280,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
@@ -293,3 +307,27 @@ function UTILS.DoString( s )
return false, err
end
end
-- Here is a customized version of pairs, which I called spairs because it iterates over the table in a sorted order.
function UTILS.spairs( t, order )
-- collect the keys
local keys = {}
for k in pairs(t) do keys[#keys+1] = k end
-- if order function given, sort by it by passing the table and keys a, b,
-- otherwise just sort the keys
if order then
table.sort(keys, function(a,b) return order(t, a, b) end)
else
table.sort(keys)
end
-- return the iterator function
local i = 0
return function()
i = i + 1
if keys[i] then
return keys[i], t[keys[i]]
end
end
end

View File

@@ -1,17 +1,28 @@
--- This module contains the AIRBASE classes.
--- **Wrapper** -- AIRBASE is a wrapper class to handle the DCS Airbase objects.
--
-- ====
--
-- ### Author: **Sven Van de Velde (FlightControl)**
--
-- ### Contributions:
--
-- ===
--
-- 1) @{Airbase#AIRBASE} class, extends @{Positionable#POSITIONABLE}
-- =================================================================
-- The @{AIRBASE} class is a wrapper class to handle the DCS Airbase objects:
-- @module Airbase
--- @type AIRBASE
-- @extends Wrapper.Positionable#POSITIONABLE
--- # AIRBASE class, extends @{Positionable#POSITIONABLE}
--
-- AIRBASE is a wrapper class to handle the DCS Airbase objects:
--
-- * Support all DCS Airbase APIs.
-- * Enhance with Airbase specific APIs not in the DCS Airbase API set.
--
--
-- 1.1) AIRBASE reference methods
-- ------------------------------
-- ## AIRBASE reference methods
--
-- For each DCS Airbase object alive within a running mission, a AIRBASE wrapper object (instance) will be created within the _@{DATABASE} object.
-- This is done at the beginning of the mission (when the mission starts).
--
@@ -29,27 +40,14 @@
--
-- IMPORTANT: ONE SHOULD NEVER SANATIZE these AIRBASE OBJECT REFERENCES! (make the AIRBASE object references nil).
--
-- 1.2) DCS AIRBASE APIs
-- ---------------------
-- ## DCS Airbase APIs
--
-- The DCS Airbase APIs are used extensively within MOOSE. The AIRBASE class has for each DCS Airbase API a corresponding method.
-- To be able to distinguish easily in your code the difference between a AIRBASE API call and a DCS Airbase API call,
-- the first letter of the method is also capitalized. So, by example, the DCS Airbase method @{DCSWrapper.Airbase#Airbase.getName}()
-- is implemented in the AIRBASE class as @{#AIRBASE.GetName}().
--
-- More functions will be added
-- ----------------------------
-- During the MOOSE development, more functions will be added.
--
-- @module Airbase
-- @author FlightControl
--- The AIRBASE class
-- @type AIRBASE
-- @extends Wrapper.Positionable#POSITIONABLE
-- @field #AIRBASE AIRBASE
AIRBASE = {
ClassName="AIRBASE",
CategoryName = {
@@ -59,6 +57,88 @@ AIRBASE = {
},
}
--- @field Caucasus
AIRBASE.Caucasus = {
["Gelendzhik"] = "Gelendzhik",
["Krasnodar_Pashkovsky"] = "Krasnodar-Pashkovsky",
["Sukhumi_Babushara"] = "Sukhumi-Babushara",
["Gudauta"] = "Gudauta",
["Batumi"] = "Batumi",
["Senaki_Kolkhi"] = "Senaki-Kolkhi",
["Kobuleti"] = "Kobuleti",
["Kutaisi"] = "Kutaisi",
["Tbilisi_Lochini"] = "Tbilisi-Lochini",
["Soganlug"] = "Soganlug",
["Vaziani"] = "Vaziani",
["Anapa_Vityazevo"] = "Anapa-Vityazevo",
["Krasnodar_Center"] = "Krasnodar-Center",
["Novorossiysk"] = "Novorossiysk",
["Krymsk"] = "Krymsk",
["Maykop_Khanskaya"] = "Maykop-Khanskaya",
["Sochi_Adler"] = "Sochi-Adler",
["Mineralnye_Vody"] = "Mineralnye Vody",
["Nalchik"] = "Nalchik",
["Mozdok"] = "Mozdok",
["Beslan"] = "Beslan",
}
--- @field Nevada
AIRBASE.Nevada = {
["Creech_AFB"] = "Creech AFB",
["Groom_Lake_AFB"] = "Groom Lake AFB",
["McCarran_International_Airport"] = "McCarran International Airport",
["Nellis_AFB"] = "Nellis AFB",
["Beatty_Airport"] = "Beatty Airport",
["Boulder_City_Airport"] = "Boulder City Airport",
["Echo_Bay"] = "Echo Bay",
["Henderson_Executive_Airport"] = "Henderson Executive Airport",
["Jean_Airport"] = "Jean Airport",
["Laughlin_Airport"] = "Laughlin Airport",
["Lincoln_County"] = "Lincoln County",
["Mellan_Airstrip"] = "Mellan Airstrip",
["Mesquite"] = "Mesquite",
["Mina_Airport_3Q0"] = "Mina Airport 3Q0",
["North_Las_Vegas"] = "North Las Vegas",
["Pahute_Mesa_Airstrip"] = "Pahute Mesa Airstrip",
["Tonopah_Airport"] = "Tonopah Airport",
["Tonopah_Test_Range_Airfield"] = "Tonopah Test Range Airfield",
}
--- @field Normandy
AIRBASE.Normandy = {
["Saint_Pierre_du_Mont"] = "Saint Pierre du Mont",
["Lignerolles"] = "Lignerolles",
["Cretteville"] = "Cretteville",
["Maupertus"] = "Maupertus",
["Brucheville"] = "Brucheville",
["Meautis"] = "Meautis",
["Cricqueville_en_Bessin"] = "Cricqueville-en-Bessin",
["Lessay"] = "Lessay",
["Sainte_Laurent_sur_Mer"] = "Sainte-Laurent-sur-Mer",
["Biniville"] = "Biniville",
["Cardonville"] = "Cardonville",
["Deux_Jumeaux"] = "Deux Jumeaux",
["Chippelle"] = "Chippelle",
["Beuzeville"] = "Beuzeville",
["Azeville"] = "Azeville",
["Picauville"] = "Picauville",
["Le_Molay"] = "Le Molay",
["Longues_sur_Mer"] = "Longues-sur-Mer",
["Carpiquet"] = "Carpiquet",
["Bazenville"] = "Bazenville",
["Sainte_Croix_sur_Mer"] = "Sainte-Croix-sur-Mer",
["Beny_sur_Mer"] = "Beny-sur-Mer",
["Rucqueville"] = "Rucqueville",
["Sommervieu"] = "Sommervieu",
["Lantheuil"] = "Lantheuil",
["Evreux"] = "Evreux",
["Chailey"] = "Chailey",
["Needs_Oar_Point"] = "Needs Oar Point",
["Funtington"] = "Funtington",
["Tangmere"] = "Tangmere",
["Ford"] = "Ford",
}
-- Registration.
--- Create a new AIRBASE from DCSAirbase.
@@ -69,6 +149,7 @@ function AIRBASE:Register( AirbaseName )
local self = BASE:Inherit( self, POSITIONABLE:New( AirbaseName ) )
self.AirbaseName = AirbaseName
self.AirbaseZone = ZONE_RADIUS:New( AirbaseName, self:GetVec2(), 8000 )
return self
end
@@ -105,5 +186,12 @@ function AIRBASE:GetDCSObject()
return nil
end
--- Get the airbase zone.
-- @param #AIRBASE self
-- @return Core.Zone#ZONE_RADIUS The zone radius of the airbase.
function AIRBASE:GetZone()
return self.AirbaseZone
end

View File

@@ -1,10 +1,26 @@
--- This module contains the CLIENT class.
--- **Wrapper** -- CLIENT wraps DCS Unit objects acting as a __Client__ or __Player__ within a mission.
--
-- ====
--
-- ### Author: **Sven Van de Velde (FlightControl)**
--
-- ### Contributions:
--
-- ===
--
-- @module Client
--- The CLIENT class
-- @type CLIENT
-- @extends Wrapper.Unit#UNIT
--- # CLIENT class, extends @{Unit#UNIT}
--
-- 1) @{Client#CLIENT} class, extends @{Unit#UNIT}
-- ===============================================
-- Clients are those **Units** defined within the Mission Editor that have the skillset defined as __Client__ or __Player__.
-- Note that clients are NOT the same as Units, they are NOT necessarily alive.
-- The @{Client#CLIENT} class is a wrapper class to handle the DCS Unit objects that have the skillset defined as __Client__ or __Player__:
-- The CLIENT class is a wrapper class to handle the DCS Unit objects that have the skillset defined as __Client__ or __Player__:
--
-- * Wraps the DCS Unit objects with skill level set to Player or Client.
-- * Support all DCS Unit APIs.
@@ -15,8 +31,8 @@
--
-- Clients are being used by the @{MISSION} class to follow players and register their successes.
--
-- 1.1) CLIENT reference methods
-- -----------------------------
-- ## CLIENT reference methods
--
-- For each DCS Unit having skill level Player or Client, a CLIENT wrapper object (instance) will be created within the _@{DATABASE} object.
-- This is done at the beginning of the mission (when the mission starts).
--
@@ -32,13 +48,9 @@
-- * @{#CLIENT.Find}(): Find a CLIENT instance from the _DATABASE object using a DCS Unit object.
-- * @{#CLIENT.FindByName}(): Find a CLIENT instance from the _DATABASE object using a DCS Unit name.
--
-- IMPORTANT: ONE SHOULD NEVER SANATIZE these CLIENT OBJECT REFERENCES! (make the CLIENT object references nil).
-- **IMPORTANT: ONE SHOULD NEVER SANATIZE these CLIENT OBJECT REFERENCES! (make the CLIENT object references nil).**
--
-- @module Client
--- The CLIENT class
-- @type CLIENT
-- @extends Wrapper.Unit#UNIT
-- @field #CLIENT
CLIENT = {
ONBOARDSIDE = {
NONE = 0,
@@ -73,7 +85,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 +94,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

@@ -1,36 +1,55 @@
--- This module contains the CONTROLLABLE class.
--- **Wrapper** -- CONTROLLABLE is an intermediate class wrapping Group and Unit classes "controllers".
--
-- 1) @{Controllable#CONTROLLABLE} class, extends @{Positionable#POSITIONABLE}
-- ===========================================================
-- The @{Controllable#CONTROLLABLE} class is a wrapper class to handle the DCS Controllable objects:
-- ====
--
-- ### Author: **Sven Van de Velde (FlightControl)**
--
-- ### Contributions:
--
-- ===
--
-- @module Controllable
--- @type CONTROLLABLE
-- @extends Wrapper.Positionable#POSITIONABLE
-- @field Dcs.DCSWrapper.Controllable#Controllable DCSControllable The DCS controllable class.
-- @field #string ControllableName The name of the controllable.
--- # CONTROLLABLE class, extends @{Positionable#POSITIONABLE}
--
-- CONTROLLABLE is a wrapper class to handle the "DCS Controllable objects", which are Groups and Units:
--
-- * Support all DCS Controllable APIs.
-- * Enhance with Controllable specific APIs not in the DCS Controllable API set.
-- * Handle local Controllable Controller.
-- * Manage the "state" of the DCS Controllable.
--
-- 1.1) CONTROLLABLE constructor
-- -----------------------------
-- ## CONTROLLABLE constructor
--
-- The CONTROLLABLE class provides the following functions to construct a CONTROLLABLE instance:
--
-- * @{#CONTROLLABLE.New}(): Create a CONTROLLABLE instance.
--
-- 1.2) CONTROLLABLE task methods
-- ------------------------------
-- ## CONTROLLABLE Task methods
--
-- Several controllable task methods are available that help you to prepare tasks.
-- These methods return a string consisting of the task description, which can then be given to either a @{Controllable#CONTROLLABLE.PushTask} or @{Controllable#SetTask} method to assign the task to the CONTROLLABLE.
-- Tasks are specific for the category of the CONTROLLABLE, more specific, for AIR, GROUND or AIR and GROUND.
-- Each task description where applicable indicates for which controllable category the task is valid.
-- There are 2 main subdivisions of tasks: Assigned tasks and EnRoute tasks.
--
-- ### 1.2.1) Assigned task methods
-- ### Task assignment
--
-- Assigned task methods make the controllable execute the task where the location of the (possible) targets of the task are known before being detected.
-- This is different from the EnRoute tasks, where the targets of the task need to be detected before the task can be executed.
--
-- 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 +57,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.
@@ -54,19 +73,20 @@
-- * @{#CONTROLLABLE.TaskRouteToZone}: (AIR + GROUND) Route the controllable to a given zone.
-- * @{#CONTROLLABLE.TaskReturnToBase}: (AIR) Route the controllable to an airbase.
--
-- ### 1.2.2) EnRoute task methods
-- ### EnRoute assignment
--
-- EnRoute tasks require the targets of the task need to be detected by the controllable (using its sensors) before the task can be executed:
--
-- * @{#CONTROLLABLE.EnRouteTaskAWACS}: (AIR) Aircraft will act as an AWACS for friendly units (will provide them with information about contacts). No parameters.
-- * @{#CONTROLLABLE.EnRouteTaskEngageControllable}: (AIR) Engaging a controllable. The task does not assign the target controllable to the unit/controllable to attack now; it just allows the unit/controllable to engage the target controllable as well as other assigned targets.
-- * @{#CONTROLLABLE.EnRouteTaskEngageTargets}: (AIR) Engaging targets of defined types.
-- * @{#CONTROLLABLE.EnRouteTaskEngageTargetsInZone}: (AIR) Engaging a targets of defined types at circle-shaped zone.
-- * @{#CONTROLLABLE.EnRouteTaskEWR}: (AIR) Attack the Unit.
-- * @{#CONTROLLABLE.EnRouteTaskFAC}: (AIR + GROUND) The task makes the controllable/unit a FAC and lets the FAC to choose a targets (enemy ground controllable) around as well as other assigned targets.
-- * @{#CONTROLLABLE.EnRouteTaskFAC_EngageControllable}: (AIR + GROUND) The task makes the controllable/unit a FAC and lets the FAC to choose the target (enemy ground controllable) as well as other assigned targets.
-- * @{#CONTROLLABLE.EnRouteTaskTanker}: (AIR) Aircraft will act as a tanker for friendly units. No parameters.
--
-- ### 1.2.3) Preparation task methods
-- ### Task preparation
--
-- There are certain task methods that allow to tailor the task behaviour:
--
@@ -75,24 +95,24 @@
-- * @{#CONTROLLABLE.TaskCondition}: Return a condition section for a controlled task.
-- * @{#CONTROLLABLE.TaskControlled}: Return a Controlled Task taking a Task and a TaskCondition.
--
-- ### 1.2.4) Obtain the mission from controllable templates
-- ### Obtain the mission from controllable templates
--
-- Controllable templates contain complete mission descriptions. Sometimes you want to copy a complete mission from a controllable and assign it to another:
--
-- * @{#CONTROLLABLE.TaskMission}: (AIR + GROUND) Return a mission task from a mission template.
--
-- 1.3) CONTROLLABLE Command methods
-- --------------------------
-- ## CONTROLLABLE Command methods
--
-- Controllable **command methods** prepare the execution of commands using the @{#CONTROLLABLE.SetCommand} method:
--
-- * @{#CONTROLLABLE.CommandDoScript}: Do Script command.
-- * @{#CONTROLLABLE.CommandSwitchWayPoint}: Perform a switch waypoint command.
--
-- 1.4) CONTROLLABLE Option methods
-- -------------------------
-- ## CONTROLLABLE Option methods
--
-- Controllable **Option methods** change the behaviour of the Controllable while being alive.
--
-- ### 1.4.1) Rule of Engagement:
-- ### Rule of Engagement:
--
-- * @{#CONTROLLABLE.OptionROEWeaponFree}
-- * @{#CONTROLLABLE.OptionROEOpenFire}
@@ -106,7 +126,7 @@
-- * @{#CONTROLLABLE.OptionROEReturnFirePossible}
-- * @{#CONTROLLABLE.OptionROEEvadeFirePossible}
--
-- ### 1.4.2) Rule on thread:
-- ### Rule on thread:
--
-- * @{#CONTROLLABLE.OptionROTNoReaction}
-- * @{#CONTROLLABLE.OptionROTPassiveDefense}
@@ -120,15 +140,7 @@
-- * @{#CONTROLLABLE.OptionROTEvadeFirePossible}
-- * @{#CONTROLLABLE.OptionROTVerticalPossible}
--
-- ===
--
-- @module Controllable
--- The CONTROLLABLE class
-- @type CONTROLLABLE
-- @extends Wrapper.Positionable#POSITIONABLE
-- @field Dcs.DCSWrapper.Controllable#Controllable DCSControllable The DCS controllable class.
-- @field #string ControllableName The name of the controllable.
-- @field #CONTROLLABLE
CONTROLLABLE = {
ClassName = "CONTROLLABLE",
ControllableName = "",
@@ -140,7 +152,7 @@ CONTROLLABLE = {
-- @param Dcs.DCSWrapper.Controllable#Controllable ControllableName The DCS Controllable name
-- @return #CONTROLLABLE self
function CONTROLLABLE:New( ControllableName )
local self = BASE:Inherit( self, POSITIONABLE:New( ControllableName ) )
local self = BASE:Inherit( self, POSITIONABLE:New( ControllableName ) ) -- #CONTROLLABLE
self:F2( ControllableName )
self.ControllableName = ControllableName
@@ -154,12 +166,10 @@ end
-- @param #CONTROLLABLE self
-- @return Dcs.DCSController#Controller
function CONTROLLABLE:_GetController()
self:F2( { self.ControllableName } )
local DCSControllable = self:GetDCSObject()
if DCSControllable then
local ControllableController = DCSControllable:getController()
self:T3( ControllableController )
return ControllableController
end
@@ -218,10 +228,58 @@ function CONTROLLABLE:GetLife()
return nil
end
--- Returns the initial health.
-- @param #CONTROLLABLE self
-- @return #number The controllable health value (unit or group average).
-- @return #nil The controllable is not existing or alive.
function CONTROLLABLE:GetLife0()
self:F2( self.ControllableName )
local DCSControllable = self:GetDCSObject()
if DCSControllable then
local UnitLife = 0
local Units = self:GetUnits()
if #Units == 1 then
local Unit = Units[1] -- Wrapper.Unit#UNIT
UnitLife = Unit:GetLife0()
else
local UnitLifeTotal = 0
for UnitID, Unit in pairs( Units ) do
local Unit = Unit -- Wrapper.Unit#UNIT
UnitLifeTotal = UnitLifeTotal + Unit:GetLife0()
end
UnitLife = UnitLifeTotal / #Units
end
return UnitLife
end
return nil
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
@@ -270,14 +328,13 @@ end
-- @param #CONTROLLABLE self
-- @return Wrapper.Controllable#CONTROLLABLE self
function CONTROLLABLE:SetTask( DCSTask, WaitTime )
self:F2( { DCSTask } )
self:F2( { DCSTask = DCSTask } )
local DCSControllable = self:GetDCSObject()
if DCSControllable then
local Controller = self:_GetController()
self:T3( Controller )
-- When a controllable SPAWNs, it takes about a second to get the controllable in the simulator. Setting tasks to unspawned controllables provides unexpected results.
-- Therefore we schedule the functions to set the mission and options for the Controllable.
@@ -295,6 +352,24 @@ function CONTROLLABLE:SetTask( DCSTask, WaitTime )
return nil
end
--- Checking the Task Queue of the controllable. Returns false if no task is on the queue. true if there is a task.
-- @param #CONTROLLABLE self
-- @return Wrapper.Controllable#CONTROLLABLE self
function CONTROLLABLE:HasTask() --R2.2
local HasTaskResult = false
local DCSControllable = self:GetDCSObject()
if DCSControllable then
local Controller = self:_GetController()
HasTaskResult = Controller:hasTask()
end
return HasTaskResult
end
--- Return a condition section for a controlled task.
-- @param #CONTROLLABLE self
@@ -359,7 +434,7 @@ function CONTROLLABLE:TaskCombo( DCSTasks )
}
for TaskID, Task in ipairs( DCSTasks ) do
self:E( Task )
self:T( Task )
end
self:T3( { DCSTaskCombo } )
@@ -439,12 +514,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 +552,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 +583,7 @@ function CONTROLLABLE:TaskAttackGroup( AttackGroup, WeaponType, WeaponExpend, At
end
local DCSTask
DCSTask = { id = 'AttackControllable',
DCSTask = { id = 'AttackGroup',
params = {
groupId = AttackGroup:GetID(),
weaponType = WeaponType,
@@ -518,51 +601,39 @@ 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
}
}
self:E( DCSTask )
self:T3( DCSTask )
return DCSTask
end
@@ -571,36 +642,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 +707,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 +786,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 +1079,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 +1090,7 @@ function CONTROLLABLE:TaskFAC_AttackGroup( AttackGroup, WeaponType, Designation,
-- }
local DCSTask
DCSTask = { id = 'FAC_AttackControllable',
DCSTask = { id = 'FAC_AttackGroup',
params = {
groupId = AttackGroup:GetID(),
weaponType = WeaponType,
@@ -1084,7 +1145,7 @@ end
-- @param Dcs.DCSTypes#AttributeNameArray TargetTypes Array of target categories allowed to engage.
-- @param #number Priority 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.
-- @return Dcs.DCSTasking.Task#Task The DCS task structure.
function CONTROLLABLE:EnRouteTaskEngageTargets( Vec2, Radius, TargetTypes, Priority )
function CONTROLLABLE:EnRouteTaskEngageTargetsInZone( Vec2, Radius, TargetTypes, Priority )
self:F2( { self.ControllableName, Vec2, Radius, TargetTypes, Priority } )
-- EngageTargetsInZone = {
@@ -1121,7 +1182,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 +1234,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 +1515,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 +1566,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 +1663,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,91 +1704,60 @@ function CONTROLLABLE:TaskRouteToZone( Zone, Randomize, Speed, Formation )
return nil
end
--- (AIR) Return the Controllable to an @{Airbase#AIRBASE}
--- (GROUND) Route the controllable to a given Vec2.
-- 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]
-- @param #Vec2 Vec2 The Vec2 where to route to.
-- @param #number Speed The speed.
-- @param Base#FORMATION Formation The formation string.
function CONTROLLABLE:TaskRouteToVec2( Vec2, Speed, Formation )
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
PointFrom.action = Formation or "Cone"
PointFrom.speed = 20 / 1.6
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
PointTo.x = Vec2.x
PointTo.y = Vec2.y
PointTo.type = "Turning Point"
if Formation then
PointTo.action = Formation
else
PointTo.action = "Cone"
end
if Speed then
PointTo.speed = Speed
else
PointTo.speed = 60 / 3.6
end
local Points = { PointFrom, PointTo }
self:T3( Points )
local Route = { points = Points, }
self:Route( Points )
return Route
return self
end
return nil
end
-- Commands
--- Do Script command
@@ -1767,6 +1797,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 +1874,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 +1882,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
@@ -2191,6 +2228,57 @@ function CONTROLLABLE:OptionROTVertical()
return nil
end
--- Set RTB on bingo fuel.
-- @param #CONTROLLABLE self
-- @param #boolean RTB true if RTB on bingo fuel (default), false if no RTB on bingo fuel.
-- Warning! When you switch this option off, the airborne group will continue to fly until all fuel has been consumed, and will crash.
-- @return #CONTROLLABLE self
function CONTROLLABLE:OptionRTBBingoFuel( RTB ) --R2.2
self:F2( { self.ControllableName } )
RTB = RTB or true
local DCSControllable = self:GetDCSObject()
if DCSControllable then
local Controller = self:_GetController()
if self:IsAir() then
Controller:setOption( AI.Option.Air.id.RTB_ON_BINGO, RTB )
end
return self
end
return nil
end
--- Set RTB on ammo.
-- @param #CONTROLLABLE self
-- @param #boolean WeaponsFlag Weapons.flag enumerator.
-- @return #CONTROLLABLE self
function CONTROLLABLE:OptionRTBAmmo( WeaponsFlag )
self:F2( { self.ControllableName } )
local DCSControllable = self:GetDCSObject()
if DCSControllable then
local Controller = self:_GetController()
if self:IsAir() then
Controller:setOption( AI.Option.GROUND.id.RTB_ON_OUT_OF_AMMO, WeaponsFlag )
end
return self
end
return nil
end
--- Retrieve the controllable mission and allow to place function hooks within the mission waypoint plan.
-- Use the method @{Controllable#CONTROLLABLE:WayPointFunction} to define the hook functions for specific waypoints.
-- Use the method @{Controllable@CONTROLLABLE:WayPointExecute) to start the execution of the new mission plan.
@@ -2258,7 +2346,7 @@ function CONTROLLABLE:TaskFunction( WayPoint, WayPointIndex, FunctionString, Fun
), WayPointIndex
)
self:T3( DCSTask )
self:T( DCSTask )
return DCSTask
@@ -2290,4 +2378,32 @@ function CONTROLLABLE:WayPointExecute( WayPoint, WaitTime )
return self
end
--- Returns if the Controllable contains AirPlanes.
-- @param #CONTROLLABLE self
-- @return #boolean true if Controllable contains AirPlanes.
function CONTROLLABLE:IsAirPlane()
self:F2()
local DCSObject = self:GetDCSObject()
if DCSObject then
local Category = DCSObject:getDesc().category
return Category == Unit.Category.AIRPLANE
end
return nil
end
function CONTROLLABLE:GetSize()
local DCSObject = self:GetDCSObject()
if DCSObject then
return 1
else
return 0
end
end
-- Message APIs

View File

@@ -1,8 +1,8 @@
--- This module contains the GROUP class.
--- **Wrapper** -- GROUP wraps the DCS Class Group objects.
--
-- 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,8 +11,29 @@
--
-- **IMPORTANT: ONE SHOULD NEVER SANATIZE these GROUP OBJECT REFERENCES! (make the GROUP object references nil).**
--
-- 1.1) GROUP reference methods
-- -----------------------
-- See the detailed documentation on the GROUP class.
--
-- ====
--
-- ### Author: **Sven Van de Velde (FlightControl)**
--
-- ### Contributions:
--
-- * [**Entropy**](https://forums.eagle.ru/member.php?u=111471), **Afinegan**: Came up with the requirement for AIOnOff().
--
-- ====
--
-- @module 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).
--
@@ -28,25 +49,25 @@
-- * @{#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
-- ## 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
-- ### 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.
--
-- ## 1.3) GROUP Command methods
-- ## 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
-- ## 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
-- ## 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:
@@ -57,60 +78,44 @@
--
-- 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
-- ## 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**
--
-- 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-24: GROUP:**SetAIOnOff( AIOnOff )** added.
--
-- 2017-01-24: GROUP:**SetAIOn()** added.
--
-- 2017-01-24: GROUP:**SetAIOff()** added.
--
-- ===
--
-- # **AUTHORS and CONTRIBUTIONS**
--
-- ### Contributions:
--
-- * [**Entropy**](https://forums.eagle.ru/member.php?u=111471), **Afinegan**: Came up with the requirement for AIOnOff().
--
-- ### Authors:
--
-- * **FlightControl**: Design & Programming
--
-- @module Group
-- @author FlightControl
--- The GROUP class
-- @type GROUP
-- @extends Wrapper.Controllable#CONTROLLABLE
-- @field #string GroupName The name of the group.
-- @field #GROUP GROUP
GROUP = {
ClassName = "GROUP",
}
--- Enumerator for location at airbases
-- @type GROUP.Takeoff
GROUP.Takeoff = {
Air = 1,
Runway = 2,
Hot = 3,
Cold = 4,
}
GROUPTEMPLATE = {}
GROUPTEMPLATE.Takeoff = {
[GROUP.Takeoff.Air] = "Turning Point",
[GROUP.Takeoff.Runway] = "TakeOff",
[GROUP.Takeoff.Hot] = "TakeOffParkingHot",
[GROUP.Takeoff.Cold] = "TakeOffParking",
}
--- Create a new GROUP from a DCSGroup
-- @param #GROUP self
-- @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 +179,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 +247,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 +315,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
@@ -333,8 +353,13 @@ function GROUP:GetSize()
if DCSGroup then
local GroupSize = DCSGroup:getSize()
self:T3( GroupSize )
return GroupSize
if GroupSize then
self:T3( GroupSize )
return GroupSize
else
return 0
end
end
return nil
@@ -402,6 +427,24 @@ function GROUP:GetTypeName()
return nil
end
--- Gets the player name of the group.
-- @param #GROUP self
-- @return #string The player name of the group.
function GROUP:GetPlayerName()
self:F2( self.GroupName )
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local PlayerName = DCSGroup:getUnit(1):getPlayerName()
self:T3( PlayerName )
return( PlayerName )
end
return nil
end
--- Gets the CallSign of the first DCS Unit of the DCS Group.
-- @param #GROUP self
-- @return #string The CallSign of the first DCS Unit of the DCS Group.
@@ -433,6 +476,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 +486,84 @@ 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 COORDINATE object indicating the point of the first UNIT of the GROUP within the mission.
-- @param Wrapper.Group#GROUP self
-- @return Core.Point#COORDINATE The COORDINATE of the GROUP.
-- @return #nil The POSITIONABLE is not existing or alive.
function GROUP:GetCoordinate()
self:F2( self.PositionableName )
local FirstUnit = self:GetUnit(1)
if FirstUnit then
local FirstUnitCoordinate = FirstUnit:GetCoordinate()
self:T3(FirstUnitCoordinate)
return FirstUnitCoordinate
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 +588,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 +628,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
@@ -721,29 +869,35 @@ end
-- @param #table Template The template of the Group retrieved with GROUP:GetTemplate()
function GROUP:Respawn( Template )
local Vec3 = self:GetVec3()
Template.x = Vec3.x
Template.y = Vec3.z
--Template.x = nil
--Template.y = nil
self:E( #Template.units )
for UnitID, UnitData in pairs( self:GetUnits() ) do
local GroupUnit = UnitData -- Wrapper.Unit#UNIT
self:E( GroupUnit:GetName() )
if GroupUnit:IsAlive() then
local GroupUnitVec3 = GroupUnit:GetVec3()
local GroupUnitHeading = GroupUnit:GetHeading()
Template.units[UnitID].alt = GroupUnitVec3.y
Template.units[UnitID].x = GroupUnitVec3.x
Template.units[UnitID].y = GroupUnitVec3.z
Template.units[UnitID].heading = GroupUnitHeading
self:E( { UnitID, Template.units[UnitID], Template.units[UnitID] } )
if self:IsAlive() then
local Vec3 = self:GetVec3()
Template.x = Vec3.x
Template.y = Vec3.z
--Template.x = nil
--Template.y = nil
self:E( #Template.units )
for UnitID, UnitData in pairs( self:GetUnits() ) do
local GroupUnit = UnitData -- Wrapper.Unit#UNIT
self:E( GroupUnit:GetName() )
if GroupUnit:IsAlive() then
local GroupUnitVec3 = GroupUnit:GetVec3()
local GroupUnitHeading = GroupUnit:GetHeading()
Template.units[UnitID].alt = GroupUnitVec3.y
Template.units[UnitID].x = GroupUnitVec3.x
Template.units[UnitID].y = GroupUnitVec3.z
Template.units[UnitID].heading = GroupUnitHeading
self:E( { UnitID, Template.units[UnitID], Template.units[UnitID] } )
end
end
end
self:Destroy()
_DATABASE:Spawn( Template )
self:ResetEvents()
end
--- Returns the group template from the @{DATABASE} (_DATABASE object).
@@ -892,9 +1046,236 @@ 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():RemoveEvent( 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 = {}
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
--do -- Smoke
--
----- Signal a flare at the position of the GROUP.
---- @param #GROUP self
---- @param Utilities.Utils#FLARECOLOR FlareColor
--function GROUP:Flare( FlareColor )
-- self:F2()
-- trigger.action.signalFlare( self:GetVec3(), FlareColor , 0 )
--end
--
----- Signal a white flare at the position of the GROUP.
---- @param #GROUP self
--function GROUP:FlareWhite()
-- self:F2()
-- trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.White , 0 )
--end
--
----- Signal a yellow flare at the position of the GROUP.
---- @param #GROUP self
--function GROUP:FlareYellow()
-- self:F2()
-- trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.Yellow , 0 )
--end
--
----- Signal a green flare at the position of the GROUP.
---- @param #GROUP self
--function GROUP:FlareGreen()
-- self:F2()
-- trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.Green , 0 )
--end
--
----- Signal a red flare at the position of the GROUP.
---- @param #GROUP self
--function GROUP:FlareRed()
-- self:F2()
-- local Vec3 = self:GetVec3()
-- if Vec3 then
-- trigger.action.signalFlare( Vec3, trigger.flareColor.Red, 0 )
-- end
--end
--
----- Smoke the GROUP.
---- @param #GROUP self
--function GROUP:Smoke( SmokeColor, Range )
-- self:F2()
-- if Range then
-- trigger.action.smoke( self:GetRandomVec3( Range ), SmokeColor )
-- else
-- trigger.action.smoke( self:GetVec3(), SmokeColor )
-- end
--
--end
--
----- Smoke the GROUP Green.
---- @param #GROUP self
--function GROUP:SmokeGreen()
-- self:F2()
-- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Green )
--end
--
----- Smoke the GROUP Red.
---- @param #GROUP self
--function GROUP:SmokeRed()
-- self:F2()
-- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Red )
--end
--
----- Smoke the GROUP White.
---- @param #GROUP self
--function GROUP:SmokeWhite()
-- self:F2()
-- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.White )
--end
--
----- Smoke the GROUP Orange.
---- @param #GROUP self
--function GROUP:SmokeOrange()
-- self:F2()
-- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Orange )
--end
--
----- Smoke the GROUP Blue.
---- @param #GROUP self
--function GROUP:SmokeBlue()
-- self:F2()
-- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Blue )
--end
--
--
--
--end

View File

@@ -1,39 +1,34 @@
--- This module contains the IDENTIFIABLE class.
--- **Wrapper** -- IDENTIFIABLE is an intermediate class wrapping DCS Object class derived Objects.
--
-- 1) @{#IDENTIFIABLE} class, extends @{Object#OBJECT}
-- ===============================================================
-- The @{#IDENTIFIABLE} class is a wrapper class to handle the DCS Identifiable objects:
-- ====
--
-- ### Author: **Sven Van de Velde (FlightControl)**
--
-- ### Contributions:
--
-- ====
--
-- @module Identifiable
--- @type IDENTIFIABLE
-- @extends Wrapper.Object#OBJECT
-- @field #string IdentifiableName The name of the identifiable.
--- # IDENTIFIABLE class, extends @{Object#OBJECT}
--
-- The IDENTIFIABLE class is a wrapper class to handle the DCS Identifiable objects:
--
-- * Support all DCS Identifiable APIs.
-- * Enhance with Identifiable specific APIs not in the DCS Identifiable API set.
-- * Manage the "state" of the DCS Identifiable.
--
-- 1.1) IDENTIFIABLE constructor:
-- ------------------------------
-- ## IDENTIFIABLE constructor
--
-- The IDENTIFIABLE class provides the following functions to construct a IDENTIFIABLE instance:
--
-- * @{#IDENTIFIABLE.New}(): Create a IDENTIFIABLE instance.
--
-- 1.2) IDENTIFIABLE methods:
-- --------------------------
-- The following methods can be used to identify an identifiable object:
--
-- * @{#IDENTIFIABLE.GetName}(): Returns the name of the Identifiable.
-- * @{#IDENTIFIABLE.IsAlive}(): Returns if the Identifiable is alive.
-- * @{#IDENTIFIABLE.GetTypeName}(): Returns the type name of the Identifiable.
-- * @{#IDENTIFIABLE.GetCoalition}(): Returns the coalition of the Identifiable.
-- * @{#IDENTIFIABLE.GetCountry}(): Returns the country of the Identifiable.
-- * @{#IDENTIFIABLE.GetDesc}(): Returns the descriptor structure of the Identifiable.
--
--
-- ===
--
-- @module Identifiable
--- The IDENTIFIABLE class
-- @type IDENTIFIABLE
-- @extends Wrapper.Object#OBJECT
-- @field #string IdentifiableName The name of the identifiable.
-- @field #IDENTIFIABLE
IDENTIFIABLE = {
ClassName = "IDENTIFIABLE",
IdentifiableName = "",
@@ -58,17 +53,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,33 +1,36 @@
--- This module contains the OBJECT class.
--- **Wrapper** -- OBJECT wraps the DCS Object derived objects.
--
-- 1) @{Object#OBJECT} class, extends @{Base#BASE}
-- ===========================================================
-- The @{Object#OBJECT} class is a wrapper class to handle the DCS Object objects:
--
-- * Support all DCS Object APIs.
-- * Enhance with Object specific APIs not in the DCS Object API set.
-- * Manage the "state" of the DCS Object.
--
-- 1.1) OBJECT constructor:
-- ------------------------------
-- The OBJECT class provides the following functions to construct a OBJECT instance:
--
-- * @{Object#OBJECT.New}(): Create a OBJECT instance.
--
-- 1.2) OBJECT methods:
-- --------------------------
-- The following methods can be used to identify an Object object:
-- ====
--
-- * @{Object#OBJECT.GetID}(): Returns the ID of the Object object.
-- ### Author: **Sven Van de Velde (FlightControl)**
--
-- ### Contributions:
--
-- ===
--
-- @module Object
--- The OBJECT class
-- @type OBJECT
--- @type OBJECT
-- @extends Core.Base#BASE
-- @field #string ObjectName The name of the Object.
--- # OBJECT class, extends @{Base#BASE}
--
-- OBJECT handles the DCS Object objects:
--
-- * Support all DCS Object APIs.
-- * Enhance with Object specific APIs not in the DCS Object API set.
-- * Manage the "state" of the DCS Object.
--
-- ## OBJECT constructor:
--
-- The OBJECT class provides the following functions to construct a OBJECT instance:
--
-- * @{Object#OBJECT.New}(): Create a OBJECT instance.
--
-- @field #OBJECT
OBJECT = {
ClassName = "OBJECT",
ObjectName = "",

View File

@@ -1,39 +1,60 @@
--- This module contains the POSITIONABLE class.
--- **Wrapper** -- POSITIONABLE wraps DCS classes that are "positionable".
--
-- 1) @{Positionable#POSITIONABLE} class, extends @{Identifiable#IDENTIFIABLE}
-- ===========================================================
-- The @{Positionable#POSITIONABLE} class is a wrapper class to handle the POSITIONABLE objects:
-- ====
--
-- ### Author: **Sven Van de Velde (FlightControl)**
--
-- ### Contributions:
--
-- ====
--
-- @module Positionable
--- @type POSITIONABLE.__ Methods which are not intended for mission designers, but which are used interally by the moose designer :-)
-- @extends Wrapper.Identifiable#IDENTIFIABLE
--- @type POSITIONABLE
-- @extends POSITIONABLE.__
--- # 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.
--
-- 1.1) POSITIONABLE constructor:
-- ------------------------------
-- ## 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.New}(): Create a POSITIONABLE instance.
--
-- * @{Positionable#POSITIONABLE.GetID}(): Returns the ID of the measurable object.
-- * @{Positionable#POSITIONABLE.GetName}(): Returns the name of the measurable object.
-- ## Get the current speed
--
-- ===
-- There are 3 methods that can be used to determine the speed.
-- Use @{#POSITIONABLE.GetVelocityKMH}() to retrieve the current speed in km/h. Use @{#POSITIONABLE.GetVelocityMPS}() to retrieve the speed in meters per second.
-- The method @{#POSITIONABLE.GetVelocity}() returns the speed vector (a Vec3).
--
-- @module Positionable
--- The POSITIONABLE class
-- @type POSITIONABLE
-- @extends Wrapper.Identifiable#IDENTIFIABLE
-- @field #string PositionableName The name of the measurable.
-- ## Get the current altitude
--
-- Altitude can be retrieved using the method @{#POSITIONABLE.GetHeight}() and returns the current altitude in meters from the orthonormal plane.
--
--
-- @field #POSITIONABLE
POSITIONABLE = {
ClassName = "POSITIONABLE",
PositionableName = "",
}
--- @field #POSITIONABLE.__
POSITIONABLE.__ = {}
--- @field #POSITIONABLE.__.Cargo
POSITIONABLE.__.Cargo = {}
--- A DCSPositionable
-- @type DCSPositionable
-- @field id_ The ID of the controllable in DCS
@@ -132,11 +153,36 @@ 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 = POINT_VEC3:NewFromVec3( PositionableVec3 )
PositionableCoordinate:SetHeading( self:GetHeading() )
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 +190,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 +227,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 +293,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,10 +345,32 @@ 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
-- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:GetVelocityKMH()
self:F2( self.PositionableName )
@@ -287,21 +384,59 @@ function POSITIONABLE:GetVelocityKMH()
return Velocity
end
return 0
end
--- Returns the POSITIONABLE velocity in meters per second.
-- @param Wrapper.Positionable#POSITIONABLE self
-- @return #number The velocity in meters per second.
function POSITIONABLE:GetVelocityMPS()
self:F2( self.PositionableName )
local DCSPositionable = self:GetDCSObject()
if DCSPositionable then
local VelocityVec3 = self:GetVelocity()
local Velocity = ( VelocityVec3.x ^ 2 + VelocityVec3.y ^ 2 + VelocityVec3.z ^ 2 ) ^ 0.5 -- in meters / sec
self:T3( Velocity )
return Velocity
end
return 0
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 +465,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 +557,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 +598,217 @@ 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
--- Add cargo.
-- @param #POSITIONABLE self
-- @param Core.Cargo#CARGO Cargo
-- @return #POSITIONABLE
function POSITIONABLE:AddCargo( Cargo )
self.__.Cargo[Cargo] = Cargo
return self
end
--- Remove cargo.
-- @param #POSITIONABLE self
-- @param Core.Cargo#CARGO Cargo
-- @return #POSITIONABLE
function POSITIONABLE:RemoveCargo( Cargo )
self.__.Cargo[Cargo] = nil
return self
end
--- Returns if carrier has given cargo.
-- @param #POSITIONABLE self
-- @return Core.Cargo#CARGO Cargo
function POSITIONABLE:HasCargo( Cargo )
return self.__.Cargo[Cargo]
end
--- Clear all cargo.
-- @param #POSITIONABLE self
function POSITIONABLE:ClearCargo()
self.__.Cargo = {}
end
--- Get cargo item count.
-- @param #POSITIONABLE self
-- @return Core.Cargo#CARGO Cargo
function POSITIONABLE:CargoItemCount()
local ItemCount = 0
for CargoName, Cargo in pairs( self.__.Cargo ) do
ItemCount = ItemCount + Cargo:GetCount()
end
return ItemCount
end
--- Signal a flare at the position of the POSITIONABLE.
-- @param #POSITIONABLE self
-- @param Utilities.Utils#FLARECOLOR FlareColor
function POSITIONABLE:Flare( FlareColor )
self:F2()
trigger.action.signalFlare( self:GetVec3(), FlareColor , 0 )
end
--- Signal a white flare at the position of the POSITIONABLE.
-- @param #POSITIONABLE self
function POSITIONABLE:FlareWhite()
self:F2()
trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.White , 0 )
end
--- Signal a yellow flare at the position of the POSITIONABLE.
-- @param #POSITIONABLE self
function POSITIONABLE:FlareYellow()
self:F2()
trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.Yellow , 0 )
end
--- Signal a green flare at the position of the POSITIONABLE.
-- @param #POSITIONABLE self
function POSITIONABLE:FlareGreen()
self:F2()
trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.Green , 0 )
end
--- Signal a red flare at the position of the POSITIONABLE.
-- @param #POSITIONABLE self
function POSITIONABLE:FlareRed()
self:F2()
local Vec3 = self:GetVec3()
if Vec3 then
trigger.action.signalFlare( Vec3, trigger.flareColor.Red, 0 )
end
end
--- Smoke the POSITIONABLE.
-- @param #POSITIONABLE self
function POSITIONABLE:Smoke( SmokeColor, Range )
self:F2()
if Range then
trigger.action.smoke( self:GetRandomVec3( Range ), SmokeColor )
else
trigger.action.smoke( self:GetVec3(), SmokeColor )
end
end
--- Smoke the POSITIONABLE Green.
-- @param #POSITIONABLE self
function POSITIONABLE:SmokeGreen()
self:F2()
trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Green )
end
--- Smoke the POSITIONABLE Red.
-- @param #POSITIONABLE self
function POSITIONABLE:SmokeRed()
self:F2()
trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Red )
end
--- Smoke the POSITIONABLE White.
-- @param #POSITIONABLE self
function POSITIONABLE:SmokeWhite()
self:F2()
trigger.action.smoke( self:GetVec3(), trigger.smokeColor.White )
end
--- Smoke the POSITIONABLE Orange.
-- @param #POSITIONABLE self
function POSITIONABLE:SmokeOrange()
self:F2()
trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Orange )
end
--- Smoke the POSITIONABLE Blue.
-- @param #POSITIONABLE self
function POSITIONABLE:SmokeBlue()
self:F2()
trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Blue )
end

View File

@@ -1,22 +1,31 @@
--- This module contains the SCENERY class.
--- **Wrapper** -- SCENERY models scenery within the DCS simulator.
--
-- ====
--
-- ### Author: **Sven Van de Velde (FlightControl)**
--
-- ### Contributions:
--
-- ====
--
-- @module Scenery
--- @type SCENERY
-- @extends Wrapper.Positionable#POSITIONABLE
--- # SCENERY class, extends @{Positionable#POSITIONABLE}
--
-- 1) @{Scenery#SCENERY} class, extends @{Positionable#POSITIONABLE}
-- ===============================================================
-- Scenery objects are defined on the map.
-- The @{Scenery#SCENERY} class is a wrapper class to handle the DCS Scenery objects:
--
-- * Wraps the DCS Scenery objects.
-- * Support all DCS Scenery APIs.
-- * Enhance with Scenery specific APIs not in the DCS API set.
--
-- @module Scenery
-- @author FlightControl
--- The SCENERY class
-- @type SCENERY
-- @extends Wrapper.Positionable#POSITIONABLE
--
-- @field #SCENERY
SCENERY = {
ClassName = "SCENERY",
}

View File

@@ -1,7 +1,21 @@
--- This module contains the STATIC class.
--- **Wrapper** -- STATIC wraps the DCS StaticObject class.
--
-- ====
--
-- ### Author: **Sven Van de Velde (FlightControl)**
--
-- ### Contributions:
--
-- ====
--
-- @module Static
--- @type STATIC
-- @extends Wrapper.Positionable#POSITIONABLE
--- # STATIC class, extends @{Positionable#POSITIONABLE}
--
-- 1) @{Static#STATIC} class, extends @{Positionable#POSITIONABLE}
-- ===============================================================
-- Statics are **Static Units** defined within the Mission Editor.
-- Note that Statics are almost the same as Units, but they don't have a controller.
-- The @{Static#STATIC} class is a wrapper class to handle the DCS Static objects:
@@ -10,8 +24,8 @@
-- * Support all DCS Static APIs.
-- * Enhance with Static specific APIs not in the DCS API set.
--
-- 1.1) STATIC reference methods
-- -----------------------------
-- ## STATIC reference methods
--
-- For each DCS Static will have a STATIC wrapper object (instance) within the _@{DATABASE} object.
-- This is done at the beginning of the mission (when the mission starts).
--
@@ -28,17 +42,7 @@
--
-- IMPORTANT: ONE SHOULD NEVER SANATIZE these STATIC OBJECT REFERENCES! (make the STATIC object references nil).
--
-- @module Static
-- @author FlightControl
--- The STATIC class
-- @type STATIC
-- @extends Wrapper.Positionable#POSITIONABLE
-- @field #STATIC
STATIC = {
ClassName = "STATIC",
}
@@ -48,19 +52,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,23 @@
-- * Handle local Unit Controller.
-- * Manage the "state" of the DCS Unit.
--
--
-- 1.1) UNIT reference methods
-- ----------------------
-- ====
--
-- ### Author: **Sven Van de Velde (FlightControl)**
--
-- ### Contributions:
--
-- ====
--
-- @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 +43,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 +59,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 +228,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 +264,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.
@@ -291,7 +324,8 @@ function UNIT:GetPlayerName()
return PlayerName
end
return nil
return nil
end
--- Returns the unit's number in the group.
@@ -502,7 +536,7 @@ function UNIT:GetLife()
return UnitLife
end
return nil
return -1
end
--- Returns the Unit's initial health.
@@ -519,9 +553,34 @@ function UNIT:GetLife0()
return UnitLife0
end
return 0
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:
--
@@ -539,122 +598,128 @@ end
-- @param #UNIT self
function UNIT:GetThreatLevel()
local Attributes = self:GetDesc().attributes
self:E( Attributes )
local ThreatLevel = 0
local ThreatText = ""
if self:IsGround() then
local Descriptor = self:GetDesc()
self:E( "Ground" )
if Descriptor then
local ThreatLevels = {
"Unarmed",
"Infantry",
"Old Tanks & APCs",
"Tanks & IFVs without ATGM",
"Tanks & IFV with ATGM",
"Modern Tanks",
"AAA",
"IR Guided SAMs",
"SR SAMs",
"MR SAMs",
"LR SAMs"
}
local Attributes = Descriptor.attributes
self:T( Attributes )
if self:IsGround() then
self:T( "Ground" )
if Attributes["LR SAM"] then ThreatLevel = 10
elseif Attributes["MR SAM"] then ThreatLevel = 9
elseif Attributes["SR SAM"] and
not Attributes["IR Guided SAM"] then ThreatLevel = 8
elseif ( Attributes["SR SAM"] or Attributes["MANPADS"] ) and
Attributes["IR Guided SAM"] then ThreatLevel = 7
elseif Attributes["AAA"] then ThreatLevel = 6
elseif Attributes["Modern Tanks"] then ThreatLevel = 5
elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and
Attributes["ATGM"] then ThreatLevel = 4
elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and
not Attributes["ATGM"] then ThreatLevel = 3
elseif Attributes["Old Tanks"] or Attributes["APC"] or Attributes["Artillery"] then ThreatLevel = 2
elseif Attributes["Infantry"] then ThreatLevel = 1
local ThreatLevels = {
"Unarmed",
"Infantry",
"Old Tanks & APCs",
"Tanks & IFVs without ATGM",
"Tanks & IFV with ATGM",
"Modern Tanks",
"AAA",
"IR Guided SAMs",
"SR SAMs",
"MR SAMs",
"LR SAMs"
}
if Attributes["LR SAM"] then ThreatLevel = 10
elseif Attributes["MR SAM"] then ThreatLevel = 9
elseif Attributes["SR SAM"] and
not Attributes["IR Guided SAM"] then ThreatLevel = 8
elseif ( Attributes["SR SAM"] or Attributes["MANPADS"] ) and
Attributes["IR Guided SAM"] then ThreatLevel = 7
elseif Attributes["AAA"] then ThreatLevel = 6
elseif Attributes["Modern Tanks"] then ThreatLevel = 5
elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and
Attributes["ATGM"] then ThreatLevel = 4
elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and
not Attributes["ATGM"] then ThreatLevel = 3
elseif Attributes["Old Tanks"] or Attributes["APC"] or Attributes["Artillery"] then ThreatLevel = 2
elseif Attributes["Infantry"] then ThreatLevel = 1
end
ThreatText = ThreatLevels[ThreatLevel+1]
end
ThreatText = ThreatLevels[ThreatLevel+1]
end
if self:IsAir() then
self:E( "Air" )
local ThreatLevels = {
"Unarmed",
"Tanker",
"AWACS",
"Transport Helicpter",
"UAV",
"Bomber",
"Strategic Bomber",
"Attack Helicopter",
"Interceptor",
"Multirole Fighter",
"Fighter"
}
if self:IsAir() then
if Attributes["Fighters"] then ThreatLevel = 10
elseif Attributes["Multirole fighters"] then ThreatLevel = 9
elseif Attributes["Battleplanes"] then ThreatLevel = 8
elseif Attributes["Attack helicopters"] then ThreatLevel = 7
elseif Attributes["Strategic bombers"] then ThreatLevel = 6
elseif Attributes["Bombers"] then ThreatLevel = 5
elseif Attributes["UAVs"] then ThreatLevel = 4
elseif Attributes["Transport helicopters"] then ThreatLevel = 3
elseif Attributes["AWACS"] then ThreatLevel = 2
elseif Attributes["Tankers"] then ThreatLevel = 1
self:T( "Air" )
local ThreatLevels = {
"Unarmed",
"Tanker",
"AWACS",
"Transport Helicopter",
"UAV",
"Bomber",
"Strategic Bomber",
"Attack Helicopter",
"Battleplane",
"Multirole Fighter",
"Fighter"
}
if Attributes["Fighters"] then ThreatLevel = 10
elseif Attributes["Multirole fighters"] then ThreatLevel = 9
elseif Attributes["Battleplanes"] then ThreatLevel = 8
elseif Attributes["Attack helicopters"] then ThreatLevel = 7
elseif Attributes["Strategic bombers"] then ThreatLevel = 6
elseif Attributes["Bombers"] then ThreatLevel = 5
elseif Attributes["UAVs"] then ThreatLevel = 4
elseif Attributes["Transport helicopters"] then ThreatLevel = 3
elseif Attributes["AWACS"] then ThreatLevel = 2
elseif Attributes["Tankers"] then ThreatLevel = 1
end
ThreatText = ThreatLevels[ThreatLevel+1]
end
ThreatText = ThreatLevels[ThreatLevel+1]
end
if self:IsShip() then
self:E( "Ship" )
--["Aircraft Carriers"] = {"Heavy armed ships",},
--["Cruisers"] = {"Heavy armed ships",},
--["Destroyers"] = {"Heavy armed ships",},
--["Frigates"] = {"Heavy armed ships",},
--["Corvettes"] = {"Heavy armed ships",},
--["Heavy armed ships"] = {"Armed ships", "Armed Air Defence", "HeavyArmoredUnits",},
--["Light armed ships"] = {"Armed ships","NonArmoredUnits"},
--["Armed ships"] = {"Ships"},
--["Unarmed ships"] = {"Ships","HeavyArmoredUnits",},
local ThreatLevels = {
"Unarmed ship",
"Light armed ships",
"Corvettes",
"",
"Frigates",
"",
"Cruiser",
"",
"Destroyer",
"",
"Aircraft Carrier"
}
if self:IsShip() then
self:T( "Ship" )
--["Aircraft Carriers"] = {"Heavy armed ships",},
--["Cruisers"] = {"Heavy armed ships",},
--["Destroyers"] = {"Heavy armed ships",},
--["Frigates"] = {"Heavy armed ships",},
--["Corvettes"] = {"Heavy armed ships",},
--["Heavy armed ships"] = {"Armed ships", "Armed Air Defence", "HeavyArmoredUnits",},
--["Light armed ships"] = {"Armed ships","NonArmoredUnits"},
--["Armed ships"] = {"Ships"},
--["Unarmed ships"] = {"Ships","HeavyArmoredUnits",},
if Attributes["Aircraft Carriers"] then ThreatLevel = 10
elseif Attributes["Destroyers"] then ThreatLevel = 8
elseif Attributes["Cruisers"] then ThreatLevel = 6
elseif Attributes["Frigates"] then ThreatLevel = 4
elseif Attributes["Corvettes"] then ThreatLevel = 2
elseif Attributes["Light armed ships"] then ThreatLevel = 1
local ThreatLevels = {
"Unarmed ship",
"Light armed ships",
"Corvettes",
"",
"Frigates",
"",
"Cruiser",
"",
"Destroyer",
"",
"Aircraft Carrier"
}
if Attributes["Aircraft Carriers"] then ThreatLevel = 10
elseif Attributes["Destroyers"] then ThreatLevel = 8
elseif Attributes["Cruisers"] then ThreatLevel = 6
elseif Attributes["Frigates"] then ThreatLevel = 4
elseif Attributes["Corvettes"] then ThreatLevel = 2
elseif Attributes["Light armed ships"] then ThreatLevel = 1
end
ThreatText = ThreatLevels[ThreatLevel+1]
end
ThreatText = ThreatLevels[ThreatLevel+1]
end
self:T2( ThreatLevel )
@@ -675,7 +740,7 @@ function UNIT:IsInZone( Zone )
if self:IsAlive() then
local IsInZone = Zone:IsVec3InZone( self:GetVec3() )
self:T( { IsInZone } )
self:T2( { IsInZone } )
return IsInZone
end
@@ -729,91 +794,7 @@ end
--- Signal a flare at the position of the UNIT.
-- @param #UNIT self
-- @param Utilities.Utils#FLARECOLOR FlareColor
function UNIT:Flare( FlareColor )
self:F2()
trigger.action.signalFlare( self:GetVec3(), FlareColor , 0 )
end
--- Signal a white flare at the position of the UNIT.
-- @param #UNIT self
function UNIT:FlareWhite()
self:F2()
trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.White , 0 )
end
--- Signal a yellow flare at the position of the UNIT.
-- @param #UNIT self
function UNIT:FlareYellow()
self:F2()
trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.Yellow , 0 )
end
--- Signal a green flare at the position of the UNIT.
-- @param #UNIT self
function UNIT:FlareGreen()
self:F2()
trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.Green , 0 )
end
--- Signal a red flare at the position of the UNIT.
-- @param #UNIT self
function UNIT:FlareRed()
self:F2()
local Vec3 = self:GetVec3()
if Vec3 then
trigger.action.signalFlare( Vec3, trigger.flareColor.Red, 0 )
end
end
--- Smoke the UNIT.
-- @param #UNIT self
function UNIT:Smoke( SmokeColor, Range )
self:F2()
if Range then
trigger.action.smoke( self:GetRandomVec3( Range ), SmokeColor )
else
trigger.action.smoke( self:GetVec3(), SmokeColor )
end
end
--- Smoke the UNIT Green.
-- @param #UNIT self
function UNIT:SmokeGreen()
self:F2()
trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Green )
end
--- Smoke the UNIT Red.
-- @param #UNIT self
function UNIT:SmokeRed()
self:F2()
trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Red )
end
--- Smoke the UNIT White.
-- @param #UNIT self
function UNIT:SmokeWhite()
self:F2()
trigger.action.smoke( self:GetVec3(), trigger.smokeColor.White )
end
--- Smoke the UNIT Orange.
-- @param #UNIT self
function UNIT:SmokeOrange()
self:F2()
trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Orange )
end
--- Smoke the UNIT Blue.
-- @param #UNIT self
function UNIT:SmokeBlue()
self:F2()
trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Blue )
end
-- Is methods
@@ -946,5 +927,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,70 @@
Utilities/Routines.lua
Utilities/Utils.lua
Core/Base.lua
Core/Scheduler.lua
Core/ScheduleDispatcher.lua
Core/Event.lua
Core/Settings.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_A2A.lua
AI/AI_A2A_Patrol.lua
AI/AI_A2A_Cap.lua
AI/AI_A2A_Gci.lua
AI/AI_A2A_Dispatcher.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_A2A_Dispatcher.lua
Tasking/Task_A2A.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.

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