mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-08-15 10:47:21 +00:00
Compare commits
1026 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e0ab3507c | ||
|
|
92a70d07d9 | ||
|
|
8db5351ad7 | ||
|
|
25a6cbcf6d | ||
|
|
ac7ae68ee8 | ||
|
|
230a803045 | ||
|
|
d61840d834 | ||
|
|
f74520afd3 | ||
|
|
74668bb7db | ||
|
|
bca964aca8 | ||
|
|
ad9e97f192 | ||
|
|
aac5efbf61 | ||
|
|
6b31ad9645 | ||
|
|
0097316333 | ||
|
|
a0adca43f1 | ||
|
|
6f0507ea7f | ||
|
|
1159d11a12 | ||
|
|
9ee21f80ac | ||
|
|
f9d7eea721 | ||
|
|
191fcb25be | ||
|
|
a1eb0ff4d9 | ||
|
|
0600c96282 | ||
|
|
95d7b8250d | ||
|
|
fc852e0386 | ||
|
|
9429ec66ca | ||
|
|
92c3b530cc | ||
|
|
dd636939bb | ||
|
|
6554fa55d1 | ||
|
|
3157a63b7e | ||
|
|
d9222c23cb | ||
|
|
16d4a65569 | ||
|
|
a2c12dc05e | ||
|
|
c36579f88a | ||
|
|
19b3dcec21 | ||
|
|
ba944444da | ||
|
|
f33856cddd | ||
|
|
57c5ab1ecd | ||
|
|
0cf4b8845e | ||
|
|
4fccfa38d4 | ||
|
|
531c1d7e90 | ||
|
|
810d900d7e | ||
|
|
274b44459e | ||
|
|
394b5f5b89 | ||
|
|
e5268a29cf | ||
|
|
b14a672b0e | ||
|
|
0ec3192fb7 | ||
|
|
33271edf78 | ||
|
|
441fba0830 | ||
|
|
7dbc9436ed | ||
|
|
c999389cda | ||
|
|
462564cd01 | ||
|
|
c2a968d2ef | ||
|
|
2297646873 | ||
|
|
af050629aa | ||
|
|
3757eb06d9 | ||
|
|
06b555a50a | ||
|
|
b9eab34d6a | ||
|
|
1444a613c5 | ||
|
|
ecf3434c3d | ||
|
|
5988ceec05 | ||
|
|
f01b2c9149 | ||
|
|
b33dd94fa0 | ||
|
|
47dd73a377 | ||
|
|
25ae0c3d15 | ||
|
|
3389c908d7 | ||
|
|
3e6017c0d5 | ||
|
|
fea2f55fbb | ||
|
|
dcc42b8e62 | ||
|
|
9cea486fdc | ||
|
|
faa934ffce | ||
|
|
1ae1b9abd7 | ||
|
|
ffe4d9a143 | ||
|
|
d5d0703502 | ||
|
|
fe3e91d1a0 | ||
|
|
1beb34231e | ||
|
|
a6889be676 | ||
|
|
4eb596f5bf | ||
|
|
01de126a8f | ||
|
|
86cc1f81b6 | ||
|
|
a95afe9956 | ||
|
|
8b8fcaaacd | ||
|
|
31f0bb9fef | ||
|
|
af23aa3b79 | ||
|
|
e606ab1b8a | ||
|
|
d792061df8 | ||
|
|
689f9fd8e9 | ||
|
|
303f5cb571 | ||
|
|
b1ecdc727c | ||
|
|
7735120f25 | ||
|
|
a247f56c7e | ||
|
|
718138abd6 | ||
|
|
269b52fd0e | ||
|
|
fd34bab65a | ||
|
|
e2df7f9732 | ||
|
|
ed0c5b2264 | ||
|
|
2ebd25d677 | ||
|
|
376eeb63c5 | ||
|
|
5971f6de09 | ||
|
|
a586d81f68 | ||
|
|
c60dda2545 | ||
|
|
45d1cb48bb | ||
|
|
0d995d1832 | ||
|
|
e094c8133a | ||
|
|
1f578d4ab5 | ||
|
|
ec973fcc76 | ||
|
|
25831db057 | ||
|
|
d0925ddfc1 | ||
|
|
a4d3089fdb | ||
|
|
a94e744028 | ||
|
|
b415039947 | ||
|
|
9de6993a4c | ||
|
|
b3facc6e88 | ||
|
|
10ebc0b1e7 | ||
|
|
1401bb29aa | ||
|
|
9688c606f0 | ||
|
|
c4ba2c0235 | ||
|
|
35f18d0d1f | ||
|
|
93640b1d8e | ||
|
|
75d179176d | ||
|
|
c37560275e | ||
|
|
812902b009 | ||
|
|
b29ce9b45e | ||
|
|
31a7a4e993 | ||
|
|
7a8881974c | ||
|
|
ea069455d3 | ||
|
|
f03807207d | ||
|
|
59bf7fdc96 | ||
|
|
6c26f2cd69 | ||
|
|
2caada0119 | ||
|
|
7a579a0ab5 | ||
|
|
cf6b7365af | ||
|
|
272308cf98 | ||
|
|
747777b297 | ||
|
|
20594ad294 | ||
|
|
b4b2034792 | ||
|
|
e22e7f2c58 | ||
|
|
12fcb99218 | ||
|
|
b1a1c6c552 | ||
|
|
61e2e8ca6b | ||
|
|
21a7bac4e0 | ||
|
|
12c4e142e9 | ||
|
|
9759640d52 | ||
|
|
395923eb07 | ||
|
|
727d64927b | ||
|
|
b6fc46fdd0 | ||
|
|
e7518d69e6 | ||
|
|
cce90b1f46 | ||
|
|
a15ef93e7c | ||
|
|
08cc4e3530 | ||
|
|
7d1d165a5a | ||
|
|
fa92615f5d | ||
|
|
c4f2446b92 | ||
|
|
7088c4c426 | ||
|
|
7963f04bdc | ||
|
|
ca32c57c52 | ||
|
|
bd4ad42fcf | ||
|
|
c4ff6d7bfd | ||
|
|
25659647a6 | ||
|
|
e2ffbba04d | ||
|
|
4bc1cd1b51 | ||
|
|
95eb88e67e | ||
|
|
089306b36d | ||
|
|
c59ed155e6 | ||
|
|
0d8f3d25e9 | ||
|
|
cbb800de02 | ||
|
|
52d783a0b7 | ||
|
|
7a4a3217df | ||
|
|
af2299d392 | ||
|
|
24a3298f2e | ||
|
|
5188c40701 | ||
|
|
e01a4fa18c | ||
|
|
de5a283e50 | ||
|
|
89e6672db1 | ||
|
|
fa15d170f7 | ||
|
|
2baa42e2bc | ||
|
|
44160dfa29 | ||
|
|
a283290c06 | ||
|
|
706aea01ca | ||
|
|
fc7e15521d | ||
|
|
7a5c8d54e7 | ||
|
|
136a7c2a5b | ||
|
|
eebd247fdd | ||
|
|
284a1853ff | ||
|
|
2b89a4b5b1 | ||
|
|
8c6482632b | ||
|
|
51c233956c | ||
|
|
c2e575ef20 | ||
|
|
7c2c6daf3e | ||
|
|
9d100e9bc1 | ||
|
|
7b5136eabf | ||
|
|
af7a87e5a0 | ||
|
|
a18d432141 | ||
|
|
011a5b94fd | ||
|
|
d672983c11 | ||
|
|
9be9277a08 | ||
|
|
5976b92281 | ||
|
|
0f18b29144 | ||
|
|
533689826e | ||
|
|
62eb36c456 | ||
|
|
34265720cc | ||
|
|
38d267b2bc | ||
|
|
251302839e | ||
|
|
285c05b6ba | ||
|
|
bdc66058ab | ||
|
|
df6bc598a4 | ||
|
|
c5c437fa50 | ||
|
|
650f16084b | ||
|
|
949178d698 | ||
|
|
e57d05fc91 | ||
|
|
92d4bad63f | ||
|
|
58b2802f3a | ||
|
|
e16eb38764 | ||
|
|
f5a2183808 | ||
|
|
2bc7a8c126 | ||
|
|
03ba031153 | ||
|
|
7fe333cc35 | ||
|
|
6f1e483fd7 | ||
|
|
01c18278fc | ||
|
|
5120088b66 | ||
|
|
f82a0c092a | ||
|
|
296d1dbc2b | ||
|
|
825f4f2fac | ||
|
|
53980423a9 | ||
|
|
fe01c03037 | ||
|
|
96ca3eeb44 | ||
|
|
309ba285b1 | ||
|
|
f2cb750aa2 | ||
|
|
3544f07169 | ||
|
|
75f5cf9ac5 | ||
|
|
a780f6635f | ||
|
|
ae2e99a560 | ||
|
|
34592a53be | ||
|
|
370278e643 | ||
|
|
778ab58eee | ||
|
|
bf9d6cbd75 | ||
|
|
6bcbffb37f | ||
|
|
68c701afc6 | ||
|
|
d11de24866 | ||
|
|
5c7caadab7 | ||
|
|
8bbff46efd | ||
|
|
0865390284 | ||
|
|
6dfba37fe9 | ||
|
|
0147c3a17c | ||
|
|
03c38356ce | ||
|
|
5e5dab99eb | ||
|
|
9f17382145 | ||
|
|
43336ae431 | ||
|
|
fed937c7b7 | ||
|
|
734f624a9e | ||
|
|
a518af1200 | ||
|
|
d3fc84fe8c | ||
|
|
a5e2309409 | ||
|
|
760bdd2016 | ||
|
|
733b4940f8 | ||
|
|
2620927146 | ||
|
|
96216494bb | ||
|
|
67b5d0ca1c | ||
|
|
296b06e0b0 | ||
|
|
4e28ed3ee6 | ||
|
|
08ad1a0bce | ||
|
|
d935cbaea2 | ||
|
|
f7a5fc7d92 | ||
|
|
6329ac9262 | ||
|
|
4649e2f823 | ||
|
|
06df068d6b | ||
|
|
0dd4676879 | ||
|
|
17fc554beb | ||
|
|
510cef1d59 | ||
|
|
dc8d9ddfdd | ||
|
|
ed0f84da61 | ||
|
|
e0aa16dbd5 | ||
|
|
7ff664b05d | ||
|
|
930768e2e3 | ||
|
|
c1278993aa | ||
|
|
7145fef233 | ||
|
|
0a118cf264 | ||
|
|
344871d727 | ||
|
|
43b2f8c3ff | ||
|
|
85f0fe758b | ||
|
|
bb2a1a82a8 | ||
|
|
b2a7c994bc | ||
|
|
83044af064 | ||
|
|
abcbbdfb79 | ||
|
|
dc75002723 | ||
|
|
3ce5d71fc9 | ||
|
|
0764affd49 | ||
|
|
8507a85915 | ||
|
|
0dcd414c24 | ||
|
|
5ab050b237 | ||
|
|
7eedd7cbf1 | ||
|
|
0e0b1f470b | ||
|
|
beba6ee751 | ||
|
|
2d1409dedb | ||
|
|
85cb0b40f8 | ||
|
|
eb9fc7589f | ||
|
|
a334511601 | ||
|
|
646958cc1a | ||
|
|
cd414a8129 | ||
|
|
857ebfab44 | ||
|
|
039491a3e1 | ||
|
|
051733ac97 | ||
|
|
f2774dbf71 | ||
|
|
6f6db6d26f | ||
|
|
f69f666deb | ||
|
|
5134de9f28 | ||
|
|
b3267c00e9 | ||
|
|
282bf8bd07 | ||
|
|
b52b5b78c9 | ||
|
|
9bca76ddcf | ||
|
|
77cddfaeb4 | ||
|
|
ee6a553b6b | ||
|
|
2e195e6dca | ||
|
|
f615d2c50a | ||
|
|
e54fad6c53 | ||
|
|
7b0c0a0c8b | ||
|
|
4efe03b07b | ||
|
|
030bd92148 | ||
|
|
0f4b3e0d88 | ||
|
|
abf0de49d1 | ||
|
|
e7d48f335c | ||
|
|
ee4f7e843d | ||
|
|
4157ede997 | ||
|
|
54450c9933 | ||
|
|
f545d5d31f | ||
|
|
8332ba5112 | ||
|
|
f35d78c11c | ||
|
|
93f03a1e8b | ||
|
|
c018a6e13f | ||
|
|
b8c1135b3b | ||
|
|
abbb59efb9 | ||
|
|
ce61e8d859 | ||
|
|
ee698722a6 | ||
|
|
be0856c9e1 | ||
|
|
e04b434fcb | ||
|
|
95112e8818 | ||
|
|
09d02e18cf | ||
|
|
2ce1057d71 | ||
|
|
e048ea995e | ||
|
|
d66b7b8c20 | ||
|
|
37dc49c14f | ||
|
|
ef1290015e | ||
|
|
02c671bd63 | ||
|
|
18a15332fe | ||
|
|
d733221317 | ||
|
|
e036ec0adf | ||
|
|
f18b18187b | ||
|
|
3135639557 | ||
|
|
5eeafd2fba | ||
|
|
0577e7ee70 | ||
|
|
d9a4c63011 | ||
|
|
7fd2500db4 | ||
|
|
822920e77e | ||
|
|
85e2811c79 | ||
|
|
757b2cb6a2 | ||
|
|
15c09d9880 | ||
|
|
64b6f52a2d | ||
|
|
27159c4234 | ||
|
|
d38c2540c2 | ||
|
|
901f460907 | ||
|
|
a2fa2c4fa2 | ||
|
|
76ea635b63 | ||
|
|
d2a7cc77cc | ||
|
|
c81e9e5a5e | ||
|
|
92c4507a55 | ||
|
|
967d608b94 | ||
|
|
14c075abfc | ||
|
|
ce449c37b1 | ||
|
|
09f5421612 | ||
|
|
3c8244b2aa | ||
|
|
057e528947 | ||
|
|
e9fe11e9b3 | ||
|
|
b7183023c9 | ||
|
|
dd58838983 | ||
|
|
ef5d69032a | ||
|
|
6dcbbec087 | ||
|
|
6c98cf3a09 | ||
|
|
5f6981d309 | ||
|
|
49d3e4e7da | ||
|
|
060a1b219f | ||
|
|
4db18a4846 | ||
|
|
42a9e4c60d | ||
|
|
f3bd097e6f | ||
|
|
1848e117aa | ||
|
|
131db74630 | ||
|
|
46e76a3833 | ||
|
|
7ff06b5ef8 | ||
|
|
f65238efe6 | ||
|
|
0a819f254a | ||
|
|
7c7722efe6 | ||
|
|
e229e2e381 | ||
|
|
ac0d2fa92c | ||
|
|
e9837acda3 | ||
|
|
2e049ccfd4 | ||
|
|
73a1c56532 | ||
|
|
5686f97565 | ||
|
|
c1910646e2 | ||
|
|
dab20a3b88 | ||
|
|
f82d07ebc0 | ||
|
|
e625aaf28c | ||
|
|
7bc0f103d9 | ||
|
|
6a0294e22b | ||
|
|
82fa9ae8b3 | ||
|
|
0c70a34561 | ||
|
|
4618e81039 | ||
|
|
84fee06196 | ||
|
|
f6f2695808 | ||
|
|
cbf0112bd7 | ||
|
|
97be67bae9 | ||
|
|
58a6c43c41 | ||
|
|
d564b0161c | ||
|
|
6e47fd0c46 | ||
|
|
9a90225d40 | ||
|
|
43b5926b74 | ||
|
|
86c7e64018 | ||
|
|
c66117464a | ||
|
|
6f0a254929 | ||
|
|
c495d0e5e9 | ||
|
|
bdf5c1e960 | ||
|
|
86ad985e0b | ||
|
|
abf84e121f | ||
|
|
212c674443 | ||
|
|
9965d8284e | ||
|
|
3ac649d6e8 | ||
|
|
0e4a5c02d5 | ||
|
|
8a4a37ac11 | ||
|
|
74951f4237 | ||
|
|
8aa14428dc | ||
|
|
8002febf09 | ||
|
|
7da932e048 | ||
|
|
6923cfe143 | ||
|
|
06283ad9e3 | ||
|
|
f4a0b83619 | ||
|
|
70e5ce30bb | ||
|
|
3b3017aa1d | ||
|
|
6c5dcb068b | ||
|
|
ea7d4e4ab8 | ||
|
|
a8c5ccd4ad | ||
|
|
e41b038730 | ||
|
|
172e51307c | ||
|
|
ccb4d7f7b5 | ||
|
|
140f81b695 | ||
|
|
81d724d881 | ||
|
|
b7528dad2e | ||
|
|
63ba44dca2 | ||
|
|
8ea8702140 | ||
|
|
b923159298 | ||
|
|
79afc5a856 | ||
|
|
4d8179ec70 | ||
|
|
224f0694d8 | ||
|
|
57b4838a5a | ||
|
|
a204dd2f4b | ||
|
|
7cab0ca22a | ||
|
|
3e16e5fa51 | ||
|
|
210ad2154c | ||
|
|
bce2e3b922 | ||
|
|
a8e77bddd4 | ||
|
|
d8eb7ce097 | ||
|
|
d0107d5cee | ||
|
|
a82be92577 | ||
|
|
730cd92d51 | ||
|
|
d3b5c77e5c | ||
|
|
f1b7ae7643 | ||
|
|
f952cd4bb5 | ||
|
|
32e61da588 | ||
|
|
c27197500c | ||
|
|
b5fbe6d55e | ||
|
|
291df87beb | ||
|
|
a661c6e711 | ||
|
|
e1d12cbd8e | ||
|
|
1e2a84608f | ||
|
|
88260ae4f3 | ||
|
|
3a8c1f97f1 | ||
|
|
0cc36b5ee2 | ||
|
|
d5c7d0028b | ||
|
|
3a0f60adc9 | ||
|
|
a575dfea7d | ||
|
|
515cf70295 | ||
|
|
fbabc54e03 | ||
|
|
059754fc28 | ||
|
|
cbc0579c79 | ||
|
|
da0bf650fa | ||
|
|
4de8bc742f | ||
|
|
93ba003e5b | ||
|
|
da476b29a6 | ||
|
|
5ee9633dc6 | ||
|
|
6d2e8d34fb | ||
|
|
a35e95a7dc | ||
|
|
126810a273 | ||
|
|
58e3e5293e | ||
|
|
8eff6493ec | ||
|
|
0227549207 | ||
|
|
ddf45d8485 | ||
|
|
6f151a6c5d | ||
|
|
9bfca83804 | ||
|
|
4b74d1b724 | ||
|
|
1067f16ce4 | ||
|
|
5896ebe9ca | ||
|
|
305cb3092e | ||
|
|
04c2a545f2 | ||
|
|
09fd43a3c9 | ||
|
|
d0c6a9756c | ||
|
|
b72f649b91 | ||
|
|
a78275814d | ||
|
|
c4fbdb32c4 | ||
|
|
4be4482957 | ||
|
|
8542823692 | ||
|
|
712b77b590 | ||
|
|
57cb31c86b | ||
|
|
7f04c98d61 | ||
|
|
15ea0bc63a | ||
|
|
50aaeb1465 | ||
|
|
a515385ae0 | ||
|
|
399021502f | ||
|
|
020f097584 | ||
|
|
5c56e75a60 | ||
|
|
f2afd524ef | ||
|
|
ea8ba2f9aa | ||
|
|
421541e88e | ||
|
|
7c26e88345 | ||
|
|
84ddb3e380 | ||
|
|
ffc1c5d6ad | ||
|
|
0a325efeaf | ||
|
|
cd83a0b488 | ||
|
|
fe47783bfa | ||
|
|
6061883194 | ||
|
|
2d6b74ee9e | ||
|
|
feef4c148e | ||
|
|
6952401238 | ||
|
|
454c0e5543 | ||
|
|
1f5030fcbc | ||
|
|
78f4f532f7 | ||
|
|
18de424352 | ||
|
|
36a9295197 | ||
|
|
ca77e2d029 | ||
|
|
93d5327811 | ||
|
|
2224cc7593 | ||
|
|
d5e9c47bad | ||
|
|
e62523786c | ||
|
|
e10913edaf | ||
|
|
4dc1fbaf52 | ||
|
|
5aad27edfc | ||
|
|
0b5d97bf3f | ||
|
|
e1aef42df8 | ||
|
|
f115630546 | ||
|
|
c6e86c494d | ||
|
|
cafcbfde90 | ||
|
|
632ce65bf5 | ||
|
|
b84d08f052 | ||
|
|
57eeefcf06 | ||
|
|
9227bbdfca | ||
|
|
5641d65f71 | ||
|
|
5558c26db7 | ||
|
|
11067d4bfd | ||
|
|
e1f4bdc24b | ||
|
|
bc072d10df | ||
|
|
ec6961fada | ||
|
|
374aae3e7e | ||
|
|
c41b30adc2 | ||
|
|
27e8226330 | ||
|
|
0c55d4d20e | ||
|
|
43a62ebf87 | ||
|
|
0df4b5fd37 | ||
|
|
2fa18ae6c7 | ||
|
|
d77cbff3f8 | ||
|
|
c1f884d024 | ||
|
|
a909e1ee5d | ||
|
|
fa14f4655e | ||
|
|
43cbc93a96 | ||
|
|
ec7cc9e547 | ||
|
|
9226ab9fa9 | ||
|
|
4edc8363e1 | ||
|
|
0f764424e8 | ||
|
|
8c5eb5fb0d | ||
|
|
56813a800c | ||
|
|
df7ffc2a3f | ||
|
|
6799cd776e | ||
|
|
6fc9baee07 | ||
|
|
a9679f831d | ||
|
|
a8e14b5e20 | ||
|
|
c1c148eab4 | ||
|
|
5ae7ee8e1b | ||
|
|
5f8bc4f3bd | ||
|
|
bae6219b7a | ||
|
|
6a725475c9 | ||
|
|
efd2f7938e | ||
|
|
09f61610c1 | ||
|
|
0a2f7c031d | ||
|
|
1ee6b3501f | ||
|
|
0ede10b1a2 | ||
|
|
e0158a9a66 | ||
|
|
27e32486fd | ||
|
|
6b08f6aaac | ||
|
|
ae2be627e3 | ||
|
|
887faacdb1 | ||
|
|
f47ac8baaf | ||
|
|
8d600ca8a4 | ||
|
|
d29d959e47 | ||
|
|
c62cd53e5f | ||
|
|
0cc3249738 | ||
|
|
902dec5233 | ||
|
|
adb4befcdf | ||
|
|
5d62125245 | ||
|
|
ca39a158d7 | ||
|
|
597a62c8ab | ||
|
|
a91be7df58 | ||
|
|
00463f401e | ||
|
|
e177c7e804 | ||
|
|
e545af51f3 | ||
|
|
6d1385a031 | ||
|
|
fa8a9b52fe | ||
|
|
217ded3492 | ||
|
|
eac57ae0a3 | ||
|
|
8a8c496c64 | ||
|
|
f2db40db6e | ||
|
|
c70b587936 | ||
|
|
560f551ed7 | ||
|
|
bdfd03a0b8 | ||
|
|
e1e2d082be | ||
|
|
51e50bee71 | ||
|
|
1baeba251e | ||
|
|
5e0e8f3f73 | ||
|
|
ae4affbf2f | ||
|
|
6a13febf7b | ||
|
|
7a23115cf9 | ||
|
|
5d627d91d8 | ||
|
|
e205af75ca | ||
|
|
4dc468e902 | ||
|
|
5992c852da | ||
|
|
bae0e4c35b | ||
|
|
1fee3eb7a8 | ||
|
|
8b26f7d975 | ||
|
|
fcce06c3f1 | ||
|
|
f628e720a9 | ||
|
|
6959f50777 | ||
|
|
e4e1990657 | ||
|
|
f79143095e | ||
|
|
8c97861e8e | ||
|
|
07878d4b6e | ||
|
|
1d1f8d8a01 | ||
|
|
9dc68fb665 | ||
|
|
c84df9bf5a | ||
|
|
9fc00dd9c3 | ||
|
|
b490412f63 | ||
|
|
27c51f8fe3 | ||
|
|
84b4651cd9 | ||
|
|
d5a21ff604 | ||
|
|
758f500857 | ||
|
|
2830bcb867 | ||
|
|
e6c765c441 | ||
|
|
c2965e0736 | ||
|
|
1f893fe544 | ||
|
|
e6dd040a43 | ||
|
|
0eb0a3a3e7 | ||
|
|
023a7a17c5 | ||
|
|
62d1da8487 | ||
|
|
1c6b760b36 | ||
|
|
e5fdd50cc6 | ||
|
|
6e27b93e45 | ||
|
|
dfd4e3562b | ||
|
|
261faebe31 | ||
|
|
199ecb87bc | ||
|
|
14c7916c55 | ||
|
|
6ff2dfe444 | ||
|
|
bfd0c19109 | ||
|
|
b4c27c270a | ||
|
|
f4e8f15090 | ||
|
|
05d9faedee | ||
|
|
8bcb47a8ee | ||
|
|
ea96a5e0a3 | ||
|
|
9568f7f87f | ||
|
|
e1a730bbe3 | ||
|
|
d26a938ba4 | ||
|
|
62ab859215 | ||
|
|
eac89f784d | ||
|
|
9784b694ba | ||
|
|
5a29b272dc | ||
|
|
61884c07c7 | ||
|
|
8bb3d5a760 | ||
|
|
6a2739da5e | ||
|
|
9289e0dac1 | ||
|
|
5be01775f7 | ||
|
|
5da44baff2 | ||
|
|
02ff2e8efa | ||
|
|
608293f1cb | ||
|
|
9dcda37703 | ||
|
|
1cf2383dfd | ||
|
|
d558c5be21 | ||
|
|
4f2afa29fa | ||
|
|
3742f2937c | ||
|
|
a9ac185034 | ||
|
|
b1e7951a47 | ||
|
|
0aa92372bf | ||
|
|
d7a5f469af | ||
|
|
27f77c5df0 | ||
|
|
49bf6010f8 | ||
|
|
e16e5d9a81 | ||
|
|
3dde62a550 | ||
|
|
8a334b6671 | ||
|
|
6dec92168e | ||
|
|
386777930e | ||
|
|
2aecf45316 | ||
|
|
63866e4aa9 | ||
|
|
2dcc1aaf0a | ||
|
|
82c7121125 | ||
|
|
b2e522aac1 | ||
|
|
5a8d1da54e | ||
|
|
464fb1aeca | ||
|
|
1883e84918 | ||
|
|
d349ed12a9 | ||
|
|
094db73176 | ||
|
|
a86a346378 | ||
|
|
3d2dbea1d7 | ||
|
|
d383c42131 | ||
|
|
cc1b34937c | ||
|
|
2ccfe27401 | ||
|
|
53845448b0 | ||
|
|
b88c84fc3b | ||
|
|
446ecc5b4d | ||
|
|
a928a1c750 | ||
|
|
544b68c51f | ||
|
|
2815e841e0 | ||
|
|
dbe1d7aaa3 | ||
|
|
36ea613f68 | ||
|
|
2611ba0fe8 | ||
|
|
2cf1801f1d | ||
|
|
5233c633a9 | ||
|
|
2501db53b8 | ||
|
|
4b60f776ce | ||
|
|
d8d06a18ce | ||
|
|
9054a493f9 | ||
|
|
9ec29f607f | ||
|
|
616e035e9a | ||
|
|
411636a7f4 | ||
|
|
27b18780f8 | ||
|
|
85bd3a1c33 | ||
|
|
87634969b3 | ||
|
|
5107366e57 | ||
|
|
82a3dd32c0 | ||
|
|
fdcad2dd93 | ||
|
|
3ec783b0e4 | ||
|
|
ea8af14df5 | ||
|
|
906c49792e | ||
|
|
61fe3cf457 | ||
|
|
600166fd80 | ||
|
|
a6830237f4 | ||
|
|
dddcb42e32 | ||
|
|
9c9ed494d9 | ||
|
|
b004929223 | ||
|
|
495786b4eb | ||
|
|
940f872b40 | ||
|
|
227752399b | ||
|
|
a19b41537e | ||
|
|
713e741299 | ||
|
|
c3ee9306f3 | ||
|
|
3924d2d8fc | ||
|
|
ec6e182db8 | ||
|
|
652ed8b178 | ||
|
|
a2630670c0 | ||
|
|
b386c2b5eb | ||
|
|
fce1007fb9 | ||
|
|
b769ad143d | ||
|
|
4d33abb0eb | ||
|
|
a61c6b4fe2 | ||
|
|
1206935886 | ||
|
|
2c16992b5c | ||
|
|
eb73c24367 | ||
|
|
295b482ce6 | ||
|
|
b21cd0c0ae | ||
|
|
beb87f82bf | ||
|
|
4252f9baac | ||
|
|
6f581cadf1 | ||
|
|
f8cca7d510 | ||
|
|
c1bee3a9b0 | ||
|
|
60681d7e23 | ||
|
|
9fe51587a1 | ||
|
|
82fd08521f | ||
|
|
2f416ea98e | ||
|
|
33916c2631 | ||
|
|
a0befeb34f | ||
|
|
0501959ab9 | ||
|
|
764beb7c22 | ||
|
|
1d939311c3 | ||
|
|
47f1b8ae66 | ||
|
|
cff8522922 | ||
|
|
d78547aa33 | ||
|
|
ab33d6b272 | ||
|
|
388103afea | ||
|
|
85975c01a4 | ||
|
|
3fe573926b | ||
|
|
8e2aef17e7 | ||
|
|
367c4d74af | ||
|
|
06e063d594 | ||
|
|
7ebf7a2bee | ||
|
|
b5c53baf67 | ||
|
|
536934390c | ||
|
|
1e6035b282 | ||
|
|
5bbe5fca60 | ||
|
|
4c5aad51b3 | ||
|
|
688875dca5 | ||
|
|
b4c8fbf75a | ||
|
|
edb53013b2 | ||
|
|
70f48a3d53 | ||
|
|
532a311db6 | ||
|
|
71da9933d7 | ||
|
|
9f5b9ab04c | ||
|
|
f76ac1e03a | ||
|
|
b84541f232 | ||
|
|
6115e12309 | ||
|
|
c22bc1c57f | ||
|
|
84055e9798 | ||
|
|
ccfcca8f9a | ||
|
|
c043eef5eb | ||
|
|
2db0265ae6 | ||
|
|
3cd787fb1e | ||
|
|
8825b26b36 | ||
|
|
300ee0a16a | ||
|
|
1283caf80b | ||
|
|
af230d9874 | ||
|
|
f221047eba | ||
|
|
e7b3aa82f9 | ||
|
|
33c6290864 | ||
|
|
9006e17c25 | ||
|
|
22b02cd3ee | ||
|
|
507e4ef25a | ||
|
|
8b1583df30 | ||
|
|
083568d3fd | ||
|
|
8e5af4ada4 | ||
|
|
5d2eb2ea15 | ||
|
|
76ec5aa009 | ||
|
|
35681c6f96 | ||
|
|
d719c437ec | ||
|
|
133910ac3b | ||
|
|
862f2ab3ac | ||
|
|
2f4361c97a | ||
|
|
4b7b042bb1 | ||
|
|
18a76fa355 | ||
|
|
bccc4abf26 | ||
|
|
a021967295 | ||
|
|
975566eb3c | ||
|
|
55164c38bf | ||
|
|
1aeb7b3ff6 | ||
|
|
fa77ba3f48 | ||
|
|
1aecd47d5a | ||
|
|
6b920ea3f4 | ||
|
|
6a2869138e | ||
|
|
1b36cee3b6 | ||
|
|
70f2c0051a | ||
|
|
d396ca1684 | ||
|
|
aed71ca9e2 | ||
|
|
50094fde6c | ||
|
|
d5b66fd08c | ||
|
|
34d2b12acc | ||
|
|
f0c20be967 | ||
|
|
6126ec9450 | ||
|
|
df7c45d7ef | ||
|
|
f52d8a3ad4 | ||
|
|
ab27a1bd2b | ||
|
|
e341287c56 | ||
|
|
ca5247ce1b | ||
|
|
81a8056233 | ||
|
|
a92174f32e | ||
|
|
79ec86f369 | ||
|
|
a31abef3cf | ||
|
|
f89964d8ba | ||
|
|
706a0949ee | ||
|
|
bdfd169d39 | ||
|
|
44f60169cb | ||
|
|
caad080c6c | ||
|
|
c4a2c9edc9 | ||
|
|
ee30fa6ac2 | ||
|
|
d23cf6028b | ||
|
|
6a58290833 | ||
|
|
3b9fdbd5cd | ||
|
|
d88c3106d0 | ||
|
|
34bf013b9b | ||
|
|
2e8efe8f4a | ||
|
|
54c8a6f9dd | ||
|
|
18ddbdac84 | ||
|
|
7c4ee9bebd | ||
|
|
ce397d0a4e | ||
|
|
06c8c00dc9 | ||
|
|
220e5b17aa | ||
|
|
ef217c0b19 | ||
|
|
c72e6ff9b4 | ||
|
|
305584344e | ||
|
|
a42b5fcea7 | ||
|
|
18591c434f | ||
|
|
439ebf1676 | ||
|
|
05d69457f2 | ||
|
|
a646dd900d | ||
|
|
fb54b2f280 | ||
|
|
2f8b881186 | ||
|
|
385dba63d9 | ||
|
|
95ce44f2d8 | ||
|
|
915c65176e | ||
|
|
4f472253de | ||
|
|
c53f8a7033 | ||
|
|
15cf215d49 | ||
|
|
208f214e96 | ||
|
|
e7f83669c4 | ||
|
|
2b6fecbe4d | ||
|
|
3e5542e592 | ||
|
|
1369a28aca | ||
|
|
00933b2905 | ||
|
|
dd39ff4e94 | ||
|
|
922b61b9fe | ||
|
|
5a7551d312 | ||
|
|
94c208cbc9 | ||
|
|
1ea916ec73 | ||
|
|
f56b2229a7 | ||
|
|
4f91ba6081 | ||
|
|
9f22e2cc71 | ||
|
|
e17de754a3 | ||
|
|
18885f0450 | ||
|
|
0f9f615313 | ||
|
|
d84f3fcd24 | ||
|
|
fa6d53634b | ||
|
|
ce24d2b4a6 | ||
|
|
f151e1e5f4 | ||
|
|
59ab62685c | ||
|
|
e68b715321 | ||
|
|
ef95cfb1f5 | ||
|
|
d120875fa9 | ||
|
|
9dfff9ae5e | ||
|
|
86d8eb023d | ||
|
|
b48c467d57 | ||
|
|
cf4c269f77 | ||
|
|
2fb83c89af | ||
|
|
37a176e3ae | ||
|
|
09776a60c9 | ||
|
|
17838d7099 | ||
|
|
531f8a9106 | ||
|
|
a3289205e6 | ||
|
|
0af5e1428b | ||
|
|
7ec9a93231 | ||
|
|
9984055f7d | ||
|
|
4e29565382 | ||
|
|
bc734f1190 | ||
|
|
7d8add6d4c | ||
|
|
ec8a399ca6 | ||
|
|
1935bd235e | ||
|
|
c6631356ea | ||
|
|
f2e966735c | ||
|
|
35c2cb45bb | ||
|
|
33fcb86383 | ||
|
|
8096c170d5 | ||
|
|
333eba2cb8 | ||
|
|
3cb6bd3a99 | ||
|
|
f8ab65ce0e | ||
|
|
707a5a778a | ||
|
|
6f183bad74 | ||
|
|
28a38d04fd | ||
|
|
051cc4955f | ||
|
|
e06b2c5e4f | ||
|
|
8157d7a8d0 | ||
|
|
a522568a60 | ||
|
|
b6ecd52444 | ||
|
|
3105ef7cb6 | ||
|
|
356f4a041f | ||
|
|
6d43ab371e | ||
|
|
a1a8f90cc5 | ||
|
|
c10b4fb129 | ||
|
|
e025b6b407 | ||
|
|
10f12e4ead | ||
|
|
3c71af48ee | ||
|
|
daa68cb110 | ||
|
|
72ccd7c6ea | ||
|
|
e23fd20d69 | ||
|
|
10b49b4a15 | ||
|
|
bcae1bbd89 | ||
|
|
624a4aa70a | ||
|
|
0702057f47 | ||
|
|
89371378b7 | ||
|
|
f3b49ecc0a | ||
|
|
fb1e9972a5 | ||
|
|
f6a26e3723 | ||
|
|
26027245f0 | ||
|
|
a66529d482 | ||
|
|
bbb086ae6b | ||
|
|
4ed387cc6b | ||
|
|
b88c6b5f6c | ||
|
|
30ae32e539 | ||
|
|
e42ea47ea8 | ||
|
|
96f7a79f3a | ||
|
|
6378cbc0ee | ||
|
|
f3a5b735d6 | ||
|
|
473735dcd7 | ||
|
|
220edef653 | ||
|
|
2619fe814a | ||
|
|
599f31dfae | ||
|
|
8ab12e5e9a | ||
|
|
314032ba3d | ||
|
|
961658ee9a | ||
|
|
2b0fcd3426 | ||
|
|
824431ae94 | ||
|
|
923ea597ec | ||
|
|
9112d6cc6e | ||
|
|
ece08e5e37 | ||
|
|
96fdf72400 | ||
|
|
5fd4f96fc8 | ||
|
|
13449cc9ee | ||
|
|
eab81a2bf9 | ||
|
|
264cf69a6f | ||
|
|
0e9caf2d3f | ||
|
|
45429c8f2a | ||
|
|
e2a0aa5573 | ||
|
|
a932f49554 | ||
|
|
a57e24212e | ||
|
|
312007b51c | ||
|
|
3106f62709 | ||
|
|
48595e1282 | ||
|
|
ee20d91a5e | ||
|
|
775a3b20ab | ||
|
|
0d2e398e37 | ||
|
|
97179cc0b3 | ||
|
|
b26d5e09e6 | ||
|
|
091373f98f | ||
|
|
9abc4f9725 | ||
|
|
cc064c95b1 | ||
|
|
694798947f | ||
|
|
87fb057e0e | ||
|
|
1f2e3d6514 | ||
|
|
71566e3a53 | ||
|
|
a644d3b8c8 | ||
|
|
4b62fbd497 | ||
|
|
b96628eba8 |
85
.appveyor/appveyor.yml
Normal file
85
.appveyor/appveyor.yml
Normal file
@@ -0,0 +1,85 @@
|
||||
version: 3.9.1.{build}
|
||||
shallow_clone: true
|
||||
skip_branch_with_pr: false
|
||||
skip_commits:
|
||||
message: /!nobuild/
|
||||
skip_tags: false
|
||||
|
||||
environment:
|
||||
access_token_documentation:
|
||||
secure: JVBVVL8uJUcLXN+48eRdELEeCGOGCCaMzCqutsUqNuaZ/KblG5ZTt7+LV4UKv/0f
|
||||
LUAROCKS_VER: 2.4.1
|
||||
LUA_VER: 5.1.5
|
||||
LUA: lua5.3
|
||||
matrix:
|
||||
- LUA_VER: 5.1.5
|
||||
|
||||
platform:
|
||||
- x64
|
||||
|
||||
init:
|
||||
- ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod `
|
||||
https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | `
|
||||
Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { `
|
||||
throw "There are newer queued builds for this pull request, failing early." }
|
||||
# - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
||||
|
||||
install:
|
||||
# Outcomment if lua environment invalidates and needs to be reinstalled, otherwise all will run from the cache.
|
||||
# - call choco install 7zip.commandline
|
||||
# - call choco install lua51
|
||||
# - call choco install luarocks
|
||||
# - call refreshenv
|
||||
# - call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"
|
||||
# - cmd: PATH = %PATH%;C:\ProgramData\chocolatey\lib\luarocks\luarocks-2.4.3-win32\systree\bin
|
||||
# - cmd: set LUA_PATH = %LUA_PATH%;C:\ProgramData\chocolatey\lib\luarocks\luarocks-2.4.3-win32\systree\share\lua\5.1\?.lua;C:\ProgramData\chocolatey\lib\luarocks\luarocks-2.4.3-win32\systree\share\lua\5.1\?\init.lua
|
||||
# - cmd: set LUA_CPATH = %LUA_CPATH%;C:\ProgramData\chocolatey\lib\luarocks\luarocks-2.4.3-win32\systree\lib\lua\5.1\?.dll
|
||||
# - call luarocks install luasrcdiet
|
||||
# - call luarocks install checks
|
||||
# - call luarocks install luadocumentor
|
||||
# - call luarocks install luacheck
|
||||
|
||||
|
||||
#cache:
|
||||
# - C:\ProgramData\chocolatey\lib
|
||||
# - C:\ProgramData\chocolatey\bin
|
||||
|
||||
|
||||
|
||||
build_script:
|
||||
- ps: |
|
||||
if( $env:appveyor_repo_branch -eq 'master' -or $env:appveyor_repo_branch -eq 'develop' )
|
||||
{
|
||||
$apiUrl = 'https://ci.appveyor.com/api'
|
||||
$token = 'qts80b5kpq0ooj4x6vvw'
|
||||
$headers = @{
|
||||
"Authorization" = "Bearer $token"
|
||||
"Content-type" = "application/json"
|
||||
}
|
||||
$RequestBody = @{ accountName = 'FlightControl-Master'; projectSlug = 'moose-include'; branch = "$env:appveyor_repo_branch"; environmentVariables = @{} } | ConvertTo-Json
|
||||
# Generate the new version ...
|
||||
$project = Invoke-RestMethod -method Post -Uri "$apiUrl/builds" -Headers $headers -Body $RequestBody
|
||||
}
|
||||
- ps: |
|
||||
if( $env:appveyor_repo_branch -eq 'master' )
|
||||
{
|
||||
$apiUrl = 'https://ci.appveyor.com/api'
|
||||
$token = 'qts80b5kpq0ooj4x6vvw'
|
||||
$headers = @{
|
||||
"Authorization" = "Bearer $token"
|
||||
"Content-type" = "application/json"
|
||||
}
|
||||
$RequestBody = @{ accountName = 'FlightControl-Master'; projectSlug = 'moose-docs'; branch = 'master'; environmentVariables = @{} } | ConvertTo-Json
|
||||
# get project with last build details
|
||||
$project = Invoke-RestMethod -method Post -Uri "$apiUrl/builds" -Headers $headers -Body $RequestBody
|
||||
}
|
||||
|
||||
|
||||
|
||||
test: off
|
||||
# test_script:
|
||||
# - cmd: luacheck "Moose Development\Moose\moose.lua" "Moose Mission Setup\moose.lua"
|
||||
|
||||
|
||||
on_finish:
|
||||
# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
||||
4
.gitmodules
vendored
4
.gitmodules
vendored
@@ -1,4 +0,0 @@
|
||||
[submodule "Moose Development/Moose/Dcs"]
|
||||
path = Moose Development/Moose/Dcs
|
||||
url = https://github.com/FlightControl-Master/DCS-API.git
|
||||
branch = master
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
rem This script will pull the latest changes from the remote repository, and update the submodules accordingly.
|
||||
|
||||
C:\Program Files (x86)\Git\bin\git pull
|
||||
C:\Program Files (x86)\Git\bin\git submodule update --init
|
||||
37
Moose Development/Debugger/MissionScripting.lua
Normal file
37
Moose Development/Debugger/MissionScripting.lua
Normal file
@@ -0,0 +1,37 @@
|
||||
--Initialization script for the Mission lua Environment (SSE)
|
||||
|
||||
dofile('Scripts/ScriptingSystem.lua')
|
||||
|
||||
-- Add LuaSocket to the LUAPATH, so that it can be found.
|
||||
package.path = package.path..";.\\LuaSocket\\?.lua;"
|
||||
|
||||
-- Connect to the debugger, first require it.
|
||||
local initconnection = require("debugger")
|
||||
|
||||
-- Now make the connection..
|
||||
-- "127.0.0.1" is the localhost.
|
||||
-- 10000 is the port. If you wanna use another port in LDT, change this number too!
|
||||
-- "dcsserver" is the name of the server. If you wanna use another name, change the name here too!
|
||||
-- nil (is for transport protocol, but not using this)
|
||||
-- "win" don't touch. But is important to indicate that we are in a windows environment to the debugger script.
|
||||
initconnection( "127.0.0.1", 10000, "dcsserver", nil, "win", "" )
|
||||
|
||||
|
||||
--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.
|
||||
|
||||
local function sanitizeModule(name)
|
||||
_G[name] = nil
|
||||
package.loaded[name] = nil
|
||||
end
|
||||
|
||||
|
||||
do
|
||||
sanitizeModule('os')
|
||||
--sanitizeModule('io')
|
||||
sanitizeModule('lfs')
|
||||
require = nil
|
||||
loadlib = nil
|
||||
end
|
||||
56
Moose Development/Debugger/READ.ME
Normal file
56
Moose Development/Debugger/READ.ME
Normal file
@@ -0,0 +1,56 @@
|
||||
|
||||
-- If you want to use the debugger, add 3 lines of extra code into MissionScripting.lua of DCS world.
|
||||
-- De-sanitize the io module. The debugger needs it.
|
||||
|
||||
|
||||
---------------------------------------------------------------------------------------------------------------------------
|
||||
-- MissionScripting.lua modifications
|
||||
---------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- --Initialization script for the Mission lua Environment (SSE)
|
||||
--
|
||||
-- dofile('Scripts/ScriptingSystem.lua')
|
||||
--
|
||||
-- package.path = package.path..";.\\LuaSocket\\?.lua;"
|
||||
-- local initconnection = require("debugger")
|
||||
-- initconnection( "127.0.0.1", 10000, "dcsserver", nil, "win", "" )
|
||||
--
|
||||
-- --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.
|
||||
--
|
||||
-- local function sanitizeModule(name)
|
||||
-- _G[name] = nil
|
||||
-- package.loaded[name] = nil
|
||||
-- end
|
||||
--
|
||||
-- do
|
||||
-- sanitizeModule('os')
|
||||
-- --sanitizeModule('io')
|
||||
-- sanitizeModule('lfs')
|
||||
-- require = nil
|
||||
-- loadlib = nil
|
||||
-- end
|
||||
|
||||
---------------------------------------------------------------------------------------------------------------------------
|
||||
---------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
-- So for clarity, these are the three lines of code that matter!
|
||||
|
||||
-- Add LuaSocket to the LUAPATH, so that it can be found.
|
||||
package.path = package.path..";.\\LuaSocket\\?.lua;"
|
||||
|
||||
-- Connect to the debugger, first require it.
|
||||
local initconnection = require("debugger")
|
||||
|
||||
-- Now make the connection..
|
||||
-- "127.0.0.1" is the localhost.
|
||||
-- 10000 is the port. If you wanna use another port in LDT, change this number too!
|
||||
-- "dcsserver" is the name of the server. Ensure the same name is used at the Debug Configuration panel!
|
||||
-- nil (is for transport protocol, but not using this)
|
||||
-- "win" don't touch. But is important to indicate that we are in a windows environment to the debugger script.
|
||||
initconnection( "127.0.0.1", 10000, "dcsserver", nil, "win", "" )
|
||||
|
||||
3482
Moose Development/Debugger/debugger.lua
Normal file
3482
Moose Development/Debugger/debugger.lua
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<launchConfiguration type="org.eclipse.ui.externaltools.ProgramLaunchConfigurationType">
|
||||
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
|
||||
<listEntry value="org.eclipse.ui.externaltools.launchGroup"/>
|
||||
</listAttribute>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/Moose_Framework/Utils/GenerateDocumentations.bat}"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/Moose_Framework/Utils}"/>
|
||||
</launchConfiguration>
|
||||
@@ -1,9 +0,0 @@
|
||||
<?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=""Moose_Create.lua" "D" "${current_date}" "${workspace_loc:/Moose_Framework//Moose Development/Moose}" "${workspace_loc:/Moose_Framework/Moose Mission Setup}""/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/Moose_Framework/Moose Mission Setup}"/>
|
||||
</launchConfiguration>
|
||||
@@ -1,9 +0,0 @@
|
||||
<?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=""Moose_Create.lua" "S" "${current_date}" "${workspace_loc:/Moose_Framework//Moose Development/Moose}" "${workspace_loc:/Moose_Framework/Moose Mission Setup}""/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/Moose_Framework/Moose Mission Setup}"/>
|
||||
</launchConfiguration>
|
||||
@@ -1,8 +0,0 @@
|
||||
<?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>
|
||||
@@ -1,9 +0,0 @@
|
||||
<?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=""${selected_resource_loc}""/>
|
||||
<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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
737
Moose Development/Moose/AI/AI_A2A.lua
Normal file
737
Moose Development/Moose/AI/AI_A2A.lua
Normal file
@@ -0,0 +1,737 @@
|
||||
--- **AI** -- (R2.2) - Models the process of air operations for airplanes.
|
||||
--
|
||||
-- This is a class used in the @{AI_A2A_Dispatcher}.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @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
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ### 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:SetFuelThreshold( .2, 60 )
|
||||
self:SetDamageThreshold( 0.4 )
|
||||
self:SetDisengageRadius( 70000 )
|
||||
|
||||
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( "Patrolling", "Refuel", "Refuelling" )
|
||||
|
||||
--- Refuel Handler OnBefore for AI_A2A
|
||||
-- @function [parent=#AI_A2A] OnBeforeRefuel
|
||||
-- @param #AI_A2A self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Refuel Handler OnAfter for AI_A2A
|
||||
-- @function [parent=#AI_A2A] OnAfterRefuel
|
||||
-- @param #AI_A2A self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
--- Refuel Trigger for AI_A2A
|
||||
-- @function [parent=#AI_A2A] Refuel
|
||||
-- @param #AI_A2A self
|
||||
|
||||
--- Refuel Asynchronous Trigger for AI_A2A
|
||||
-- @function [parent=#AI_A2A] __Refuel
|
||||
-- @param #AI_A2A self
|
||||
-- @param #number Delay
|
||||
|
||||
self:AddTransition( "*", "Takeoff", "Airborne" )
|
||||
self:AddTransition( "*", "Return", "Returning" )
|
||||
self:AddTransition( "*", "Hold", "Holding" )
|
||||
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
|
||||
|
||||
--- @param Wrapper.Group#GROUP self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function GROUP:OnEventTakeoff( EventData, Fsm )
|
||||
Fsm:Takeoff()
|
||||
self:UnHandleEvent( EVENTS.Takeoff )
|
||||
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
|
||||
|
||||
--- Sets to refuel at the given tanker.
|
||||
-- @param #AI_A2A self
|
||||
-- @param Wrapper.Group#GROUP TankerName The group name of the tanker as defined within the Mission Editor or spawned.
|
||||
-- @return #AI_A2A self
|
||||
function AI_A2A:SetTanker( TankerName )
|
||||
self:F2( { TankerName } )
|
||||
|
||||
self.TankerName = TankerName
|
||||
end
|
||||
|
||||
|
||||
--- Sets the disengage range, that when engaging a target beyond the specified range, the engagement will be cancelled and the plane will RTB.
|
||||
-- @param #AI_A2A self
|
||||
-- @param #number DisengageRadius The disengage range.
|
||||
-- @return #AI_A2A self
|
||||
function AI_A2A:SetDisengageRadius( DisengageRadius )
|
||||
self:F2( { DisengageRadius } )
|
||||
|
||||
self.DisengageRadius = DisengageRadius
|
||||
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 PatrolFuelThresholdPercentage 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:SetFuelThreshold( PatrolFuelThresholdPercentage, PatrolOutOfFuelOrbitTime )
|
||||
|
||||
self.PatrolManageFuel = true
|
||||
self.PatrolFuelThresholdPercentage = PatrolFuelThresholdPercentage
|
||||
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 PatrolDamageThreshold The treshold in percentage (between 0 and 1) when the AI is considered to be damaged.
|
||||
-- @return #AI_A2A self
|
||||
function AI_A2A:SetDamageThreshold( PatrolDamageThreshold )
|
||||
|
||||
self.PatrolManageDamage = true
|
||||
self.PatrolDamageThreshold = PatrolDamageThreshold
|
||||
|
||||
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( " Checking Status" )
|
||||
|
||||
if self.Controllable and self.Controllable:IsAlive() then
|
||||
|
||||
local RTB = false
|
||||
|
||||
local DistanceFromHomeBase = self.HomeAirbase:GetCoordinate():Get2DDistance( self.Controllable:GetCoordinate() )
|
||||
|
||||
if not self:Is( "Holding" ) and not self:Is( "Returning" ) then
|
||||
local DistanceFromHomeBase = self.HomeAirbase:GetCoordinate():Get2DDistance( self.Controllable:GetCoordinate() )
|
||||
self:F({DistanceFromHomeBase=DistanceFromHomeBase})
|
||||
|
||||
if DistanceFromHomeBase > self.DisengageRadius then
|
||||
self:E( self.Controllable:GetName() .. " is too far from home base, RTB!" )
|
||||
self:Hold( 300 )
|
||||
RTB = false
|
||||
end
|
||||
end
|
||||
|
||||
if self:Is( "Fuel" ) or self:Is( "Damaged" ) or self:Is( "LostControl" ) then
|
||||
if DistanceFromHomeBase < 5000 then
|
||||
self:E( self.Controllable:GetName() .. " is too far from home base, RTB!" )
|
||||
self:Home( "Destroy" )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if not self:Is( "Fuel" ) and not self:Is( "Home" ) then
|
||||
local Fuel = self.Controllable:GetFuel()
|
||||
self:F({Fuel=Fuel})
|
||||
if Fuel < self.PatrolFuelThresholdPercentage then
|
||||
if self.TankerName then
|
||||
self:E( self.Controllable:GetName() .. " is out of fuel: " .. Fuel .. " ... Refuelling at Tanker!" )
|
||||
self:Refuel()
|
||||
else
|
||||
self:E( self.Controllable:GetName() .. " is out of fuel: " .. Fuel .. " ... RTB!" )
|
||||
local OldAIControllable = self.Controllable
|
||||
|
||||
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
|
||||
end
|
||||
else
|
||||
end
|
||||
end
|
||||
|
||||
-- TODO: Check GROUP damage function.
|
||||
local Damage = self.Controllable:GetLife()
|
||||
local InitialLife = self.Controllable:GetLife0()
|
||||
self:F( { Damage = Damage, InitialLife = InitialLife, DamageThreshold = self.PatrolDamageThreshold } )
|
||||
if ( Damage / InitialLife ) < self.PatrolDamageThreshold then
|
||||
self:E( self.Controllable:GetName() .. " is damaged: " .. Damage .. " ... RTB!" )
|
||||
self:Damaged()
|
||||
RTB = true
|
||||
self:SetStatusOff()
|
||||
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" ) and
|
||||
not self:Is( "Home" ) then
|
||||
if self.IdleCount >= 2 then
|
||||
if Damage ~= InitialLife then
|
||||
self:Damaged()
|
||||
else
|
||||
self:E( self.Controllable:GetName() .. " control lost! " )
|
||||
self:LostControl()
|
||||
end
|
||||
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, Fsm )
|
||||
|
||||
AIGroup:F( { "AI_A2A.RTBRoute:", AIGroup:GetName() } )
|
||||
|
||||
if AIGroup:IsAlive() then
|
||||
Fsm:__RTB( 0.5 )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- @param Wrapper.Group#GROUP AIGroup
|
||||
function AI_A2A.RTBHold( AIGroup, Fsm )
|
||||
|
||||
AIGroup:F( { "AI_A2A.RTBHold:", AIGroup:GetName() } )
|
||||
if AIGroup:IsAlive() then
|
||||
Fsm:__RTB( 0.5 )
|
||||
Fsm:Return()
|
||||
local Task = AIGroup:TaskOrbitCircle( 4000, 400 )
|
||||
AIGroup:SetTask( Task )
|
||||
end
|
||||
|
||||
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: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:E( "RTB and near the airbase!" )
|
||||
self:Home()
|
||||
return
|
||||
end
|
||||
--- Create a route point of type air.
|
||||
local ToRTBRoutePoint = ToAirbaseCoord:WaypointAir(
|
||||
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] = ToRTBRoutePoint
|
||||
EngageRoute[#EngageRoute+1] = ToRTBRoutePoint
|
||||
|
||||
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( "AI_A2A.RTBRoute", self )
|
||||
EngageRoute[#EngageRoute].task = AIGroup:TaskCombo( Tasks )
|
||||
|
||||
--- NOW ROUTE THE GROUP!
|
||||
AIGroup:Route( EngageRoute, 0.5 )
|
||||
|
||||
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
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
function AI_A2A:onafterHold( AIGroup, From, Event, To, HoldTime )
|
||||
self:F( { AIGroup, From, Event, To } )
|
||||
|
||||
self:E( "Group " .. self.Controllable:GetName() .. " ... Holding! ( " .. self:GetState() .. " )" )
|
||||
|
||||
if AIGroup and AIGroup:IsAlive() then
|
||||
local OrbitTask = AIGroup:TaskOrbitCircle( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ), self.PatrolMinSpeed )
|
||||
local TimedOrbitTask = AIGroup:TaskControlled( OrbitTask, AIGroup:TaskCondition( nil, nil, nil, nil, HoldTime , nil ) )
|
||||
|
||||
local RTBTask = AIGroup:TaskFunction( "AI_A2A.RTBHold", self )
|
||||
|
||||
local OrbitHoldTask = AIGroup:TaskOrbitCircle( 4000, self.PatrolMinSpeed )
|
||||
|
||||
--AIGroup:SetState( AIGroup, "AI_A2A", self )
|
||||
|
||||
AIGroup:SetTask( AIGroup:TaskCombo( { TimedOrbitTask, RTBTask, OrbitHoldTask } ), 1 )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- @param Wrapper.Group#GROUP AIGroup
|
||||
function AI_A2A.Resume( AIGroup, Fsm )
|
||||
|
||||
AIGroup:F( { "AI_A2A.Resume:", AIGroup:GetName() } )
|
||||
if AIGroup:IsAlive() then
|
||||
Fsm:__RTB( 0.5 )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- @param #AI_A2A self
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
function AI_A2A:onafterRefuel( AIGroup, From, Event, To )
|
||||
self:F( { AIGroup, From, Event, To } )
|
||||
|
||||
self:E( "Group " .. self.Controllable:GetName() .. " ... Refuelling! ( " .. self:GetState() .. " )" )
|
||||
|
||||
if AIGroup and AIGroup:IsAlive() then
|
||||
local Tanker = GROUP:FindByName( self.TankerName )
|
||||
if Tanker:IsAlive() and Tanker:IsAirPlane() then
|
||||
|
||||
local RefuelRoute = {}
|
||||
|
||||
--- Calculate the target route point.
|
||||
|
||||
local CurrentCoord = AIGroup:GetCoordinate()
|
||||
local ToRefuelCoord = Tanker:GetCoordinate()
|
||||
local ToRefuelSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed )
|
||||
|
||||
--- Create a route point of type air.
|
||||
local ToRefuelRoutePoint = ToRefuelCoord:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
ToRefuelSpeed,
|
||||
true
|
||||
)
|
||||
|
||||
self:F( { ToRefuelSpeed = ToRefuelSpeed } )
|
||||
|
||||
RefuelRoute[#RefuelRoute+1] = ToRefuelRoutePoint
|
||||
RefuelRoute[#RefuelRoute+1] = ToRefuelRoutePoint
|
||||
|
||||
AIGroup:OptionROEHoldFire()
|
||||
AIGroup:OptionROTEvadeFire()
|
||||
|
||||
local Tasks = {}
|
||||
Tasks[#Tasks+1] = AIGroup:TaskRefueling()
|
||||
Tasks[#Tasks+1] = AIGroup:TaskFunction( self:GetClassName() .. ".Resume", self )
|
||||
RefuelRoute[#RefuelRoute].task = AIGroup:TaskCombo( Tasks )
|
||||
|
||||
AIGroup:Route( RefuelRoute, 0.5 )
|
||||
else
|
||||
self:RTB()
|
||||
end
|
||||
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
|
||||
492
Moose Development/Moose/AI/AI_A2A_Cap.lua
Normal file
492
Moose Development/Moose/AI/AI_A2A_Cap.lua
Normal file
@@ -0,0 +1,492 @@
|
||||
--- **AI** -- (R2.2) - Models the process of Combat Air Patrol (CAP) for airplanes.
|
||||
--
|
||||
-- This is a class used in the @{AI_A2A_Dispatcher}.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @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 @{Group} or @{Group}
|
||||
-- and automatically engage any airborne enemies that are within a certain range or within a certain zone.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- 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.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- 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.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- This cycle will continue.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- When enemies are detected, the AI will automatically engage the enemy.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- 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.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ## 1. AI_A2A_CAP constructor
|
||||
--
|
||||
-- * @{#AI_A2A_CAP.New}(): Creates a new AI_A2A_CAP object.
|
||||
--
|
||||
-- ## 2. AI_A2A_CAP is a FSM
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ### 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
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- 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
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- 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 AICap
|
||||
-- @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 @{Group} in km/h.
|
||||
-- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Group} in km/h.
|
||||
-- @param Dcs.DCSTypes#Speed EngageMinSpeed The minimum speed of the @{Group} in km/h when engaging a target.
|
||||
-- @param Dcs.DCSTypes#Speed EngageMaxSpeed The maximum speed of the @{Group} 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( AICap, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageMinSpeed, EngageMaxSpeed, PatrolAltType )
|
||||
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, AI_A2A_PATROL:New( AICap, 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", "Airborne" }, "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.Group#GROUP AICap The Group 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.Group#GROUP AICap The Group 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.Group#GROUP AICap The Group 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.Group#GROUP AICap The Group 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.Group#GROUP AICap The Group 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.Group#GROUP AICap The Group 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.Group#GROUP AICap The Group 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.Group#GROUP AICap The Group 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.Group#GROUP AICap The Group 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.Group#GROUP AICap The Group 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.Group#GROUP AICap The Group 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.Group#GROUP AICap The Group 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
|
||||
|
||||
--- onafter State Transition for Event Patrol.
|
||||
-- @param #AI_A2A_GCI self
|
||||
-- @param Wrapper.Group#GROUP AICap 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:onafterStart( AICap, From, Event, To )
|
||||
|
||||
AICap:HandleEvent( EVENTS.Takeoff, nil, 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.Group#GROUP AICap 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( AICap, From, Event, To )
|
||||
|
||||
-- Call the parent Start event handler
|
||||
self:GetParent(self).onafterPatrol( self, AICap, From, Event, To )
|
||||
self:HandleEvent( EVENTS.Dead )
|
||||
|
||||
end
|
||||
|
||||
-- todo: need to fix this global function
|
||||
|
||||
--- @param Wrapper.Group#GROUP AICap
|
||||
function AI_A2A_CAP.AttackRoute( AICap, Fsm )
|
||||
|
||||
AICap:F( { "AI_A2A_CAP.AttackRoute:", AICap:GetName() } )
|
||||
|
||||
if AICap:IsAlive() then
|
||||
Fsm:__Engage( 0.5 )
|
||||
end
|
||||
end
|
||||
|
||||
--- @param #AI_A2A_CAP self
|
||||
-- @param Wrapper.Group#GROUP AICap The Group 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( AICap, From, Event, To )
|
||||
|
||||
if self.Accomplished == true then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
--- @param #AI_A2A_CAP self
|
||||
-- @param Wrapper.Group#GROUP AICap 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( AICap, From, Event, To )
|
||||
AICap:ClearTasks()
|
||||
self:__Route( 0.5 )
|
||||
end
|
||||
|
||||
|
||||
--- @param #AI_A2A_CAP self
|
||||
-- @param Wrapper.Group#GROUP AICap The AICap 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( AICap, From, Event, To, AttackSetUnit )
|
||||
|
||||
self:F( { AICap, From, Event, To, AttackSetUnit} )
|
||||
|
||||
self.AttackSetUnit = AttackSetUnit or self.AttackSetUnit -- Core.Set#SET_UNIT
|
||||
|
||||
local FirstAttackUnit = self.AttackSetUnit:GetFirst() -- Wrapper.Unit#UNIT
|
||||
|
||||
if FirstAttackUnit and FirstAttackUnit:IsAlive() then -- If there is no attacker anymore, stop the engagement.
|
||||
|
||||
if AICap:IsAlive() then
|
||||
|
||||
local EngageRoute = {}
|
||||
|
||||
--- Calculate the target route point.
|
||||
local CurrentCoord = AICap: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 ):WaypointAir(
|
||||
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
|
||||
EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint
|
||||
|
||||
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] = AICap:TaskAttackUnit( AttackUnit )
|
||||
end
|
||||
end
|
||||
|
||||
if #AttackTasks == 0 then
|
||||
self:E("No targets found -> Going back to Patrolling")
|
||||
self:__Abort( 0.5 )
|
||||
else
|
||||
AICap:OptionROEOpenFire()
|
||||
AICap:OptionROTEvadeFire()
|
||||
|
||||
AttackTasks[#AttackTasks+1] = AICap:TaskFunction( "AI_A2A_CAP.AttackRoute", self )
|
||||
EngageRoute[#EngageRoute].task = AICap:TaskCombo( AttackTasks )
|
||||
end
|
||||
|
||||
AICap:Route( EngageRoute, 0.5 )
|
||||
end
|
||||
else
|
||||
self:E("No targets found -> Going back to Patrolling")
|
||||
self:__Abort( 0.5 )
|
||||
end
|
||||
end
|
||||
|
||||
--- @param #AI_A2A_CAP self
|
||||
-- @param Wrapper.Group#GROUP AICap The Group 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( AICap, From, Event, To )
|
||||
self.Accomplished = true
|
||||
self:SetDetectionOff()
|
||||
end
|
||||
|
||||
--- @param #AI_A2A_CAP self
|
||||
-- @param Wrapper.Group#GROUP AICap The Group 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( AICap, 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
|
||||
|
||||
--- @param Wrapper.Group#GROUP AICap
|
||||
function AI_A2A_CAP.Resume( AICap )
|
||||
|
||||
AICap:F( { "AI_A2A_CAP.Resume:", AICap:GetName() } )
|
||||
if AICap:IsAlive() then
|
||||
local _AI_A2A = AICap:GetState( AICap, "AI_A2A" ) -- #AI_A2A
|
||||
_AI_A2A:__Reset( 1 )
|
||||
_AI_A2A:__Route( 5 )
|
||||
end
|
||||
|
||||
end
|
||||
3749
Moose Development/Moose/AI/AI_A2A_Dispatcher.lua
Normal file
3749
Moose Development/Moose/AI/AI_A2A_Dispatcher.lua
Normal file
File diff suppressed because it is too large
Load Diff
462
Moose Development/Moose/AI/AI_A2A_Gci.lua
Normal file
462
Moose Development/Moose/AI/AI_A2A_Gci.lua
Normal file
@@ -0,0 +1,462 @@
|
||||
--- **AI** -- (R2.2) - Models the process of Ground Controlled Interception (GCI) for airplanes.
|
||||
--
|
||||
-- This is a class used in the @{AI_A2A_Dispatcher}.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module 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.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- 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.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- 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.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- This cycle will continue.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- When enemies are detected, the AI will automatically engage the enemy.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- 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.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ## 1. AI_A2A_GCI constructor
|
||||
--
|
||||
-- * @{#AI_A2A_GCI.New}(): Creates a new AI_A2A_GCI object.
|
||||
--
|
||||
-- ## 2. AI_A2A_GCI is a FSM
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ### 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
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- 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
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- 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 AIIntercept
|
||||
-- @return #AI_A2A_GCI
|
||||
function AI_A2A_GCI:New( AIIntercept, EngageMinSpeed, EngageMaxSpeed )
|
||||
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, AI_A2A:New( AIIntercept ) ) -- #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", "Airborne" }, "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 AIIntercept The Group 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 AIIntercept The Group 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 AIIntercept The Group 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 AIIntercept The Group 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 AIIntercept The Group 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 AIIntercept The Group 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 AIIntercept The Group 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 AIIntercept The Group 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 AIIntercept The Group 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 AIIntercept The Group 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 AIIntercept The Group 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 AIIntercept The Group 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 AIIntercept 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:onafterStart( AIIntercept, From, Event, To )
|
||||
|
||||
AIIntercept:HandleEvent( EVENTS.Takeoff, nil, self )
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- onafter State Transition for Event Patrol.
|
||||
-- @param #AI_A2A_GCI self
|
||||
-- @param Wrapper.Group#GROUP AIIntercept 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( AIIntercept, 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( AIIntercept, Fsm )
|
||||
|
||||
AIIntercept:F( { "AI_A2A_GCI.InterceptRoute:", AIIntercept:GetName() } )
|
||||
|
||||
if AIIntercept:IsAlive() then
|
||||
Fsm:__Engage( 0.5 )
|
||||
|
||||
--local Task = AIIntercept:TaskOrbitCircle( 4000, 400 )
|
||||
--AIIntercept:SetTask( Task )
|
||||
end
|
||||
end
|
||||
|
||||
--- @param #AI_A2A_GCI self
|
||||
-- @param Wrapper.Group#GROUP AIIntercept The Group 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( AIIntercept, From, Event, To )
|
||||
|
||||
if self.Accomplished == true then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
--- @param #AI_A2A_GCI self
|
||||
-- @param Wrapper.Group#GROUP AIIntercept 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( AIIntercept, From, Event, To )
|
||||
AIIntercept:ClearTasks()
|
||||
self:Return()
|
||||
self:__RTB( 0.5 )
|
||||
end
|
||||
|
||||
|
||||
--- @param #AI_A2A_GCI self
|
||||
-- @param Wrapper.Group#GROUP AIIntercept The GroupGroup 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( AIIntercept, From, Event, To, AttackSetUnit )
|
||||
|
||||
self:F( { AIIntercept, From, Event, To, AttackSetUnit} )
|
||||
|
||||
self.AttackSetUnit = AttackSetUnit or self.AttackSetUnit -- Core.Set#SET_UNIT
|
||||
|
||||
local FirstAttackUnit = self.AttackSetUnit:GetFirst()
|
||||
|
||||
if FirstAttackUnit and FirstAttackUnit:IsAlive() then
|
||||
|
||||
if AIIntercept:IsAlive() then
|
||||
|
||||
local EngageRoute = {}
|
||||
|
||||
local CurrentCoord = AIIntercept:GetCoordinate()
|
||||
|
||||
--- Calculate the target route point.
|
||||
|
||||
local CurrentCoord = AIIntercept: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( 15000, ToInterceptAngle ):WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
ToTargetSpeed,
|
||||
true
|
||||
)
|
||||
|
||||
self:F( { Angle = ToInterceptAngle, ToTargetSpeed = ToTargetSpeed } )
|
||||
self:F( { self.EngageMinSpeed, self.EngageMaxSpeed, ToTargetSpeed } )
|
||||
|
||||
EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint
|
||||
EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint
|
||||
|
||||
local AttackTasks = {}
|
||||
|
||||
for AttackUnitID, AttackUnit in pairs( self.AttackSetUnit:GetSet() ) do
|
||||
local AttackUnit = AttackUnit -- Wrapper.Unit#UNIT
|
||||
if AttackUnit:IsAlive() and AttackUnit:IsAir() then
|
||||
self:T( { "Intercepting Unit:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsAir() } )
|
||||
AttackTasks[#AttackTasks+1] = AIIntercept:TaskAttackUnit( AttackUnit )
|
||||
end
|
||||
end
|
||||
|
||||
if #AttackTasks == 0 then
|
||||
self:E("No targets found -> Going RTB")
|
||||
self:Return()
|
||||
self:__RTB( 0.5 )
|
||||
else
|
||||
AIIntercept:OptionROEOpenFire()
|
||||
AIIntercept:OptionROTEvadeFire()
|
||||
|
||||
AttackTasks[#AttackTasks+1] = AIIntercept:TaskFunction( "AI_A2A_GCI.InterceptRoute", self )
|
||||
EngageRoute[#EngageRoute].task = AIIntercept:TaskCombo( AttackTasks )
|
||||
end
|
||||
|
||||
AIIntercept:Route( EngageRoute, 0.5 )
|
||||
|
||||
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 AIIntercept The Group 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( AIIntercept, From, Event, To )
|
||||
self.Accomplished = true
|
||||
self:SetDetectionOff()
|
||||
end
|
||||
|
||||
--- @param #AI_A2A_GCI self
|
||||
-- @param Wrapper.Group#GROUP AIIntercept The Group 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( AIIntercept, 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
|
||||
366
Moose Development/Moose/AI/AI_A2A_Patrol.lua
Normal file
366
Moose Development/Moose/AI/AI_A2A_Patrol.lua
Normal file
@@ -0,0 +1,366 @@
|
||||
--- **AI** -- (R2.2) - Models the process of air patrol of airplanes.
|
||||
--
|
||||
-- This is a class used in the @{AI_A2A_Dispatcher}.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @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 @{Group} or @{Group}.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- 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.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- 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.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- This cycle will continue.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
---- 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.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- 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.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ## 1. AI_A2A_PATROL constructor
|
||||
--
|
||||
-- * @{#AI_A2A_PATROL.New}(): Creates a new AI_A2A_PATROL object.
|
||||
--
|
||||
-- ## 2. AI_A2A_PATROL is a FSM
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ### 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.SetRefreshTimeInterval}( 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 Patrol is started. However, damage cannon be foreseen early on.
|
||||
-- Therefore, when the damage treshold is reached, the AI will return immediately to the home base (RTB).
|
||||
-- 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 AIPatrol
|
||||
-- @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 @{Group} in km/h.
|
||||
-- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Group} 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 a Group 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( AIPatrol, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
|
||||
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, AI_A2A:New( AIPatrol ) ) -- #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", "Airborne", "Refuelling" }, "Patrol", "Patrolling" )
|
||||
|
||||
--- OnBefore Transition Handler for Event Patrol.
|
||||
-- @function [parent=#AI_A2A_PATROL] OnBeforePatrol
|
||||
-- @param #AI_A2A_PATROL self
|
||||
-- @param Wrapper.Group#GROUP AIPatrol The Group 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.Group#GROUP AIPatrol The Group 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.Group#GROUP AIPatrol The Group 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.Group#GROUP AIPatrol The Group 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.Group#GROUP AIPatrol The Group 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.Group#GROUP AIPatrol The Group 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 @{Group} in km/h.
|
||||
-- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Group} 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.Group#GROUP AIPatrol The Group 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( AIPatrol, From, Event, To )
|
||||
self:F2()
|
||||
|
||||
self:ClearTargetDistance()
|
||||
|
||||
self:__Route( 1 )
|
||||
|
||||
AIPatrol:OnReSpawn(
|
||||
function( PatrolGroup )
|
||||
self:__Reset( 1 )
|
||||
self:__Route( 5 )
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- @param Wrapper.Group#GROUP AIPatrol
|
||||
-- This statis method is called from the route path within the last task at the last waaypoint of the AIPatrol.
|
||||
-- Note that this method is required, as triggers the next route when patrolling for the AIPatrol.
|
||||
function AI_A2A_PATROL.PatrolRoute( AIPatrol, Fsm )
|
||||
|
||||
AIPatrol:F( { "AI_A2A_PATROL.PatrolRoute:", AIPatrol:GetName() } )
|
||||
|
||||
if AIPatrol:IsAlive() then
|
||||
Fsm:Route()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Defines a new patrol route using the @{Process_PatrolZone} parameters and settings.
|
||||
-- @param #AI_A2A_PATROL self
|
||||
-- @param Wrapper.Group#GROUP AIPatrol The 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_PATROL:onafterRoute( AIPatrol, From, Event, To )
|
||||
|
||||
self:F2()
|
||||
|
||||
-- When RTB, don't allow anymore the routing.
|
||||
if From == "RTB" then
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
if AIPatrol:IsAlive() then
|
||||
|
||||
local PatrolRoute = {}
|
||||
|
||||
--- Calculate the target route point.
|
||||
|
||||
local CurrentCoord = AIPatrol: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:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
ToTargetSpeed,
|
||||
true
|
||||
)
|
||||
|
||||
PatrolRoute[#PatrolRoute+1] = ToPatrolRoutePoint
|
||||
PatrolRoute[#PatrolRoute+1] = ToPatrolRoutePoint
|
||||
|
||||
local Tasks = {}
|
||||
Tasks[#Tasks+1] = AIPatrol:TaskFunction( "AI_A2A_PATROL.PatrolRoute", self )
|
||||
PatrolRoute[#PatrolRoute].task = AIPatrol:TaskCombo( Tasks )
|
||||
|
||||
AIPatrol:OptionROEReturnFire()
|
||||
AIPatrol:OptionROTEvadeFire()
|
||||
|
||||
AIPatrol:Route( PatrolRoute, 0.5 )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- @param Wrapper.Group#GROUP AIPatrol
|
||||
function AI_A2A_PATROL.Resume( AIPatrol )
|
||||
|
||||
AIPatrol:F( { "AI_A2A_PATROL.Resume:", AIPatrol:GetName() } )
|
||||
if AIPatrol:IsAlive() then
|
||||
local _AI_A2A = AIPatrol:GetState( AIPatrol, "AI_A2A" ) -- #AI_A2A
|
||||
_AI_A2A:__Reset( 1 )
|
||||
_AI_A2A:__Route( 5 )
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,55 +1,25 @@
|
||||
--- **AI** -- **Provide Battlefield Air Interdiction (bombing).**
|
||||
--- **AI** -- (R2.1) - Manages the independent process of Battlefield Air Interdiction (bombing) for airplanes.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- 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)
|
||||
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/BAI%20-%20Battlefield%20Air%20Interdiction)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # **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.
|
||||
--
|
||||
--
|
||||
-- ### [YouTube Playlist]()
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # **AUTHORS and CONTRIBUTIONS**
|
||||
--
|
||||
-- ### Contributions:
|
||||
--
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- * **[Gunterlund](http://forums.eagle.ru:8080/member.php?u=75036)**: Test case revision.
|
||||
--
|
||||
-- ### Authors:
|
||||
--
|
||||
-- * **FlightControl**: Concept, Design & Programming.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI_Bai
|
||||
|
||||
@@ -546,7 +516,7 @@ function AI_BAI_ZONE:onafterEngage( Controllable, From, Event, To,
|
||||
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(
|
||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
@@ -603,7 +573,7 @@ function AI_BAI_ZONE:onafterEngage( Controllable, From, Event, To,
|
||||
local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, self.EngageAltitude, ToTargetVec2.y )
|
||||
|
||||
--- Create a route point of type air.
|
||||
local ToTargetRoutePoint = ToTargetPointVec3:RoutePointAir(
|
||||
local ToTargetRoutePoint = ToTargetPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
@@ -627,7 +597,7 @@ function AI_BAI_ZONE:onafterEngage( Controllable, From, Event, To,
|
||||
--- NOW ROUTE THE GROUP!
|
||||
Controllable:WayPointExecute( 1 )
|
||||
|
||||
self:SetDetectionInterval( 2 )
|
||||
self:SetRefreshTimeInterval( 2 )
|
||||
self:SetDetectionActivated()
|
||||
self:__Target( -2 ) -- Start Targetting
|
||||
end
|
||||
|
||||
@@ -1,58 +1,31 @@
|
||||
--- Single-Player:**No** / Multi-Player:**Yes** / AI:**Yes** / Human:**No** / Types:**All** -- **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.**
|
||||
--- **AI** -- (2.1) - Balance player slots with AI to create an engaging simulation environment, independent of the amount of players.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- # Demo Missions
|
||||
--
|
||||
-- ### [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)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # **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.
|
||||
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/AIB%20-%20AI%20Balancing)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # **AUTHORS and CONTRIBUTIONS**
|
||||
-- ### [YouTube Playlist](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl2CJVIrL1TdAumuVS8n64B7)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **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 :-)
|
||||
-- * **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
|
||||
|
||||
--- @type AI_BALANCER
|
||||
-- @field Core.Set#SET_CLIENT SetClient
|
||||
-- @field Functional.Spawn#SPAWN SpawnAI
|
||||
-- @field Core.Spawn#SPAWN SpawnAI
|
||||
-- @field Wrapper.Group#GROUP Test
|
||||
-- @extends Core.Fsm#FSM_SET
|
||||
|
||||
@@ -125,7 +98,7 @@ AI_BALANCER = {
|
||||
--- Creates a new AI_BALANCER object
|
||||
-- @param #AI_BALANCER self
|
||||
-- @param Core.Set#SET_CLIENT SetClient A SET\_CLIENT object that will contain the CLIENT objects to be monitored if they are alive or not (joined by a player).
|
||||
-- @param Functional.Spawn#SPAWN SpawnAI The default Spawn object to spawn new AI Groups when needed.
|
||||
-- @param Core.Spawn#SPAWN SpawnAI The default Spawn object to spawn new AI Groups when needed.
|
||||
-- @return #AI_BALANCER
|
||||
function AI_BALANCER:New( SetClient, SpawnAI )
|
||||
|
||||
@@ -170,22 +143,22 @@ end
|
||||
|
||||
--- Returns the AI to the nearest friendly @{Airbase#AIRBASE}.
|
||||
-- @param #AI_BALANCER self
|
||||
-- @param Dcs.DCSTypes#Distance ReturnTresholdRange If there is an enemy @{Client#CLIENT} within the ReturnTresholdRange given in meters, the AI will not return to the nearest @{Airbase#AIRBASE}.
|
||||
-- @param Dcs.DCSTypes#Distance ReturnThresholdRange If there is an enemy @{Client#CLIENT} within the ReturnThresholdRange given in meters, the AI will not return to the nearest @{Airbase#AIRBASE}.
|
||||
-- @param Core.Set#SET_AIRBASE ReturnAirbaseSet The SET of @{Set#SET_AIRBASE}s to evaluate where to return to.
|
||||
function AI_BALANCER:ReturnToNearestAirbases( ReturnTresholdRange, ReturnAirbaseSet )
|
||||
function AI_BALANCER:ReturnToNearestAirbases( ReturnThresholdRange, ReturnAirbaseSet )
|
||||
|
||||
self.ToNearestAirbase = true
|
||||
self.ReturnTresholdRange = ReturnTresholdRange
|
||||
self.ReturnThresholdRange = ReturnThresholdRange
|
||||
self.ReturnAirbaseSet = ReturnAirbaseSet
|
||||
end
|
||||
|
||||
--- Returns the AI to the home @{Airbase#AIRBASE}.
|
||||
-- @param #AI_BALANCER self
|
||||
-- @param Dcs.DCSTypes#Distance ReturnTresholdRange If there is an enemy @{Client#CLIENT} within the ReturnTresholdRange given in meters, the AI will not return to the nearest @{Airbase#AIRBASE}.
|
||||
function AI_BALANCER:ReturnToHomeAirbase( ReturnTresholdRange )
|
||||
-- @param Dcs.DCSTypes#Distance ReturnThresholdRange If there is an enemy @{Client#CLIENT} within the ReturnThresholdRange given in meters, the AI will not return to the nearest @{Airbase#AIRBASE}.
|
||||
function AI_BALANCER:ReturnToHomeAirbase( ReturnThresholdRange )
|
||||
|
||||
self.ToHomeAirbase = true
|
||||
self.ReturnTresholdRange = ReturnTresholdRange
|
||||
self.ReturnThresholdRange = ReturnThresholdRange
|
||||
end
|
||||
|
||||
--- @param #AI_BALANCER self
|
||||
@@ -197,9 +170,10 @@ function AI_BALANCER:onenterSpawning( SetGroup, From, Event, To, ClientName )
|
||||
-- OK, Spawn a new group from the default SpawnAI object provided.
|
||||
local AIGroup = self.SpawnAI:Spawn() -- Wrapper.Group#GROUP
|
||||
if AIGroup then
|
||||
AIGroup:E( "Spawning new AIGroup" )
|
||||
AIGroup:T( { "Spawning new AIGroup", ClientName = ClientName } )
|
||||
--TODO: need to rework UnitName thing ...
|
||||
|
||||
SetGroup:Remove( ClientName ) -- Ensure that the previously allocated AIGroup to ClientName is removed in the Set.
|
||||
SetGroup:Add( ClientName, AIGroup )
|
||||
self.SpawnQueue[ClientName] = nil
|
||||
|
||||
@@ -215,9 +189,9 @@ end
|
||||
function AI_BALANCER:onenterDestroying( SetGroup, From, Event, To, ClientName, AIGroup )
|
||||
|
||||
AIGroup:Destroy()
|
||||
SetGroup:Flush()
|
||||
SetGroup:Flush( self )
|
||||
SetGroup:Remove( ClientName )
|
||||
SetGroup:Flush()
|
||||
SetGroup:Flush( self )
|
||||
end
|
||||
|
||||
--- @param #AI_BALANCER self
|
||||
@@ -258,23 +232,24 @@ function AI_BALANCER:onenterMonitoring( SetGroup )
|
||||
self:T3(Client.ClientName)
|
||||
|
||||
local AIGroup = self.Set:Get( Client.UnitName ) -- Wrapper.Group#GROUP
|
||||
if Client:IsAlive() then
|
||||
if AIGroup then self:T( { AIGroup = AIGroup:GetName(), IsAlive = AIGroup:IsAlive() } ) end
|
||||
if Client:IsAlive() == true then
|
||||
|
||||
if AIGroup and AIGroup:IsAlive() == true then
|
||||
|
||||
if self.ToNearestAirbase == false and self.ToHomeAirbase == false then
|
||||
self:Destroy( Client.UnitName, AIGroup )
|
||||
else
|
||||
-- We test if there is no other CLIENT within the self.ReturnTresholdRange of the first unit of the AI group.
|
||||
-- We test if there is no other CLIENT within the self.ReturnThresholdRange of the first unit of the AI group.
|
||||
-- If there is a CLIENT, the AI stays engaged and will not return.
|
||||
-- If there is no CLIENT within the self.ReturnTresholdRange, then the unit will return to the Airbase return method selected.
|
||||
-- If there is no CLIENT within the self.ReturnThresholdRange, then the unit will return to the Airbase return method selected.
|
||||
|
||||
local PlayerInRange = { Value = false }
|
||||
local RangeZone = ZONE_RADIUS:New( 'RangeZone', AIGroup:GetVec2(), self.ReturnTresholdRange )
|
||||
local RangeZone = ZONE_RADIUS:New( 'RangeZone', AIGroup:GetVec2(), self.ReturnThresholdRange )
|
||||
|
||||
self:T2( RangeZone )
|
||||
|
||||
_DATABASE:ForEachPlayer(
|
||||
_DATABASE:ForEachPlayerUnit(
|
||||
--- @param Wrapper.Unit#UNIT RangeTestUnit
|
||||
function( RangeTestUnit, RangeZone, AIGroup, PlayerInRange )
|
||||
self:T2( { PlayerInRange, RangeTestUnit.UnitName, RangeZone.ZoneName } )
|
||||
@@ -303,11 +278,12 @@ function AI_BALANCER:onenterMonitoring( SetGroup )
|
||||
else
|
||||
if not AIGroup or not AIGroup:IsAlive() == true then
|
||||
self:T( "Client " .. Client.UnitName .. " not alive." )
|
||||
self:T( { Queue = self.SpawnQueue[Client.UnitName] } )
|
||||
if not self.SpawnQueue[Client.UnitName] then
|
||||
-- Spawn a new AI taking into account the spawn interval Earliest, Latest
|
||||
self:__Spawn( math.random( self.Earliest, self.Latest ), Client.UnitName )
|
||||
self.SpawnQueue[Client.UnitName] = true
|
||||
self:E( "New AI Spawned for Client " .. Client.UnitName )
|
||||
self:T( "New AI Spawned for Client " .. Client.UnitName )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,59 +1,29 @@
|
||||
--- **AI** - **Execute Combat Air Patrol (CAP).**
|
||||
--- **AI** -- (R2.1) - Manages the independent process of Combat Air Patrol (CAP) for airplanes.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- AI CAP classes makes AI Controllables execute a Combat Air Patrol.
|
||||
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/CAP%20-%20Combat%20Air%20Patrol)
|
||||
--
|
||||
-- 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)
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- # **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:
|
||||
--
|
||||
-- ### [YouTube Playlist](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl1YCyPxJgoZn-CfhwyeW65L)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **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.
|
||||
--
|
||||
-- ### Authors:
|
||||
--
|
||||
-- * **FlightControl**: Concept, Design & Programming.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI_Cap
|
||||
|
||||
@@ -373,16 +343,20 @@ function AI_CAP_ZONE:onafterStart( Controllable, From, Event, To )
|
||||
|
||||
end
|
||||
|
||||
-- todo: need to fix this global function
|
||||
|
||||
--- @param Wrapper.Controllable#CONTROLLABLE AIControllable
|
||||
function _NewEngageCapRoute( AIControllable )
|
||||
--- @param AI.AI_CAP#AI_CAP_ZONE
|
||||
-- @param Wrapper.Group#GROUP EngageGroup
|
||||
function AI_CAP_ZONE.EngageRoute( EngageGroup, Fsm )
|
||||
|
||||
AIControllable:T( "NewEngageRoute" )
|
||||
local EngageZone = AIControllable:GetState( AIControllable, "EngageZone" ) -- AI.AI_Cap#AI_CAP_ZONE
|
||||
EngageZone:__Engage( 1 )
|
||||
EngageGroup:F( { "AI_CAP_ZONE.EngageRoute:", EngageGroup:GetName() } )
|
||||
|
||||
if EngageGroup:IsAlive() then
|
||||
Fsm:__Engage( 1 )
|
||||
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.
|
||||
@@ -417,7 +391,7 @@ 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
|
||||
@@ -455,7 +429,7 @@ function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To )
|
||||
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(
|
||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
@@ -479,7 +453,7 @@ function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To )
|
||||
local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, ToTargetAltitude, ToTargetVec2.y )
|
||||
|
||||
--- Create a route point of type air.
|
||||
local ToPatrolRoutePoint = ToTargetPointVec3:RoutePointAir(
|
||||
local ToPatrolRoutePoint = ToTargetPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
@@ -490,7 +464,7 @@ function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To )
|
||||
EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint
|
||||
|
||||
Controllable:OptionROEOpenFire()
|
||||
Controllable:OptionROTPassiveDefense()
|
||||
Controllable:OptionROTEvadeFire()
|
||||
|
||||
local AttackTasks = {}
|
||||
|
||||
@@ -500,13 +474,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
|
||||
@@ -518,28 +492,20 @@ function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To )
|
||||
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:F("No targets found -> Going back to Patrolling")
|
||||
self:__Abort( 1 )
|
||||
self:__Route( 1 )
|
||||
self:SetDetectionActivated()
|
||||
else
|
||||
|
||||
AttackTasks[#AttackTasks+1] = Controllable:TaskFunction( "AI_CAP_ZONE.EngageRoute", self )
|
||||
EngageRoute[1].task = Controllable:TaskCombo( AttackTasks )
|
||||
|
||||
--- 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, "_NewEngageCapRoute" )
|
||||
|
||||
self:SetDetectionDeactivated()
|
||||
end
|
||||
|
||||
--- NOW ROUTE THE GROUP!
|
||||
self.Controllable:WayPointExecute( 1, 2 )
|
||||
Controllable:Route( EngageRoute, 0.5 )
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,44 +1,27 @@
|
||||
--- **AI** -- **Provide Close Air Support to friendly ground troops.**
|
||||
--- **AI** -- (R2.1) - Manages the independent process of Close Air Support for airplanes.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- 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)
|
||||
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/CAS%20-%20Close%20Air%20Support)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # **AUTHORS and CONTRIBUTIONS**
|
||||
--
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ### [YouTube Playlist](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl3JBO1WDqqpyYRRmIkR2ir2)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **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.
|
||||
--
|
||||
-- ### Authors:
|
||||
--
|
||||
-- * **FlightControl**: Concept, Design & Programming.
|
||||
-- ===
|
||||
--
|
||||
-- @module AI_Cas
|
||||
|
||||
@@ -107,11 +90,11 @@
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- # 1. AI_CAS_ZONE constructor
|
||||
-- ## AI_CAS_ZONE constructor
|
||||
--
|
||||
-- * @{#AI_CAS_ZONE.New}(): Creates a new AI_CAS_ZONE object.
|
||||
--
|
||||
-- ## 2. AI_CAS_ZONE is a FSM
|
||||
-- ## AI_CAS_ZONE is a FSM
|
||||
--
|
||||
-- 
|
||||
--
|
||||
@@ -374,12 +357,15 @@ function AI_CAS_ZONE:onafterStart( Controllable, From, Event, To )
|
||||
self:SetDetectionDeactivated() -- When not engaging, set the detection off.
|
||||
end
|
||||
|
||||
--- @param Wrapper.Controllable#CONTROLLABLE AIControllable
|
||||
function _NewEngageRoute( AIControllable )
|
||||
--- @param AI.AI_CAS#AI_CAS_ZONE
|
||||
-- @param Wrapper.Group#GROUP EngageGroup
|
||||
function AI_CAS_ZONE.EngageRoute( EngageGroup, Fsm )
|
||||
|
||||
AIControllable:T( "NewEngageRoute" )
|
||||
local EngageZone = AIControllable:GetState( AIControllable, "EngageZone" ) -- AI.AI_Cas#AI_CAS_ZONE
|
||||
EngageZone:__Engage( 1, EngageZone.EngageSpeed, EngageZone.EngageAltitude, EngageZone.EngageWeaponExpend, EngageZone.EngageAttackQty, EngageZone.EngageDirection )
|
||||
EngageGroup:F( { "AI_CAS_ZONE.EngageRoute:", EngageGroup:GetName() } )
|
||||
|
||||
if EngageGroup:IsAlive() then
|
||||
Fsm:__Engage( 1, Fsm.EngageSpeed, Fsm.EngageAltitude, Fsm.EngageWeaponExpend, Fsm.EngageAttackQty, Fsm.EngageDirection )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -401,7 +387,6 @@ end
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_CAS_ZONE:onafterTarget( Controllable, From, Event, To )
|
||||
self:E("onafterTarget")
|
||||
|
||||
if Controllable:IsAlive() then
|
||||
|
||||
@@ -412,7 +397,7 @@ function AI_CAS_ZONE:onafterTarget( Controllable, From, Event, To )
|
||||
if DetectedUnit:IsAlive() then
|
||||
if DetectedUnit:IsInZone( self.EngageZone ) then
|
||||
if Detected == true then
|
||||
self:E( {"Target: ", DetectedUnit } )
|
||||
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 )
|
||||
@@ -465,6 +450,9 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
|
||||
|
||||
if Controllable:IsAlive() then
|
||||
|
||||
Controllable:OptionROEOpenFire()
|
||||
Controllable:OptionROTVertical()
|
||||
|
||||
local EngageRoute = {}
|
||||
|
||||
--- Calculate the current route point.
|
||||
@@ -474,7 +462,7 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
|
||||
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(
|
||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
@@ -486,12 +474,12 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
|
||||
|
||||
local AttackTasks = {}
|
||||
|
||||
for DetectedUnitID, DetectedUnit in pairs( self.DetectedUnits ) do
|
||||
for DetectedUnit, Detected 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 } )
|
||||
self:F( {"Engaging ", DetectedUnit } )
|
||||
AttackTasks[#AttackTasks+1] = Controllable:TaskAttackUnit( DetectedUnit,
|
||||
true,
|
||||
EngageWeaponExpend,
|
||||
@@ -504,7 +492,8 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
|
||||
end
|
||||
end
|
||||
|
||||
EngageRoute[1].task = Controllable:TaskCombo( AttackTasks )
|
||||
AttackTasks[#AttackTasks+1] = Controllable:TaskFunction( "AI_CAS_ZONE.EngageRoute", self )
|
||||
EngageRoute[#EngageRoute].task = Controllable:TaskCombo( AttackTasks )
|
||||
|
||||
--- Define a random point in the @{Zone}. The AI will fly to that point within the zone.
|
||||
|
||||
@@ -516,7 +505,7 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
|
||||
local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, self.EngageAltitude, ToTargetVec2.y )
|
||||
|
||||
--- Create a route point of type air.
|
||||
local ToTargetRoutePoint = ToTargetPointVec3:RoutePointAir(
|
||||
local ToTargetRoutePoint = ToTargetPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
@@ -525,22 +514,10 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
|
||||
)
|
||||
|
||||
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 )
|
||||
|
||||
Controllable:Route( EngageRoute, 0.5 )
|
||||
|
||||
--- 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()
|
||||
|
||||
self:SetDetectionInterval( 2 )
|
||||
self:SetRefreshTimeInterval( 2 )
|
||||
self:SetDetectionActivated()
|
||||
self:__Target( -2 ) -- Start Targetting
|
||||
end
|
||||
|
||||
680
Moose Development/Moose/AI/AI_Cargo_APC.lua
Normal file
680
Moose Development/Moose/AI/AI_Cargo_APC.lua
Normal file
@@ -0,0 +1,680 @@
|
||||
--- **AI** -- (R2.3) - Models the intelligent transportation of infantry and other cargo.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI_Cargo_APC
|
||||
|
||||
--- @type AI_CARGO_APC
|
||||
-- @extends Core.Fsm#FSM_CONTROLLABLE
|
||||
|
||||
|
||||
--- # AI\_CARGO\_APC class, extends @{Core.Base#BASE}
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- AI\_CARGO\APC brings a dynamic cargo handling capability for AI groups.
|
||||
--
|
||||
-- Armoured Personnel Carriers (APC), Trucks, Jeeps and other ground based carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation.
|
||||
-- The AI\_CARGO\APC module uses the @{Cargo} capabilities within the MOOSE framework.
|
||||
-- CARGO derived objects must be declared within the mission to make the AI\_CARGO\APC object recognize the cargo.
|
||||
-- Please consult the @{Cargo} module for more information.
|
||||
--
|
||||
-- ## Cargo loading.
|
||||
--
|
||||
-- The module will load automatically cargo when the APCs are within boarding or loading range.
|
||||
-- The boarding or loading range is specified when the cargo is created in the simulation, and therefore, this range depends on the type of cargo
|
||||
-- and the specified boarding range.
|
||||
--
|
||||
-- ## Enemies nearby.
|
||||
--
|
||||
-- When the APCs are approaching enemy units, something special is happening.
|
||||
-- The APCs will stop moving, and the loaded infantry will unboard and follow the APCs and will help to defend the group.
|
||||
-- The carrier will hold the route once the unboarded infantry is further than 50 meters from the APCs,
|
||||
-- to ensure that the APCs are not too far away from the following running infantry.
|
||||
-- Once all enemies are cleared, the infantry will board again automatically into the APCs. Once boarded, the APCs will follow its pre-defined route.
|
||||
--
|
||||
-- A combat range needs to be specified in meters at the @{#AI_CARGO_APC.New}() method.
|
||||
-- This combat range will trigger the unboarding of troops when enemies are within the combat range around the APCs.
|
||||
-- During my tests, I've noticed that there is a balance between ensuring that the infantry is within sufficient hit range (effectiveness) versus
|
||||
-- vulnerability of the infantry. It all depends on the kind of enemies that are expected to be encountered.
|
||||
-- A combat range of 350 meters to 500 meters has been proven to be the most effective and efficient.
|
||||
--
|
||||
-- ## Infantry health.
|
||||
--
|
||||
-- When infantry is unboarded from the APCs, the infantry is actually respawned into the battlefield.
|
||||
-- As a result, the unboarding infantry is very _healthy_ every time it unboards.
|
||||
-- This is due to the limitation of the DCS simulator, which is not able to specify the health of new spawned units as a parameter.
|
||||
-- However, infantry that was destroyed when unboarded and following the APCs, won't be respawned again. Destroyed is destroyed.
|
||||
-- As a result, there is some additional strength that is gained when an unboarding action happens, but in terms of simulation balance this has
|
||||
-- marginal impact on the overall battlefield simulation. Fortunately, the firing strength of infantry is limited, and thus, respacing healthy infantry every
|
||||
-- time is not so much of an issue ...
|
||||
--
|
||||
-- ## Control the APCs on the map.
|
||||
--
|
||||
-- It is possible also as a human ground commander to influence the path of the APCs, by pointing a new path using the DCS user interface on the map.
|
||||
-- In this case, the APCs will change the direction towards its new indicated route. However, there is a catch!
|
||||
-- Once the APCs are near the enemy, and infantry is unboarded, the APCs won't be able to hold the route until the infantry could catch up.
|
||||
-- The APCs will simply drive on and won't stop! This is a limitation in ED that prevents user actions being controlled by the scripting engine.
|
||||
-- No workaround is possible on this.
|
||||
--
|
||||
-- ## Cargo deployment.
|
||||
--
|
||||
-- Using the @{#AI_CARGO_APC.Deploy}() method, you are able to direct the APCs towards a point on the battlefield to unboard/unload the cargo at the specific coordinate.
|
||||
-- The APCs will follow nearby roads as much as possible, to ensure fast and clean cargo transportation between the objects and villages in the simulation environment.
|
||||
--
|
||||
-- ## Cargo pickup.
|
||||
--
|
||||
-- Using the @{#AI_CARGO_APC.Pickup}() method, you are able to direct the APCs towards a point on the battlefield to board/load the cargo at the specific coordinate.
|
||||
-- The APCs will follow nearby roads as much as possible, to ensure fast and clean cargo transportation between the objects and villages in the simulation environment.
|
||||
--
|
||||
--
|
||||
--
|
||||
-- @field #AI_CARGO_APC
|
||||
AI_CARGO_APC = {
|
||||
ClassName = "AI_CARGO_APC",
|
||||
Coordinate = nil, -- Core.Point#COORDINATE,
|
||||
APC_Cargo = {},
|
||||
}
|
||||
|
||||
--- Creates a new AI_CARGO_APC object.
|
||||
-- @param #AI_CARGO_APC self
|
||||
-- @param Wrapper.Group#GROUP CargoCarrier
|
||||
-- @param Core.Set#SET_CARGO CargoSet
|
||||
-- @param #number CombatRadius
|
||||
-- @return #AI_CARGO_APC
|
||||
function AI_CARGO_APC:New( CargoCarrier, CargoSet, CombatRadius )
|
||||
|
||||
local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- #AI_CARGO_APC
|
||||
|
||||
self.CargoSet = CargoSet -- Core.Set#SET_CARGO
|
||||
self.CombatRadius = CombatRadius
|
||||
|
||||
self:SetStartState( "Unloaded" )
|
||||
|
||||
self:AddTransition( "Unloaded", "Pickup", "*" )
|
||||
self:AddTransition( "Loaded", "Deploy", "*" )
|
||||
|
||||
self:AddTransition( "*", "Load", "Boarding" )
|
||||
self:AddTransition( "Boarding", "Board", "Boarding" )
|
||||
self:AddTransition( "Boarding", "Loaded", "Loaded" )
|
||||
self:AddTransition( "Loaded", "Unload", "Unboarding" )
|
||||
self:AddTransition( "Unboarding", "Unboard", "Unboarding" )
|
||||
self:AddTransition( { "Unboarding", "Unloaded" }, "Unloaded", "Unloaded" )
|
||||
|
||||
self:AddTransition( "*", "Monitor", "*" )
|
||||
self:AddTransition( "*", "Follow", "Following" )
|
||||
self:AddTransition( "*", "Guard", "Unloaded" )
|
||||
self:AddTransition( "*", "Home", "*" )
|
||||
|
||||
self:AddTransition( "*", "Destroyed", "Destroyed" )
|
||||
|
||||
|
||||
--- Pickup Handler OnBefore for AI_CARGO_APC
|
||||
-- @function [parent=#AI_CARGO_APC] OnBeforePickup
|
||||
-- @param #AI_CARGO_APC self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @return #boolean
|
||||
|
||||
--- Pickup Handler OnAfter for AI_CARGO_APC
|
||||
-- @function [parent=#AI_CARGO_APC] OnAfterPickup
|
||||
-- @param #AI_CARGO_APC self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
|
||||
--- Pickup Trigger for AI_CARGO_APC
|
||||
-- @function [parent=#AI_CARGO_APC] Pickup
|
||||
-- @param #AI_CARGO_APC self
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
|
||||
--- Pickup Asynchronous Trigger for AI_CARGO_APC
|
||||
-- @function [parent=#AI_CARGO_APC] __Pickup
|
||||
-- @param #AI_CARGO_APC self
|
||||
-- @param #number Delay
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
|
||||
--- Deploy Handler OnBefore for AI_CARGO_APC
|
||||
-- @function [parent=#AI_CARGO_APC] OnBeforeDeploy
|
||||
-- @param #AI_CARGO_APC self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @return #boolean
|
||||
|
||||
--- Deploy Handler OnAfter for AI_CARGO_APC
|
||||
-- @function [parent=#AI_CARGO_APC] OnAfterDeploy
|
||||
-- @param #AI_CARGO_APC self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
|
||||
--- Deploy Trigger for AI_CARGO_APC
|
||||
-- @function [parent=#AI_CARGO_APC] Deploy
|
||||
-- @param #AI_CARGO_APC self
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
|
||||
--- Deploy Asynchronous Trigger for AI_CARGO_APC
|
||||
-- @function [parent=#AI_CARGO_APC] __Deploy
|
||||
-- @param #AI_CARGO_APC self
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @param #number Delay
|
||||
|
||||
|
||||
--- Loaded Handler OnAfter for AI_CARGO_APC
|
||||
-- @function [parent=#AI_CARGO_APC] OnAfterLoaded
|
||||
-- @param #AI_CARGO_APC self
|
||||
-- @param Wrapper.Group#GROUP APC
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
--- Unloaded Handler OnAfter for AI_CARGO_APC
|
||||
-- @function [parent=#AI_CARGO_APC] OnAfterUnloaded
|
||||
-- @param #AI_CARGO_APC self
|
||||
-- @param Wrapper.Group#GROUP APC
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
|
||||
self:__Monitor( 1 )
|
||||
|
||||
self:SetCarrier( CargoCarrier )
|
||||
self.Transporting = false
|
||||
self.Relocating = false
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set the Carrier.
|
||||
-- @param #AI_CARGO_APC self
|
||||
-- @param Wrapper.Group#GROUP CargoCarrier
|
||||
-- @return #AI_CARGO_APC
|
||||
function AI_CARGO_APC:SetCarrier( CargoCarrier )
|
||||
|
||||
self.CargoCarrier = CargoCarrier -- Wrapper.Group#GROUP
|
||||
self.CargoCarrier:SetState( self.CargoCarrier, "AI_CARGO_APC", self )
|
||||
|
||||
CargoCarrier:HandleEvent( EVENTS.Dead )
|
||||
CargoCarrier:HandleEvent( EVENTS.Hit )
|
||||
|
||||
function CargoCarrier:OnEventDead( EventData )
|
||||
self:F({"dead"})
|
||||
local AICargoTroops = self:GetState( self, "AI_CARGO_APC" )
|
||||
self:F({AICargoTroops=AICargoTroops})
|
||||
if AICargoTroops then
|
||||
self:F({})
|
||||
if not AICargoTroops:Is( "Loaded" ) then
|
||||
-- There are enemies within combat range. Unload the CargoCarrier.
|
||||
AICargoTroops:Destroyed()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function CargoCarrier:OnEventHit( EventData )
|
||||
self:F({"hit"})
|
||||
local AICargoTroops = self:GetState( self, "AI_CARGO_APC" )
|
||||
if AICargoTroops then
|
||||
self:F( { OnHitLoaded = AICargoTroops:Is( "Loaded" ) } )
|
||||
if AICargoTroops:Is( "Loaded" ) or AICargoTroops:Is( "Boarding" ) then
|
||||
-- There are enemies within combat range. Unload the CargoCarrier.
|
||||
AICargoTroops:Unload( false )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self.Zone = ZONE_UNIT:New( self.CargoCarrier:GetName() .. "-Zone", self.CargoCarrier, self.CombatRadius )
|
||||
self.Coalition = self.CargoCarrier:GetCoalition()
|
||||
|
||||
self:SetControllable( CargoCarrier )
|
||||
|
||||
self:Guard()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
function AI_CARGO_APC:IsTransporting()
|
||||
|
||||
return self.Transporting == true
|
||||
end
|
||||
|
||||
function AI_CARGO_APC:IsRelocating()
|
||||
|
||||
return self.Relocating == true
|
||||
end
|
||||
|
||||
--- Find a free Carrier within a range.
|
||||
-- @param #AI_CARGO_APC self
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @param #number Radius
|
||||
-- @return Wrapper.Group#GROUP NewCarrier
|
||||
function AI_CARGO_APC:FindCarrier( Coordinate, Radius )
|
||||
|
||||
local CoordinateZone = ZONE_RADIUS:New( "Zone" , Coordinate:GetVec2(), Radius )
|
||||
CoordinateZone:Scan( { Object.Category.UNIT } )
|
||||
for _, DCSUnit in pairs( CoordinateZone:GetScannedUnits() ) do
|
||||
local NearUnit = UNIT:Find( DCSUnit )
|
||||
self:F({NearUnit=NearUnit})
|
||||
if not NearUnit:GetState( NearUnit, "AI_CARGO_APC" ) then
|
||||
local Attributes = NearUnit:GetDesc()
|
||||
self:F({Desc=Attributes})
|
||||
if NearUnit:HasAttribute( "Trucks" ) then
|
||||
return NearUnit:GetGroup()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Follow Infantry to the Carrier.
|
||||
-- @param #AI_CARGO_APC self
|
||||
-- @param #AI_CARGO_APC Me
|
||||
-- @param Wrapper.Unit#UNIT APCUnit
|
||||
-- @param Cargo.CargoGroup#CARGO_GROUP Cargo
|
||||
-- @return #AI_CARGO_APC
|
||||
function AI_CARGO_APC:FollowToCarrier( Me, APCUnit, CargoGroup )
|
||||
|
||||
local InfantryGroup = CargoGroup:GetGroup()
|
||||
|
||||
self:F( { self = self:GetClassNameAndID(), InfantryGroup = InfantryGroup:GetName() } )
|
||||
|
||||
--if self:Is( "Following" ) then
|
||||
|
||||
if APCUnit:IsAlive() then
|
||||
-- We check if the Cargo is near to the CargoCarrier.
|
||||
if InfantryGroup:IsPartlyInZone( ZONE_UNIT:New( "Radius", APCUnit, 25 ) ) then
|
||||
|
||||
-- The Cargo does not need to follow the Carrier.
|
||||
Me:Guard()
|
||||
|
||||
else
|
||||
|
||||
self:F( { InfantryGroup = InfantryGroup:GetName() } )
|
||||
|
||||
if InfantryGroup:IsAlive() then
|
||||
|
||||
self:F( { InfantryGroup = InfantryGroup:GetName() } )
|
||||
|
||||
local Waypoints = {}
|
||||
|
||||
-- Calculate the new Route.
|
||||
local FromCoord = InfantryGroup:GetCoordinate()
|
||||
local FromGround = FromCoord:WaypointGround( 10, "Diamond" )
|
||||
self:F({FromGround=FromGround})
|
||||
table.insert( Waypoints, FromGround )
|
||||
|
||||
local ToCoord = APCUnit:GetCoordinate():GetRandomCoordinateInRadius( 10, 5 )
|
||||
local ToGround = ToCoord:WaypointGround( 10, "Diamond" )
|
||||
self:F({ToGround=ToGround})
|
||||
table.insert( Waypoints, ToGround )
|
||||
|
||||
local TaskRoute = InfantryGroup:TaskFunction( "AI_CARGO_APC.FollowToCarrier", Me, APCUnit, CargoGroup )
|
||||
|
||||
self:F({Waypoints = Waypoints})
|
||||
local Waypoint = Waypoints[#Waypoints]
|
||||
InfantryGroup:SetTaskWaypoint( Waypoint, TaskRoute ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone.
|
||||
|
||||
InfantryGroup:Route( Waypoints, 1 ) -- Move after a random seconds to the Route. See the Route method for details.
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- @param #AI_CARGO_APC self
|
||||
-- @param Wrapper.Group#GROUP APC
|
||||
function AI_CARGO_APC:onafterMonitor( APC, From, Event, To )
|
||||
self:F( { APC, From, Event, To } )
|
||||
|
||||
if APC and APC:IsAlive() then
|
||||
if self.CarrierCoordinate then
|
||||
if self:IsRelocating() == true then
|
||||
local Coordinate = APC:GetCoordinate()
|
||||
self.Zone:Scan( { Object.Category.UNIT } )
|
||||
if self.Zone:IsAllInZoneOfCoalition( self.Coalition ) then
|
||||
if self:Is( "Unloaded" ) or self:Is( "Following" ) then
|
||||
-- There are no enemies within combat range. Load the CargoCarrier.
|
||||
self:Load()
|
||||
end
|
||||
else
|
||||
if self:Is( "Loaded" ) then
|
||||
-- There are enemies within combat range. Unload the CargoCarrier.
|
||||
self:__Unload( 1, false )
|
||||
else
|
||||
if self:Is( "Unloaded" ) then
|
||||
self:Follow()
|
||||
end
|
||||
if self:Is( "Following" ) then
|
||||
for APCUnit, Cargo in pairs( self.APC_Cargo ) do
|
||||
local Cargo = Cargo -- Cargo.Cargo#CARGO
|
||||
if Cargo:IsAlive() then
|
||||
if not Cargo:IsNear( APCUnit, 40 ) then
|
||||
APCUnit:RouteStop()
|
||||
self.CarrierStopped = true
|
||||
else
|
||||
if self.CarrierStopped then
|
||||
if Cargo:IsNear( APCUnit, 25 ) then
|
||||
APCUnit:RouteResume()
|
||||
self.CarrierStopped = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
self.CarrierCoordinate = APC:GetCoordinate()
|
||||
end
|
||||
|
||||
self:__Monitor( -5 )
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- @param #AI_CARGO_APC self
|
||||
-- @param Wrapper.Group#GROUP APC
|
||||
function AI_CARGO_APC:onbeforeLoad( APC, From, Event, To )
|
||||
self:F( { APC, From, Event, To } )
|
||||
|
||||
local Boarding = false
|
||||
self.BoardingCount = 0
|
||||
|
||||
if APC and APC:IsAlive() then
|
||||
self.APC_Cargo = {}
|
||||
for _, APCUnit in pairs( APC:GetUnits() ) do
|
||||
local APCUnit = APCUnit -- Wrapper.Unit#UNIT
|
||||
for _, Cargo in pairs( self.CargoSet:GetSet() ) do
|
||||
local Cargo = Cargo -- Cargo.Cargo#CARGO
|
||||
self:F( { IsUnLoaded = Cargo:IsUnLoaded(), IsDeployed = Cargo:IsDeployed(), Cargo:GetName(), APC:GetName() } )
|
||||
if Cargo:IsUnLoaded() and not Cargo:IsDeployed() then
|
||||
if Cargo:IsInLoadRadius( APCUnit:GetCoordinate() ) then
|
||||
self:F( { "In radius", APCUnit:GetName() } )
|
||||
APC:RouteStop()
|
||||
--Cargo:Ungroup()
|
||||
Cargo:Board( APCUnit, 25 )
|
||||
self:__Board( 1, Cargo )
|
||||
Boarding = true
|
||||
|
||||
-- So now this APCUnit has Cargo that is being loaded.
|
||||
-- This will be used further in the logic to follow and to check cargo status.
|
||||
self.APC_Cargo[APCUnit] = Cargo
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return Boarding
|
||||
|
||||
end
|
||||
|
||||
--- @param #AI_CARGO_APC self
|
||||
-- @param Wrapper.Group#GROUP APC
|
||||
function AI_CARGO_APC:onafterBoard( APC, From, Event, To, Cargo )
|
||||
self:F( { APC, From, Event, To, Cargo } )
|
||||
|
||||
if APC and APC:IsAlive() then
|
||||
self:F({ IsLoaded = Cargo:IsLoaded(), Cargo:GetName(), APC:GetName() } )
|
||||
if not Cargo:IsLoaded() then
|
||||
self:__Board( 10, Cargo )
|
||||
else
|
||||
self:__Loaded( 1 )
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- @param #AI_CARGO_APC self
|
||||
-- @param Wrapper.Group#GROUP APC
|
||||
function AI_CARGO_APC:onbeforeLoaded( APC, From, Event, To )
|
||||
self:F( { APC, From, Event, To } )
|
||||
|
||||
local Loaded = true
|
||||
|
||||
if APC and APC:IsAlive() then
|
||||
for APCUnit, Cargo in pairs( self.APC_Cargo ) do
|
||||
local Cargo = Cargo -- Cargo.Cargo#CARGO
|
||||
self:F( { IsLoaded = Cargo:IsLoaded(), IsDestroyed = Cargo:IsDestroyed(), Cargo:GetName(), APC:GetName() } )
|
||||
if not Cargo:IsLoaded() and not Cargo:IsDestroyed() then
|
||||
Loaded = false
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if Loaded == true then
|
||||
APC:RouteResume()
|
||||
end
|
||||
|
||||
return Loaded
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- @param #AI_CARGO_APC self
|
||||
-- @param Wrapper.Group#GROUP APC
|
||||
function AI_CARGO_APC:onafterUnload( APC, From, Event, To, Deployed )
|
||||
self:F( { APC, From, Event, To, Deployed } )
|
||||
|
||||
if APC and APC:IsAlive() then
|
||||
for _, APCUnit in pairs( APC:GetUnits() ) do
|
||||
local APCUnit = APCUnit -- Wrapper.Unit#UNIT
|
||||
APC:RouteStop()
|
||||
for _, Cargo in pairs( APCUnit:GetCargo() ) do
|
||||
Cargo:UnBoard()
|
||||
self:__Unboard( 10, Cargo, Deployed )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- @param #AI_CARGO_APC self
|
||||
-- @param Wrapper.Group#GROUP APC
|
||||
function AI_CARGO_APC:onafterUnboard( APC, From, Event, To, Cargo, Deployed )
|
||||
self:F( { APC, From, Event, To, Cargo:GetName() } )
|
||||
|
||||
if APC and APC:IsAlive() then
|
||||
if not Cargo:IsUnLoaded() then
|
||||
self:__Unboard( 10, Cargo, Deployed )
|
||||
else
|
||||
self:__Unloaded( 1, Cargo, Deployed )
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- @param #AI_CARGO_APC self
|
||||
-- @param Wrapper.Group#GROUP APC
|
||||
function AI_CARGO_APC:onbeforeUnloaded( APC, From, Event, To, Cargo, Deployed )
|
||||
self:F( { APC, From, Event, To, Cargo:GetName(), Deployed = Deployed } )
|
||||
|
||||
local AllUnloaded = true
|
||||
|
||||
--Cargo:Regroup()
|
||||
|
||||
if APC and APC:IsAlive() then
|
||||
for _, APCUnit in pairs( APC:GetUnits() ) do
|
||||
local APCUnit = APCUnit -- Wrapper.Unit#UNIT
|
||||
local CargoCheck = self.APC_Cargo[APCUnit]
|
||||
if CargoCheck then
|
||||
self:F( { CargoCheck:GetName(), IsUnLoaded = CargoCheck:IsUnLoaded() } )
|
||||
if CargoCheck:IsUnLoaded() == false then
|
||||
AllUnloaded = false
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if AllUnloaded == true then
|
||||
if Deployed == true then
|
||||
for APCUnit, Cargo in pairs( self.APC_Cargo ) do
|
||||
local Cargo = Cargo -- Cargo.Cargo#CARGO
|
||||
Cargo:SetDeployed( true )
|
||||
end
|
||||
self.APC_Cargo = {}
|
||||
end
|
||||
self:Guard()
|
||||
self.CargoCarrier = APC
|
||||
end
|
||||
end
|
||||
|
||||
self:F( { AllUnloaded = AllUnloaded } )
|
||||
return AllUnloaded
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- @param #AI_CARGO_APC self
|
||||
-- @param Wrapper.Group#GROUP APC
|
||||
function AI_CARGO_APC:onafterFollow( APC, From, Event, To )
|
||||
self:F( { APC, From, Event, To } )
|
||||
|
||||
self:F( "Follow" )
|
||||
if APC and APC:IsAlive() then
|
||||
for APCUnit, Cargo in pairs( self.APC_Cargo ) do
|
||||
local Cargo = Cargo -- Cargo.Cargo#CARGO
|
||||
if Cargo:IsUnLoaded() then
|
||||
self:FollowToCarrier( self, APCUnit, Cargo )
|
||||
APCUnit:RouteResume()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- @param #AI_CARGO_APC
|
||||
-- @param Wrapper.Group#GROUP APC
|
||||
function AI_CARGO_APC._Pickup( APC, self )
|
||||
|
||||
APC:F( { "AI_CARGO_APC._Pickup:", APC:GetName() } )
|
||||
|
||||
if APC:IsAlive() then
|
||||
self:Load()
|
||||
self.Relocating = true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- @param #AI_CARGO_APC
|
||||
-- @param Wrapper.Group#GROUP APC
|
||||
function AI_CARGO_APC._Deploy( APC, self )
|
||||
|
||||
APC:F( { "AI_CARGO_APC._Deploy:", APC } )
|
||||
|
||||
if APC:IsAlive() then
|
||||
self:Unload( true )
|
||||
self.Transporting = false
|
||||
self.Relocating = false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- @param #AI_CARGO_APC self
|
||||
-- @param Wrapper.Group#GROUP APC
|
||||
-- @param From
|
||||
-- @param Event
|
||||
-- @param To
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @param #number Speed
|
||||
-- @param #string EndPointFormation The formation at the end point of the action.
|
||||
function AI_CARGO_APC:onafterPickup( APC, From, Event, To, Coordinate )
|
||||
|
||||
if APC and APC:IsAlive() then
|
||||
|
||||
if Coordinate then
|
||||
self.RoutePickup = true
|
||||
|
||||
local Waypoints = APC:TaskGroundOnRoad( Coordinate, 150, "Line abreast" )
|
||||
|
||||
local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Pickup", self )
|
||||
|
||||
self:F({Waypoints = Waypoints})
|
||||
local Waypoint = Waypoints[#Waypoints]
|
||||
APC:SetTaskWaypoint( Waypoint, TaskFunction ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone.
|
||||
|
||||
APC:Route( Waypoints, 1 ) -- Move after a random seconds to the Route. See the Route method for details.
|
||||
else
|
||||
AI_CARGO_APC._Pickup( APC, self )
|
||||
end
|
||||
|
||||
self.Transporting = true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- @param #AI_CARGO_APC self
|
||||
-- @param Wrapper.Group#GROUP APC
|
||||
-- @param From
|
||||
-- @param Event
|
||||
-- @param To
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @param #number Speed
|
||||
-- @param #string EndPointFormation The formation at the end point of the action.
|
||||
function AI_CARGO_APC:onafterDeploy( APC, From, Event, To, Coordinate )
|
||||
|
||||
if APC and APC:IsAlive() then
|
||||
|
||||
self.RouteDeploy = true
|
||||
|
||||
local Waypoints = APC:TaskGroundOnRoad( Coordinate, 150, "Line abreast" )
|
||||
|
||||
local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Deploy", self )
|
||||
|
||||
self:F({Waypoints = Waypoints})
|
||||
local Waypoint = Waypoints[#Waypoints]
|
||||
APC:SetTaskWaypoint( Waypoint, TaskFunction ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone.
|
||||
|
||||
APC:Route( Waypoints, 1 ) -- Move after a random seconds to the Route. See the Route method for details.
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param Wrapper.Group#GROUP APC
|
||||
-- @param From
|
||||
-- @param Event
|
||||
-- @param To
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @param #number Speed
|
||||
function AI_CARGO_APC:onafterHome( APC, From, Event, To, Coordinate )
|
||||
|
||||
if APC and APC:IsAlive() ~= nil then
|
||||
|
||||
self.RouteHome = true
|
||||
|
||||
local Waypoints = APC:TaskGroundOnRoad( Coordinate, 120, "Line abreast" )
|
||||
|
||||
self:F({Waypoints = Waypoints})
|
||||
local Waypoint = Waypoints[#Waypoints]
|
||||
|
||||
APC:Route( Waypoints, 1 ) -- Move after a random seconds to the Route. See the Route method for details.
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
448
Moose Development/Moose/AI/AI_Cargo_Airplane.lua
Normal file
448
Moose Development/Moose/AI/AI_Cargo_Airplane.lua
Normal file
@@ -0,0 +1,448 @@
|
||||
--- **AI** -- (R2.3) - Models the intelligent transportation of infantry (cargo).
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI_Cargo_Airplane
|
||||
|
||||
--- @type AI_CARGO_AIRPLANE
|
||||
-- @extends Core.Fsm#FSM_CONTROLLABLE
|
||||
|
||||
|
||||
--- # AI\_CARGO\_AIRPLANE class, extends @{Core.Base@BASE}
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #AI_CARGO_AIRPLANE
|
||||
AI_CARGO_AIRPLANE = {
|
||||
ClassName = "AI_CARGO_AIRPLANE",
|
||||
Coordinate = nil -- Core.Point#COORDINATE,
|
||||
}
|
||||
|
||||
--- Creates a new AI_CARGO_AIRPLANE object.
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Group#GROUP Airplane
|
||||
-- @param Core.Set#SET_CARGO CargoSet
|
||||
-- @param #number CombatRadius
|
||||
-- @return #AI_CARGO_AIRPLANE
|
||||
function AI_CARGO_AIRPLANE:New( Airplane, CargoSet )
|
||||
|
||||
local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- #AI_CARGO_AIRPLANE
|
||||
|
||||
self.CargoSet = CargoSet -- Cargo.CargoGroup#CARGO_GROUP
|
||||
|
||||
self:SetStartState( "Unloaded" )
|
||||
|
||||
self:AddTransition( "Unloaded", "Pickup", "*" )
|
||||
self:AddTransition( "Loaded", "Deploy", "*" )
|
||||
|
||||
self:AddTransition( "Unloaded", "Load", "Boarding" )
|
||||
self:AddTransition( "Boarding", "Board", "Boarding" )
|
||||
self:AddTransition( "Boarding", "Loaded", "Loaded" )
|
||||
self:AddTransition( "Loaded", "Unload", "Unboarding" )
|
||||
self:AddTransition( "Unboarding", "Unboard", "Unboarding" )
|
||||
self:AddTransition( "Unboarding", "Unloaded", "Unloaded" )
|
||||
|
||||
self:AddTransition( "*", "Landed", "*" )
|
||||
|
||||
self:AddTransition( "*", "Destroyed", "Destroyed" )
|
||||
|
||||
--- Pickup Handler OnBefore for AI_CARGO_AIRPLANE
|
||||
-- @function [parent=#AI_CARGO_AIRPLANE] OnBeforePickup
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Wrapper.Airbase#AIRBASE Airbase
|
||||
-- @return #boolean
|
||||
|
||||
--- Pickup Handler OnAfter for AI_CARGO_AIRPLANE
|
||||
-- @function [parent=#AI_CARGO_AIRPLANE] OnAfterPickup
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Wrapper.Airbase#AIRBASE Airbase
|
||||
|
||||
--- Pickup Trigger for AI_CARGO_AIRPLANE
|
||||
-- @function [parent=#AI_CARGO_AIRPLANE] Pickup
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Airbase#AIRBASE Airbase
|
||||
|
||||
--- Pickup Asynchronous Trigger for AI_CARGO_AIRPLANE
|
||||
-- @function [parent=#AI_CARGO_AIRPLANE] __Pickup
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param #number Delay
|
||||
-- @param Wrapper.Airbase#AIRBASE Airbase
|
||||
|
||||
--- Deploy Handler OnBefore for AI_CARGO_AIRPLANE
|
||||
-- @function [parent=#AI_CARGO_AIRPLANE] OnBeforeDeploy
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Wrapper.Airbase#AIRBASE Airbase
|
||||
-- @return #boolean
|
||||
|
||||
--- Deploy Handler OnAfter for AI_CARGO_AIRPLANE
|
||||
-- @function [parent=#AI_CARGO_AIRPLANE] OnAfterDeploy
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Wrapper.Airbase#AIRBASE Airbase
|
||||
|
||||
--- Deploy Trigger for AI_CARGO_AIRPLANE
|
||||
-- @function [parent=#AI_CARGO_AIRPLANE] Deploy
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Airbase#AIRBASE Airbase
|
||||
|
||||
--- Deploy Asynchronous Trigger for AI_CARGO_AIRPLANE
|
||||
-- @function [parent=#AI_CARGO_AIRPLANE] __Deploy
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Airbase#AIRBASE Airbase
|
||||
-- @param #number Delay
|
||||
|
||||
|
||||
self:SetCarrier( Airplane )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set the Carrier.
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Group#GROUP Airplane
|
||||
-- @return #AI_CARGO_AIRPLANE
|
||||
function AI_CARGO_AIRPLANE:SetCarrier( Airplane )
|
||||
|
||||
local AICargo = self
|
||||
|
||||
self.Airplane = Airplane -- Wrapper.Group#GROUP
|
||||
self.Airplane:SetState( self.Airplane, "AI_CARGO_AIRPLANE", self )
|
||||
|
||||
self.RoutePickup = false
|
||||
self.RouteDeploy = false
|
||||
|
||||
Airplane:HandleEvent( EVENTS.Dead )
|
||||
Airplane:HandleEvent( EVENTS.Hit )
|
||||
Airplane:HandleEvent( EVENTS.EngineShutdown )
|
||||
|
||||
function Airplane:OnEventDead( EventData )
|
||||
local AICargoTroops = self:GetState( self, "AI_CARGO_AIRPLANE" )
|
||||
self:F({AICargoTroops=AICargoTroops})
|
||||
if AICargoTroops then
|
||||
self:F({})
|
||||
if not AICargoTroops:Is( "Loaded" ) then
|
||||
-- There are enemies within combat range. Unload the Airplane.
|
||||
AICargoTroops:Destroyed()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function Airplane:OnEventHit( EventData )
|
||||
local AICargoTroops = self:GetState( self, "AI_CARGO_AIRPLANE" )
|
||||
if AICargoTroops then
|
||||
self:F( { OnHitLoaded = AICargoTroops:Is( "Loaded" ) } )
|
||||
if AICargoTroops:Is( "Loaded" ) or AICargoTroops:Is( "Boarding" ) then
|
||||
-- There are enemies within combat range. Unload the Airplane.
|
||||
AICargoTroops:Unload()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function Airplane:OnEventEngineShutdown( EventData )
|
||||
AICargo:Landed()
|
||||
end
|
||||
|
||||
self.Coalition = self.Airplane:GetCoalition()
|
||||
|
||||
self:SetControllable( Airplane )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Find a free Carrier within a range.
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Airbase#AIRBASE Airbase
|
||||
-- @param #number Radius
|
||||
-- @return Wrapper.Group#GROUP NewCarrier
|
||||
function AI_CARGO_AIRPLANE:FindCarrier( Coordinate, Radius )
|
||||
|
||||
local CoordinateZone = ZONE_RADIUS:New( "Zone" , Coordinate:GetVec2(), Radius )
|
||||
CoordinateZone:Scan( { Object.Category.UNIT } )
|
||||
for _, DCSUnit in pairs( CoordinateZone:GetScannedUnits() ) do
|
||||
local NearUnit = UNIT:Find( DCSUnit )
|
||||
self:F({NearUnit=NearUnit})
|
||||
if not NearUnit:GetState( NearUnit, "AI_CARGO_AIRPLANE" ) then
|
||||
local Attributes = NearUnit:GetDesc()
|
||||
self:F({Desc=Attributes})
|
||||
if NearUnit:HasAttribute( "Trucks" ) then
|
||||
self:SetCarrier( NearUnit )
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Group#GROUP Airplane
|
||||
-- @param From
|
||||
-- @param Event
|
||||
-- @param To
|
||||
-- @param Wrapper.Airbase#AIRBASE Airbase
|
||||
-- @param #number Speed
|
||||
function AI_CARGO_AIRPLANE:onafterLanded( Airplane, From, Event, To )
|
||||
|
||||
if Airplane and Airplane:IsAlive() then
|
||||
|
||||
if self.RoutePickup == true then
|
||||
self:Load( Airplane:GetPointVec2() )
|
||||
self.RoutePickup = false
|
||||
end
|
||||
|
||||
if self.RouteDeploy == true then
|
||||
self:Unload()
|
||||
self.RouteDeploy = false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Group#GROUP Airplane
|
||||
-- @param From
|
||||
-- @param Event
|
||||
-- @param To
|
||||
-- @param Wrapper.Airbase#AIRBASE Airbase
|
||||
-- @param #number Speed
|
||||
function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Airbase, Speed )
|
||||
|
||||
if Airplane and Airplane:IsAlive() then
|
||||
self:Route( Airplane, Airbase, Speed )
|
||||
self.RoutePickup = true
|
||||
self.Airbase = Airbase
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Group#GROUP Airplane
|
||||
-- @param From
|
||||
-- @param Event
|
||||
-- @param To
|
||||
-- @param Wrapper.Airbase#AIRBASE Airbase
|
||||
-- @param #number Speed
|
||||
function AI_CARGO_AIRPLANE:onafterDeploy( Airplane, From, Event, To, Airbase, Speed )
|
||||
|
||||
if Airplane and Airplane:IsAlive() then
|
||||
self:Route( Airplane, Airbase, Speed )
|
||||
self.RouteDeploy = true
|
||||
self.Airbase = Airbase
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Group#GROUP Airplane
|
||||
function AI_CARGO_AIRPLANE:onafterLoad( Airplane, From, Event, To, Coordinate )
|
||||
|
||||
if Airplane and Airplane:IsAlive() then
|
||||
|
||||
for _, Cargo in pairs( self.CargoSet:GetSet() ) do
|
||||
if Cargo:IsInLoadRadius( Coordinate ) then
|
||||
self:__Board( 5 )
|
||||
Cargo:Board( Airplane, 25 )
|
||||
self.Cargo = Cargo
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Group#GROUP Airplane
|
||||
function AI_CARGO_AIRPLANE:onafterBoard( Airplane, From, Event, To )
|
||||
|
||||
if Airplane and Airplane:IsAlive() then
|
||||
self:F({ IsLoaded = self.Cargo:IsLoaded() } )
|
||||
if not self.Cargo:IsLoaded() then
|
||||
self:__Board( 10 )
|
||||
else
|
||||
self:__Loaded( 1 )
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Group#GROUP Airplane
|
||||
function AI_CARGO_AIRPLANE:onafterLoaded( Airplane, From, Event, To )
|
||||
|
||||
if Airplane and Airplane:IsAlive() then
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Group#GROUP Airplane
|
||||
function AI_CARGO_AIRPLANE:onafterUnload( Airplane, From, Event, To )
|
||||
|
||||
if Airplane and Airplane:IsAlive() then
|
||||
self.Cargo:UnBoard()
|
||||
self:__Unboard( 10 )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Group#GROUP Airplane
|
||||
function AI_CARGO_AIRPLANE:onafterUnboard( Airplane, From, Event, To )
|
||||
|
||||
if Airplane and Airplane:IsAlive() then
|
||||
if not self.Cargo:IsUnLoaded() then
|
||||
self:__Unboard( 10 )
|
||||
else
|
||||
self:__Unloaded( 1 )
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Group#GROUP Airplane
|
||||
function AI_CARGO_AIRPLANE:onafterUnloaded( Airplane, From, Event, To )
|
||||
|
||||
if Airplane and Airplane:IsAlive() then
|
||||
self.Airplane = Airplane
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Group#GROUP Airplane
|
||||
-- @param Wrapper.Airbase#AIRBASE Airbase
|
||||
-- @param #number Speed
|
||||
function AI_CARGO_AIRPLANE:Route( Airplane, Airbase, Speed )
|
||||
|
||||
if Airplane and Airplane:IsAlive() then
|
||||
|
||||
local PointVec3 = Airplane:GetPointVec3()
|
||||
|
||||
local Takeoff = SPAWN.Takeoff.Hot
|
||||
|
||||
local Template = Airplane:GetTemplate()
|
||||
|
||||
if Template then
|
||||
|
||||
local Points = {}
|
||||
|
||||
if self.Airbase then
|
||||
|
||||
local FromWaypoint = Template.route.points[1]
|
||||
|
||||
-- These are only for ships.
|
||||
FromWaypoint.linkUnit = nil
|
||||
FromWaypoint.helipadId = nil
|
||||
FromWaypoint.airdromeId = nil
|
||||
|
||||
local AirbaseID = self.Airbase:GetID()
|
||||
local AirbaseCategory = self.Airbase:GetDesc().category
|
||||
|
||||
FromWaypoint.airdromeId = AirbaseID
|
||||
|
||||
FromWaypoint.alt = 0
|
||||
|
||||
FromWaypoint.type = GROUPTEMPLATE.Takeoff[Takeoff][1] -- type
|
||||
FromWaypoint.action = GROUPTEMPLATE.Takeoff[Takeoff][2] -- action
|
||||
|
||||
|
||||
-- Translate the position of the Group Template to the Vec3.
|
||||
for UnitID = 1, #Template.units do
|
||||
self:T( 'Before Translation SpawnTemplate.units['..UnitID..'].x = ' .. Template.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. Template.units[UnitID].y )
|
||||
|
||||
-- These cause a lot of confusion.
|
||||
local UnitTemplate = Template.units[UnitID]
|
||||
|
||||
UnitTemplate.parking = 15
|
||||
UnitTemplate.parking_id = "1"
|
||||
UnitTemplate.alt = 0
|
||||
|
||||
local SX = UnitTemplate.x
|
||||
local SY = UnitTemplate.y
|
||||
local BX = FromWaypoint.x
|
||||
local BY = FromWaypoint.y
|
||||
local TX = PointVec3.x + ( SX - BX )
|
||||
local TY = PointVec3.z + ( SY - BY )
|
||||
|
||||
UnitTemplate.x = TX
|
||||
UnitTemplate.y = TY
|
||||
|
||||
self:T( 'After Translation SpawnTemplate.units['..UnitID..'].x = ' .. UnitTemplate.x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. UnitTemplate.y )
|
||||
end
|
||||
|
||||
FromWaypoint.x = PointVec3.x
|
||||
FromWaypoint.y = PointVec3.z
|
||||
|
||||
Points[#Points+1] = FromWaypoint
|
||||
else
|
||||
|
||||
local GroupPoint = Airplane:GetVec2()
|
||||
local GroupVelocity = Airplane:GetUnit(1):GetDesc().speedMax
|
||||
|
||||
local FromWaypoint = {}
|
||||
FromWaypoint.x = GroupPoint.x
|
||||
FromWaypoint.y = GroupPoint.y
|
||||
FromWaypoint.type = "Turning Point"
|
||||
FromWaypoint.action = "Turning Point"
|
||||
FromWaypoint.speed = GroupVelocity
|
||||
|
||||
Points[#Points+1] = FromWaypoint
|
||||
end
|
||||
|
||||
local AirbasePointVec2 = Airbase:GetPointVec2()
|
||||
local ToWaypoint = AirbasePointVec2:WaypointAir(
|
||||
POINT_VEC3.RoutePointAltType.BARO,
|
||||
"Land",
|
||||
"Landing",
|
||||
Speed or Airplane:GetUnit(1):GetDesc().speedMax
|
||||
)
|
||||
|
||||
ToWaypoint["airdromeId"] = Airbase:GetID()
|
||||
ToWaypoint["speed_locked"] = true,
|
||||
|
||||
self:F( ToWaypoint )
|
||||
|
||||
Points[#Points+1] = ToWaypoint
|
||||
|
||||
Template.x = PointVec3.x
|
||||
Template.y = PointVec3.z
|
||||
|
||||
self:T3( Points )
|
||||
Template.route.points = Points
|
||||
|
||||
--self:Respawn( Template )
|
||||
|
||||
local GroupSpawned = Airplane:Respawn( Template )
|
||||
|
||||
return GroupSpawned
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
356
Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua
Normal file
356
Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua
Normal file
@@ -0,0 +1,356 @@
|
||||
--- **AI** -- (R2.4) - Models the intelligent transportation of infantry and other cargo.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI_Cargo_Dispatcher
|
||||
|
||||
--- @type AI_CARGO_DISPATCHER
|
||||
-- @extends Core.Fsm#FSM_CONTROLLABLE
|
||||
|
||||
|
||||
--- # AI\_CARGO\_DISPATCHER class, extends @{Core.Base#BASE}
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- AI\_CARGO\_DISPATCHER brings a dynamic cargo handling capability for AI groups.
|
||||
--
|
||||
-- Armoured Personnel APCs (APC), Trucks, Jeeps and other carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation.
|
||||
-- The AI\_CARGO\_DISPATCHER module uses the @{Cargo} capabilities within the MOOSE framework.
|
||||
-- CARGO derived objects must be declared within the mission to make the AI\_CARGO\_DISPATCHER object recognize the cargo.
|
||||
-- Please consult the @{Cargo} module for more information.
|
||||
--
|
||||
--
|
||||
--
|
||||
-- @field #AI_CARGO_DISPATCHER
|
||||
AI_CARGO_DISPATCHER = {
|
||||
ClassName = "AI_CARGO_DISPATCHER",
|
||||
SetAPC = nil,
|
||||
SetDeployZones = nil,
|
||||
AI_CARGO_APC = {}
|
||||
}
|
||||
|
||||
--- @type AI_CARGO_DISPATCHER.AI_CARGO_APC
|
||||
-- @map <Wrapper.Group#GROUP, AI.AI_Cargo_APC#AI_CARGO_APC>
|
||||
|
||||
--- @field #AI_CARGO_DISPATCHER.AI_CARGO_APC
|
||||
AI_CARGO_DISPATCHER.AI_Cargo = {}
|
||||
|
||||
--- @field #AI_CARGO_DISPATCHER.PickupCargo
|
||||
AI_CARGO_DISPATCHER.PickupCargo = {}
|
||||
|
||||
|
||||
|
||||
--- Creates a new AI_CARGO_DISPATCHER object.
|
||||
-- @param #AI_CARGO_DISPATCHER self
|
||||
-- @param Core.Set#SET_GROUP SetAPC
|
||||
-- @param Core.Set#SET_CARGO SetCargo
|
||||
-- @param Core.Set#SET_ZONE SetDeployZone
|
||||
-- @return #AI_CARGO_DISPATCHER
|
||||
-- @usage
|
||||
--
|
||||
-- -- Create a new cargo dispatcher
|
||||
-- SetAPC = SET_GROUP:New():FilterPrefixes( "APC" ):FilterStart()
|
||||
-- SetCargo = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart()
|
||||
-- SetDeployZone = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart()
|
||||
-- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZone )
|
||||
--
|
||||
function AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZones )
|
||||
|
||||
local self = BASE:Inherit( self, FSM:New() ) -- #AI_CARGO_DISPATCHER
|
||||
|
||||
self.SetAPC = SetAPC -- Core.Set#SET_GROUP
|
||||
self.SetCargo = SetCargo -- Core.Set#SET_CARGO
|
||||
self.SetDeployZones = SetDeployZones -- Core.Set#SET_ZONE
|
||||
|
||||
self:SetStartState( "Dispatch" )
|
||||
|
||||
self:AddTransition( "*", "Monitor", "*" )
|
||||
|
||||
self:AddTransition( "*", "Pickup", "*" )
|
||||
self:AddTransition( "*", "Loading", "*" )
|
||||
self:AddTransition( "*", "Loaded", "*" )
|
||||
|
||||
self:AddTransition( "*", "Deploy", "*" )
|
||||
self:AddTransition( "*", "Unloading", "*" )
|
||||
self:AddTransition( "*", "Unloaded", "*" )
|
||||
|
||||
self:AddTransition( "*", "Home", "*" )
|
||||
|
||||
self.MonitorTimeInterval = 30
|
||||
self.DeployRadiusInner = 200
|
||||
self.DeployRadiusOuter = 500
|
||||
|
||||
self.CarrierHome = {}
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set the home zone.
|
||||
-- When there is nothing anymore to pickup, the carriers will go to a random coordinate in this zone.
|
||||
-- They will await here new orders.
|
||||
-- @param #AI_CARGO_DISPATCHER self
|
||||
-- @param Core.Zone#ZONE_BASE HomeZone
|
||||
-- @return #AI_CARGO_DISPATCHER
|
||||
-- @usage
|
||||
--
|
||||
-- -- Create a new cargo dispatcher
|
||||
-- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZone )
|
||||
--
|
||||
-- -- Set the home coordinate
|
||||
-- local HomeZone = ZONE:New( "Home" )
|
||||
-- AICargoDispatcher:SetHomeZone( HomeZone )
|
||||
--
|
||||
function AI_CARGO_DISPATCHER:SetHomeZone( HomeZone )
|
||||
|
||||
self.HomeZone = HomeZone
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Sets or randomizes the pickup location for the carrier around the cargo coordinate in a radius defined an outer and optional inner radius.
|
||||
-- This radius is influencing the location where the carrier will land to pickup the cargo.
|
||||
-- There are two aspects that are very important to remember and take into account:
|
||||
--
|
||||
-- - Ensure that the outer and inner radius are within reporting radius set by the cargo.
|
||||
-- For example, if the cargo has a reporting radius of 400 meters, and the outer and inner radius is set to 500 and 450 respectively,
|
||||
-- then no cargo will be loaded!!!
|
||||
-- - Also take care of the potential cargo position and possible reasons to crash the carrier. This is especially important
|
||||
-- for locations which are crowded with other objects, like in the middle of villages or cities.
|
||||
-- So, for the best operation of cargo operations, always ensure that the cargo is located at open spaces.
|
||||
--
|
||||
-- The default radius is 0, so the center. In case of a polygon zone, a random location will be selected as the center in the zone.
|
||||
-- @param #AI_CARGO_DISPATCHER self
|
||||
-- @param #number OuterRadius The outer radius in meters around the cargo coordinate.
|
||||
-- @param #number InnerRadius (optional) The inner radius in meters around the cargo coordinate.
|
||||
-- @return #AI_CARGO_DISPATCHER
|
||||
-- @usage
|
||||
--
|
||||
-- -- Create a new cargo dispatcher
|
||||
-- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZone )
|
||||
--
|
||||
-- -- Set the carrier to land within a band around the cargo coordinate between 500 and 300 meters!
|
||||
-- AICargoDispatcher:SetPickupRadius( 500, 300 )
|
||||
--
|
||||
function AI_CARGO_DISPATCHER:SetPickupRadius( OuterRadius, InnerRadius )
|
||||
|
||||
OuterRadius = OuterRadius or 0
|
||||
InnerRadius = InnerRadius or OuterRadius
|
||||
|
||||
self.PickupOuterRadius = OuterRadius
|
||||
self.PickupInnerRadius = InnerRadius
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set the speed or randomizes the speed in km/h to pickup the cargo.
|
||||
-- @param #AI_CARGO_DISPATCHER self
|
||||
-- @param #number MaxSpeed (optional) The maximum speed to move to the cargo pickup location.
|
||||
-- @param #number MinSpeed The minimum speed to move to the cargo pickup location.
|
||||
-- @return #AI_CARGO_DISPATCHER
|
||||
-- @usage
|
||||
--
|
||||
-- -- Create a new cargo dispatcher
|
||||
-- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZone )
|
||||
--
|
||||
-- -- Set the minimum pickup speed to be 100 km/h and the maximum speed to be 200 km/h.
|
||||
-- AICargoDispatcher:SetPickupSpeed( 200, 100 )
|
||||
--
|
||||
function AI_CARGO_DISPATCHER:SetPickupSpeed( MaxSpeed, MinSpeed )
|
||||
|
||||
MaxSpeed = MaxSpeed or 999
|
||||
MinSpeed = MinSpeed or MaxSpeed
|
||||
|
||||
self.PickupMinSpeed = MinSpeed
|
||||
self.PickupMaxSpeed = MaxSpeed
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Sets or randomizes the deploy location for the carrier around the cargo coordinate in a radius defined an outer and an optional inner radius.
|
||||
-- This radius is influencing the location where the carrier will land to deploy the cargo.
|
||||
-- There is an aspect that is very important to remember and take into account:
|
||||
--
|
||||
-- - Take care of the potential cargo position and possible reasons to crash the carrier. This is especially important
|
||||
-- for locations which are crowded with other objects, like in the middle of villages or cities.
|
||||
-- So, for the best operation of cargo operations, always ensure that the cargo is located at open spaces.
|
||||
--
|
||||
-- The default radius is 0, so the center. In case of a polygon zone, a random location will be selected as the center in the zone.
|
||||
-- @param #AI_CARGO_DISPATCHER self
|
||||
-- @param #number OuterRadius The outer radius in meters around the cargo coordinate.
|
||||
-- @param #number InnerRadius (optional) The inner radius in meters around the cargo coordinate.
|
||||
-- @return #AI_CARGO_DISPATCHER
|
||||
-- @usage
|
||||
--
|
||||
-- -- Create a new cargo dispatcher
|
||||
-- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZone )
|
||||
--
|
||||
-- -- Set the carrier to land within a band around the cargo coordinate between 500 and 300 meters!
|
||||
-- AICargoDispatcher:SetDeployRadius( 500, 300 )
|
||||
--
|
||||
function AI_CARGO_DISPATCHER:SetDeployRadius( OuterRadius, InnerRadius )
|
||||
|
||||
OuterRadius = OuterRadius or 0
|
||||
InnerRadius = InnerRadius or OuterRadius
|
||||
|
||||
self.DeployOuterRadius = OuterRadius
|
||||
self.DeployInnerRadius = InnerRadius
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Sets or randomizes the speed in km/h to deploy the cargo.
|
||||
-- @param #AI_CARGO_DISPATCHER self
|
||||
-- @param #number MaxSpeed The maximum speed to move to the cargo deploy location.
|
||||
-- @param #number MinSpeed (optional) The minimum speed to move to the cargo deploy location.
|
||||
-- @return #AI_CARGO_DISPATCHER
|
||||
-- @usage
|
||||
--
|
||||
-- -- Create a new cargo dispatcher
|
||||
-- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZone )
|
||||
--
|
||||
-- -- Set the minimum deploy speed to be 100 km/h and the maximum speed to be 200 km/h.
|
||||
-- AICargoDispatcher:SetDeploySpeed( 200, 100 )
|
||||
--
|
||||
function AI_CARGO_DISPATCHER:SetDeploySpeed( MaxSpeed, MinSpeed )
|
||||
|
||||
MaxSpeed = MaxSpeed or 999
|
||||
MinSpeed = MinSpeed or MaxSpeed
|
||||
|
||||
self.DeployMinSpeed = MinSpeed
|
||||
self.DeployMaxSpeed = MaxSpeed
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- The Start trigger event, which actually takes action at the specified time interval.
|
||||
-- @param #AI_CARGO_DISPATCHER self
|
||||
-- @param Wrapper.Group#GROUP APC
|
||||
-- @return #AI_CARGO_DISPATCHER
|
||||
function AI_CARGO_DISPATCHER:onafterMonitor()
|
||||
|
||||
for APCGroupName, Carrier in pairs( self.SetAPC:GetSet() ) do
|
||||
local Carrier = Carrier -- Wrapper.Group#GROUP
|
||||
local AI_Cargo = self.AI_Cargo[Carrier]
|
||||
if not AI_Cargo then
|
||||
|
||||
-- ok, so this APC does not have yet an AI_CARGO_APC object...
|
||||
-- let's create one and also declare the Loaded and UnLoaded handlers.
|
||||
self.AI_Cargo[Carrier] = self:AICargo( Carrier, self.SetCargo, self.CombatRadius )
|
||||
AI_Cargo = self.AI_Cargo[Carrier]
|
||||
|
||||
function AI_Cargo.OnAfterPickup( AI_Cargo, APC, From, Event, To, Cargo )
|
||||
self:Pickup( APC, Cargo )
|
||||
end
|
||||
|
||||
function AI_Cargo.OnAfterLoad( AI_Cargo, APC )
|
||||
self:Loading( APC )
|
||||
end
|
||||
|
||||
function AI_Cargo.OnAfterLoaded( AI_Cargo, APC, From, Event, To, Cargo )
|
||||
self:Loaded( APC, Cargo )
|
||||
end
|
||||
|
||||
function AI_Cargo.OnAfterDeploy( AI_Cargo, APC )
|
||||
self:Deploy( APC )
|
||||
end
|
||||
|
||||
function AI_Cargo.OnAfterUnload( AI_Cargo, APC )
|
||||
self:Unloading( APC )
|
||||
end
|
||||
|
||||
function AI_Cargo.OnAfterUnloaded( AI_Cargo, APC )
|
||||
self:Unloaded( APC )
|
||||
end
|
||||
end
|
||||
|
||||
-- The Pickup sequence ...
|
||||
-- Check if this APC need to go and Pickup something...
|
||||
self:I( { IsTransporting = AI_Cargo:IsTransporting() } )
|
||||
if AI_Cargo:IsTransporting() == false then
|
||||
-- ok, so there is a free APC
|
||||
-- now find the first cargo that is Unloaded
|
||||
|
||||
local PickupCargo = nil
|
||||
|
||||
for CargoName, Cargo in pairs( self.SetCargo:GetSet() ) do
|
||||
local Cargo = Cargo -- Cargo.Cargo#CARGO
|
||||
self:F( { Cargo = Cargo:GetName(), UnLoaded = Cargo:IsUnLoaded(), Deployed = Cargo:IsDeployed(), PickupCargo = self.PickupCargo[Cargo] ~= nil } )
|
||||
if Cargo:IsUnLoaded() and not Cargo:IsDeployed() then
|
||||
local CargoCoordinate = Cargo:GetCoordinate()
|
||||
local CoordinateFree = true
|
||||
for APC, Coordinate in pairs( self.PickupCargo ) do
|
||||
if CargoCoordinate:Get2DDistance( Coordinate ) <= 25 then
|
||||
CoordinateFree = false
|
||||
break
|
||||
end
|
||||
end
|
||||
if CoordinateFree == true then
|
||||
self.PickupCargo[Carrier] = CargoCoordinate
|
||||
PickupCargo = Cargo
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if PickupCargo then
|
||||
self.CarrierHome[Carrier] = nil
|
||||
local PickupCoordinate = PickupCargo:GetCoordinate():GetRandomCoordinateInRadius( self.PickupOuterRadius, self.PickupInnerRadius )
|
||||
AI_Cargo:Pickup( PickupCoordinate, math.random( self.PickupMinSpeed, self.PickupMaxSpeed ) )
|
||||
break
|
||||
else
|
||||
if self.HomeZone then
|
||||
if not self.CarrierHome[Carrier] then
|
||||
self.CarrierHome[Carrier] = true
|
||||
AI_Cargo:__Home( 10, self.HomeZone:GetRandomPointVec2() )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self:__Monitor( self.MonitorTimeInterval )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Make a APC run for a cargo deploy action after the cargo Pickup trigger has been initiated, by default.
|
||||
-- @param #AI_CARGO_DISPATCHER self
|
||||
-- @param Wrapper.Group#GROUP APC
|
||||
-- @return #AI_CARGO_DISPATCHER
|
||||
function AI_CARGO_DISPATCHER:onafterPickup( From, Event, To, APC, Cargo )
|
||||
return self
|
||||
end
|
||||
|
||||
--- Make a APC run for a cargo deploy action after the cargo has been loaded, by default.
|
||||
-- @param #AI_CARGO_DISPATCHER self
|
||||
-- @param Wrapper.Group#GROUP APC
|
||||
-- @return #AI_CARGO_DISPATCHER
|
||||
function AI_CARGO_DISPATCHER:OnAfterLoaded( From, Event, To, APC, Cargo )
|
||||
|
||||
self:I( { "Loaded Dispatcher", APC } )
|
||||
local DeployZone = self.SetDeployZones:GetRandomZone()
|
||||
self:I( { RandomZone = DeployZone } )
|
||||
|
||||
local DeployCoordinate = DeployZone:GetCoordinate():GetRandomCoordinateInRadius( self.DeployOuterRadius, self.DeployInnerRadius )
|
||||
self.AI_Cargo[APC]:Deploy( DeployCoordinate, math.random( self.DeployMinSpeed, self.DeployMaxSpeed ) )
|
||||
|
||||
self.PickupCargo[APC] = nil
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
67
Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua
Normal file
67
Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua
Normal file
@@ -0,0 +1,67 @@
|
||||
--- **AI** -- (R2.4) - Models the intelligent transportation of infantry and other cargo using APCs.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI_Cargo_Dispatcher_APC
|
||||
|
||||
--- @type AI_CARGO_DISPATCHER_APC
|
||||
-- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER
|
||||
|
||||
|
||||
--- # AI\_CARGO\_DISPATCHER\_APC class, extends @{Core.Base#BASE}
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- AI\_CARGO\_DISPATCHER\_APC brings a dynamic cargo handling capability for AI groups.
|
||||
--
|
||||
-- Armoured Personnel APCs (APC), Trucks, Jeeps and other carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation.
|
||||
-- The AI\_CARGO\_DISPATCHER\_APC module uses the @{Cargo} capabilities within the MOOSE framework.
|
||||
-- CARGO derived objects must be declared within the mission to make the AI\_CARGO\_DISPATCHER\_APC object recognize the cargo.
|
||||
-- Please consult the @{Cargo} module for more information.
|
||||
--
|
||||
--
|
||||
--
|
||||
-- @field #AI_CARGO_DISPATCHER_APC
|
||||
AI_CARGO_DISPATCHER_APC = {
|
||||
ClassName = "AI_CARGO_DISPATCHER_APC",
|
||||
}
|
||||
|
||||
--- Creates a new AI_CARGO_DISPATCHER_APC object.
|
||||
-- @param #AI_CARGO_DISPATCHER_APC self
|
||||
-- @param Core.Set#SET_GROUP SetAPC
|
||||
-- @param Core.Set#SET_CARGO SetCargo
|
||||
-- @param Core.Set#SET_ZONE SetDeployZone
|
||||
-- @return #AI_CARGO_DISPATCHER_APC
|
||||
-- @usage
|
||||
--
|
||||
-- -- Create a new cargo dispatcher
|
||||
-- SetAPC = SET_GROUP:New():FilterPrefixes( "APC" ):FilterStart()
|
||||
-- SetCargo = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart()
|
||||
-- SetDeployZone = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart()
|
||||
-- AICargoDispatcher = AI_CARGO_DISPATCHER_APC:New( SetAPC, SetCargo )
|
||||
--
|
||||
function AI_CARGO_DISPATCHER_APC:New( SetAPC, SetCargo, SetDeployZones )
|
||||
|
||||
local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZones ) ) -- #AI_CARGO_DISPATCHER_APC
|
||||
|
||||
self.CombatRadius = 500
|
||||
|
||||
self:SetDeploySpeed( 70, 120 )
|
||||
self:SetPickupSpeed( 70, 120 )
|
||||
self:SetPickupRadius( 0, 0 )
|
||||
self:SetDeployRadius( 0, 0 )
|
||||
|
||||
self:Monitor( 1 )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
function AI_CARGO_DISPATCHER_APC:AICargo( APC, SetCargo )
|
||||
|
||||
return AI_CARGO_APC:New( APC, SetCargo, self.CombatRadius )
|
||||
end
|
||||
54
Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua
Normal file
54
Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua
Normal file
@@ -0,0 +1,54 @@
|
||||
--- **AI** -- (R2.4) - Models the intelligent transportation of infantry and other cargo using Planes.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI_Cargo_Dispatcher_Airplane
|
||||
|
||||
--- @type AI_CARGO_DISPATCHER_AIRPLANE
|
||||
-- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER
|
||||
|
||||
|
||||
--- # AI\_CARGO\_DISPATCHER\_AIRPLANE class, extends @{Core.Base#BASE}
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- AI\_CARGO\_DISPATCHER\_AIRPLANE brings a dynamic cargo handling capability for AI groups.
|
||||
--
|
||||
-- Airplanes can be mobilized to intelligently transport infantry and other cargo within the simulation.
|
||||
-- The AI\_CARGO\_DISPATCHER\_AIRPLANE module uses the @{Cargo} capabilities within the MOOSE framework.
|
||||
-- CARGO derived objects must be declared within the mission to make the AI\_CARGO\_DISPATCHER\_AIRPLANE object recognize the cargo.
|
||||
-- Please consult the @{Cargo} module for more information.
|
||||
--
|
||||
--
|
||||
--
|
||||
-- @field #AI_CARGO_DISPATCHER_AIRPLANE
|
||||
AI_CARGO_DISPATCHER_AIRPLANE = {
|
||||
ClassName = "AI_CARGO_DISPATCHER_AIRPLANE",
|
||||
}
|
||||
|
||||
--- Creates a new AI_CARGO_DISPATCHER_AIRPLANE object.
|
||||
-- @param #AI_CARGO_DISPATCHER_AIRPLANE self
|
||||
-- @param Core.Set#SET_GROUP SetAirplane
|
||||
-- @param Core.Set#SET_CARGO SetCargo
|
||||
-- @param Core.Set#SET_ZONE SetDeployZone
|
||||
-- @return #AI_CARGO_DISPATCHER_AIRPLANE
|
||||
-- @usage
|
||||
--
|
||||
-- -- Create a new cargo dispatcher
|
||||
-- SetAirplane = SET_GROUP:New():FilterPrefixes( "Airplane" ):FilterStart()
|
||||
-- SetCargo = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart()
|
||||
-- SetDeployZone = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart()
|
||||
-- AICargoDispatcher = AI_CARGO_DISPATCHER_AIRPLANE:New( SetAirplane, SetCargo )
|
||||
--
|
||||
function AI_CARGO_DISPATCHER_AIRPLANE:New( SetAirplane, SetCargo, SetDeployZones )
|
||||
|
||||
local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:New( SetAirplane, SetCargo, SetDeployZones ) ) -- #AI_CARGO_DISPATCHER_AIRPLANE
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
--- **AI** -- (R2.4) - Models the intelligent transportation of infantry and other cargo using Helicopters.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI_Cargo_Dispatcher_Helicopter
|
||||
|
||||
--- @type AI_CARGO_DISPATCHER_HELICOPTER
|
||||
-- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER
|
||||
|
||||
|
||||
--- # AI\_CARGO\_DISPATCHER\_HELICOPTER class, extends @{Core.Base#BASE}
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- AI\_CARGO\_DISPATCHER\_HELICOPTER brings a dynamic cargo handling capability for AI groups.
|
||||
--
|
||||
-- Helicopters can be mobilized to intelligently transport infantry and other cargo within the simulation.
|
||||
-- The AI\_CARGO\_DISPATCHER\_HELICOPTER module uses the @{Cargo} capabilities within the MOOSE framework.
|
||||
-- CARGO derived objects must be declared within the mission to make the AI\_CARGO\_DISPATCHER\_HELICOPTER object recognize the cargo.
|
||||
-- Please consult the @{Cargo} module for more information.
|
||||
--
|
||||
--
|
||||
--
|
||||
-- @field #AI_CARGO_DISPATCHER_HELICOPTER
|
||||
AI_CARGO_DISPATCHER_HELICOPTER = {
|
||||
ClassName = "AI_CARGO_DISPATCHER_HELICOPTER",
|
||||
}
|
||||
|
||||
--- Creates a new AI_CARGO_DISPATCHER_HELICOPTER object.
|
||||
-- @param #AI_CARGO_DISPATCHER_HELICOPTER self
|
||||
-- @param Core.Set#SET_GROUP SetHelicopter
|
||||
-- @param Core.Set#SET_CARGO SetCargo
|
||||
-- @param Core.Set#SET_ZONE SetDeployZone
|
||||
-- @return #AI_CARGO_DISPATCHER_HELICOPTER
|
||||
-- @usage
|
||||
--
|
||||
-- -- Create a new cargo dispatcher
|
||||
-- SetHelicopter = SET_GROUP:New():FilterPrefixes( "Helicopter" ):FilterStart()
|
||||
-- SetCargo = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart()
|
||||
-- SetDeployZone = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart()
|
||||
-- AICargoDispatcher = AI_CARGO_DISPATCHER_HELICOPTER:New( SetHelicopter, SetCargo )
|
||||
--
|
||||
function AI_CARGO_DISPATCHER_HELICOPTER:New( SetHelicopter, SetCargo, SetDeployZones )
|
||||
|
||||
local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:New( SetHelicopter, SetCargo, SetDeployZones ) ) -- #AI_CARGO_DISPATCHER_HELICOPTER
|
||||
|
||||
self:SetDeploySpeed( 200, 150 )
|
||||
self:SetPickupSpeed( 200, 150 )
|
||||
self:SetPickupRadius( 0, 0 )
|
||||
self:SetDeployRadius( 0, 0 )
|
||||
|
||||
self:Monitor( 1 )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function AI_CARGO_DISPATCHER_HELICOPTER:AICargo( Helicopter, SetCargo )
|
||||
|
||||
return AI_CARGO_HELICOPTER:New( Helicopter, SetCargo )
|
||||
end
|
||||
|
||||
685
Moose Development/Moose/AI/AI_Cargo_Helicopter.lua
Normal file
685
Moose Development/Moose/AI/AI_Cargo_Helicopter.lua
Normal file
@@ -0,0 +1,685 @@
|
||||
--- **AI** -- (R2.3) - Models the intelligent transportation of infantry (cargo).
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI_Cargo_Helicopter
|
||||
|
||||
--- @type AI_CARGO_HELICOPTER
|
||||
-- @extends Core.Fsm#FSM_CONTROLLABLE
|
||||
|
||||
|
||||
--- # AI\_CARGO\_TROOPS class, extends @{Core.Base@BASE}
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #AI_CARGO_HELICOPTER
|
||||
AI_CARGO_HELICOPTER = {
|
||||
ClassName = "AI_CARGO_HELICOPTER",
|
||||
Coordinate = nil -- Core.Point#COORDINATE,
|
||||
}
|
||||
|
||||
AI_CARGO_QUEUE = {}
|
||||
|
||||
--- Creates a new AI_CARGO_HELICOPTER object.
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param Wrapper.Group#GROUP Helicopter
|
||||
-- @param Core.Set#SET_CARGO CargoSet
|
||||
-- @param #number CombatRadius
|
||||
-- @return #AI_CARGO_HELICOPTER
|
||||
function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet )
|
||||
|
||||
local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- #AI_CARGO_HELICOPTER
|
||||
|
||||
self.CargoSet = CargoSet -- Cargo.CargoGroup#CARGO_GROUP
|
||||
|
||||
self.Zone = ZONE_GROUP:New( Helicopter:GetName(), Helicopter, 300 )
|
||||
|
||||
self:SetStartState( "Unloaded" )
|
||||
|
||||
self:AddTransition( "Unloaded", "Pickup", "*" )
|
||||
self:AddTransition( "Loaded", "Deploy", "*" )
|
||||
|
||||
self:AddTransition( "Unloaded", "Load", "Boarding" )
|
||||
self:AddTransition( "Boarding", "Board", "Boarding" )
|
||||
self:AddTransition( "Boarding", "Loaded", "Loaded" )
|
||||
self:AddTransition( "Loaded", "Unload", "Unboarding" )
|
||||
self:AddTransition( "Unboarding", "Unboard", "Unboarding" )
|
||||
self:AddTransition( "Unboarding", "Unloaded", "Unloaded" )
|
||||
|
||||
self:AddTransition( "*", "Landed", "*" )
|
||||
self:AddTransition( "*", "Queue", "*" )
|
||||
self:AddTransition( "*", "Orbit" , "*" )
|
||||
self:AddTransition( "*", "Home" , "*" )
|
||||
|
||||
self:AddTransition( "*", "Destroyed", "Destroyed" )
|
||||
|
||||
--- Pickup Handler OnBefore for AI_CARGO_HELICOPTER
|
||||
-- @function [parent=#AI_CARGO_HELICOPTER] OnBeforePickup
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @return #boolean
|
||||
|
||||
--- Pickup Handler OnAfter for AI_CARGO_HELICOPTER
|
||||
-- @function [parent=#AI_CARGO_HELICOPTER] OnAfterPickup
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
|
||||
--- Pickup Trigger for AI_CARGO_HELICOPTER
|
||||
-- @function [parent=#AI_CARGO_HELICOPTER] Pickup
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
|
||||
--- Pickup Asynchronous Trigger for AI_CARGO_HELICOPTER
|
||||
-- @function [parent=#AI_CARGO_HELICOPTER] __Pickup
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param #number Delay
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
|
||||
--- Deploy Handler OnBefore for AI_CARGO_HELICOPTER
|
||||
-- @function [parent=#AI_CARGO_HELICOPTER] OnBeforeDeploy
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @return #boolean
|
||||
|
||||
--- Deploy Handler OnAfter for AI_CARGO_HELICOPTER
|
||||
-- @function [parent=#AI_CARGO_HELICOPTER] OnAfterDeploy
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
|
||||
--- Deploy Trigger for AI_CARGO_HELICOPTER
|
||||
-- @function [parent=#AI_CARGO_HELICOPTER] Deploy
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
|
||||
--- Deploy Asynchronous Trigger for AI_CARGO_HELICOPTER
|
||||
-- @function [parent=#AI_CARGO_HELICOPTER] __Deploy
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @param #number Delay
|
||||
|
||||
|
||||
|
||||
self:SetCarrier( Helicopter )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function AI_CARGO_HELICOPTER:IsTransporting()
|
||||
|
||||
return self.Transporting == true
|
||||
end
|
||||
|
||||
function AI_CARGO_HELICOPTER:IsRelocating()
|
||||
|
||||
return self.Relocating == true
|
||||
end
|
||||
|
||||
|
||||
--- Set the Carrier.
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param Wrapper.Group#GROUP Helicopter
|
||||
-- @return #AI_CARGO_HELICOPTER
|
||||
function AI_CARGO_HELICOPTER:SetCarrier( Helicopter )
|
||||
|
||||
local AICargo = self
|
||||
|
||||
self.Helicopter = Helicopter -- Wrapper.Group#GROUP
|
||||
self.Helicopter:SetState( self.Helicopter, "AI_CARGO_HELICOPTER", self )
|
||||
|
||||
self.RoutePickup = false
|
||||
self.RouteDeploy = false
|
||||
|
||||
Helicopter:HandleEvent( EVENTS.Dead )
|
||||
Helicopter:HandleEvent( EVENTS.Hit )
|
||||
Helicopter:HandleEvent( EVENTS.Land )
|
||||
|
||||
function Helicopter:OnEventDead( EventData )
|
||||
local AICargoTroops = self:GetState( self, "AI_CARGO_HELICOPTER" )
|
||||
self:F({AICargoTroops=AICargoTroops})
|
||||
if AICargoTroops then
|
||||
self:F({})
|
||||
if not AICargoTroops:Is( "Loaded" ) then
|
||||
-- There are enemies within combat range. Unload the Helicopter.
|
||||
AICargoTroops:Destroyed()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function Helicopter:OnEventHit( EventData )
|
||||
local AICargoTroops = self:GetState( self, "AI_CARGO_HELICOPTER" )
|
||||
if AICargoTroops then
|
||||
self:F( { OnHitLoaded = AICargoTroops:Is( "Loaded" ) } )
|
||||
if AICargoTroops:Is( "Loaded" ) or AICargoTroops:Is( "Boarding" ) then
|
||||
-- There are enemies within combat range. Unload the Helicopter.
|
||||
AICargoTroops:Unload()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function Helicopter:OnEventLand( EventData )
|
||||
AICargo:Landed()
|
||||
end
|
||||
|
||||
self.Coalition = self.Helicopter:GetCoalition()
|
||||
|
||||
self:SetControllable( Helicopter )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param Wrapper.Group#GROUP Helicopter
|
||||
-- @param From
|
||||
-- @param Event
|
||||
-- @param To
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @param #number Speed
|
||||
function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To )
|
||||
|
||||
if Helicopter and Helicopter:IsAlive() then
|
||||
|
||||
if self.RoutePickup == true then
|
||||
self:Load( Helicopter:GetPointVec2() )
|
||||
self.RoutePickup = false
|
||||
self.Relocating = true
|
||||
end
|
||||
|
||||
if self.RouteDeploy == true then
|
||||
self:Unload( true )
|
||||
self.RouteDeploy = false
|
||||
self.Transporting = false
|
||||
self.Relocating = false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param Wrapper.Group#GROUP Helicopter
|
||||
-- @param From
|
||||
-- @param Event
|
||||
-- @param To
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @param #number Speed
|
||||
function AI_CARGO_HELICOPTER:onafterQueue( Helicopter, From, Event, To, Coordinate )
|
||||
|
||||
local HelicopterInZone = false
|
||||
|
||||
if Helicopter and Helicopter:IsAlive() then
|
||||
|
||||
local Distance = Coordinate:DistanceFromPointVec2( Helicopter:GetCoordinate() )
|
||||
|
||||
if Distance > 500 then
|
||||
self:__Queue( -10, Coordinate )
|
||||
else
|
||||
|
||||
local ZoneFree = true
|
||||
|
||||
for Helicopter, ZoneQueue in pairs( AI_CARGO_QUEUE ) do
|
||||
local ZoneQueue = ZoneQueue -- Core.Zone#ZONE_RADIUS
|
||||
if ZoneQueue:IsCoordinateInZone( Coordinate ) then
|
||||
ZoneFree = false
|
||||
end
|
||||
end
|
||||
|
||||
self:F({ZoneFree=ZoneFree})
|
||||
|
||||
if ZoneFree == true then
|
||||
|
||||
local ZoneQueue = ZONE_RADIUS:New( Helicopter:GetName(), Coordinate:GetVec2(), 100 )
|
||||
|
||||
AI_CARGO_QUEUE[Helicopter] = ZoneQueue
|
||||
|
||||
local Route = {}
|
||||
|
||||
-- local CoordinateFrom = Helicopter:GetCoordinate()
|
||||
-- local WaypointFrom = CoordinateFrom:WaypointAir(
|
||||
-- "RADIO",
|
||||
-- POINT_VEC3.RoutePointType.TurningPoint,
|
||||
-- POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
-- Speed,
|
||||
-- true
|
||||
-- )
|
||||
-- Route[#Route+1] = WaypointFrom
|
||||
local CoordinateTo = Coordinate
|
||||
local WaypointTo = CoordinateTo:WaypointAir(
|
||||
"RADIO",
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
50,
|
||||
true
|
||||
)
|
||||
Route[#Route+1] = WaypointTo
|
||||
|
||||
local Tasks = {}
|
||||
Tasks[#Tasks+1] = Helicopter:TaskLandAtVec2( CoordinateTo:GetVec2() )
|
||||
Route[#Route].task = Helicopter:TaskCombo( Tasks )
|
||||
|
||||
Route[#Route+1] = WaypointTo
|
||||
|
||||
-- Now route the helicopter
|
||||
Helicopter:Route( Route, 0 )
|
||||
else
|
||||
self:__Queue( -10, Coordinate )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param Wrapper.Group#GROUP Helicopter
|
||||
-- @param From
|
||||
-- @param Event
|
||||
-- @param To
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @param #number Speed
|
||||
function AI_CARGO_HELICOPTER:onafterOrbit( Helicopter, From, Event, To, Coordinate )
|
||||
|
||||
if Helicopter and Helicopter:IsAlive() then
|
||||
|
||||
if not self:IsTransporting() then
|
||||
local Route = {}
|
||||
|
||||
-- local CoordinateFrom = Helicopter:GetCoordinate()
|
||||
-- local WaypointFrom = CoordinateFrom:WaypointAir(
|
||||
-- "RADIO",
|
||||
-- POINT_VEC3.RoutePointType.TurningPoint,
|
||||
-- POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
-- Speed,
|
||||
-- true
|
||||
-- )
|
||||
-- Route[#Route+1] = WaypointFrom
|
||||
local CoordinateTo = Coordinate
|
||||
local WaypointTo = CoordinateTo:WaypointAir(
|
||||
"RADIO",
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
50,
|
||||
true
|
||||
)
|
||||
Route[#Route+1] = WaypointTo
|
||||
|
||||
local Tasks = {}
|
||||
Tasks[#Tasks+1] = Helicopter:TaskOrbitCircle( math.random( 30, 80 ), 0, CoordinateTo:GetRandomCoordinateInRadius( 800, 500 ) )
|
||||
Route[#Route].task = Helicopter:TaskCombo( Tasks )
|
||||
|
||||
Route[#Route+1] = WaypointTo
|
||||
|
||||
-- Now route the helicopter
|
||||
Helicopter:Route( Route, 0 )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param Wrapper.Group#GROUP Helicopter
|
||||
function AI_CARGO_HELICOPTER:onbeforeLoad( Helicopter, From, Event, To, Coordinate )
|
||||
|
||||
local Boarding = false
|
||||
|
||||
if Helicopter and Helicopter:IsAlive() then
|
||||
|
||||
self.BoardingCount = 0
|
||||
|
||||
if Helicopter and Helicopter:IsAlive() then
|
||||
self.Helicopter_Cargo = {}
|
||||
for _, HelicopterUnit in pairs( Helicopter:GetUnits() ) do
|
||||
local HelicopterUnit = HelicopterUnit -- Wrapper.Unit#UNIT
|
||||
for _, Cargo in pairs( self.CargoSet:GetSet() ) do
|
||||
local Cargo = Cargo -- Cargo.Cargo#CARGO
|
||||
self:F( { IsUnLoaded = Cargo:IsUnLoaded() } )
|
||||
if Cargo:IsUnLoaded() then
|
||||
if Cargo:IsInLoadRadius( HelicopterUnit:GetCoordinate() ) then
|
||||
self:F( { "In radius", HelicopterUnit:GetName() } )
|
||||
--Cargo:Ungroup()
|
||||
Cargo:Board( HelicopterUnit, 25 )
|
||||
self:__Board( 1, Cargo )
|
||||
Boarding = true
|
||||
|
||||
-- So now this APCUnit has Cargo that is being loaded.
|
||||
-- This will be used further in the logic to follow and to check cargo status.
|
||||
self.Helicopter_Cargo[HelicopterUnit] = Cargo
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return Boarding
|
||||
|
||||
end
|
||||
|
||||
--- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param Wrapper.Group#GROUP Helicopter
|
||||
function AI_CARGO_HELICOPTER:onafterBoard( Helicopter, From, Event, To, Cargo )
|
||||
self:F( { APC, From, Event, To, Cargo } )
|
||||
|
||||
if Helicopter and Helicopter:IsAlive() then
|
||||
self:F({ IsLoaded = Cargo:IsLoaded() } )
|
||||
if not Cargo:IsLoaded() then
|
||||
self:__Board( 10, Cargo )
|
||||
else
|
||||
self:__Loaded( 1, Cargo )
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param Wrapper.Group#GROUP Helicopter
|
||||
function AI_CARGO_HELICOPTER:onbeforeLoaded( Helicopter, From, Event, To, Cargo )
|
||||
self:F( { APC, From, Event, To } )
|
||||
|
||||
local Loaded = true
|
||||
|
||||
if Helicopter and Helicopter:IsAlive() then
|
||||
for HelicopterUnit, Cargo in pairs( self.Helicopter_Cargo ) do
|
||||
local Cargo = Cargo -- Cargo.Cargo#CARGO
|
||||
self:F( { IsLoaded = Cargo:IsLoaded(), IsDestroyed = Cargo:IsDestroyed() } )
|
||||
if not Cargo:IsLoaded() and not Cargo:IsDestroyed() then
|
||||
Loaded = false
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return Loaded
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param Wrapper.Group#GROUP Helicopter
|
||||
function AI_CARGO_HELICOPTER:onafterUnload( Helicopter, From, Event, To, Deployed )
|
||||
|
||||
if Helicopter and Helicopter:IsAlive() then
|
||||
for _, HelicopterUnit in pairs( Helicopter:GetUnits() ) do
|
||||
local HelicopterUnit = HelicopterUnit -- Wrapper.Unit#UNIT
|
||||
for _, Cargo in pairs( HelicopterUnit:GetCargo() ) do
|
||||
Cargo:UnBoard()
|
||||
Cargo:SetDeployed( true )
|
||||
self:__Unboard( 10, Cargo, Deployed )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param Wrapper.Group#GROUP Helicopter
|
||||
function AI_CARGO_HELICOPTER:onafterUnboard( Helicopter, From, Event, To, Cargo, Deployed )
|
||||
|
||||
if Helicopter and Helicopter:IsAlive() then
|
||||
if not Cargo:IsUnLoaded() then
|
||||
self:__Unboard( 10, Cargo, Deployed )
|
||||
else
|
||||
self:__Unloaded( 1, Cargo, Deployed )
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param Wrapper.Group#GROUP Helicopter
|
||||
function AI_CARGO_HELICOPTER:onbeforeUnloaded( Helicopter, From, Event, To, Cargo, Deployed )
|
||||
self:F( { APC, From, Event, To, Cargo:GetName(), Deployed = Deployed } )
|
||||
|
||||
local AllUnloaded = true
|
||||
|
||||
--Cargo:Regroup()
|
||||
|
||||
if Helicopter and Helicopter:IsAlive() then
|
||||
for _, HelicopterUnit in pairs( Helicopter:GetUnits() ) do
|
||||
local CargoCheck = self.Helicopter_Cargo[HelicopterUnit] -- Cargo.Cargo#CARGO
|
||||
if CargoCheck then
|
||||
self:F( { CargoCheck:GetName(), IsUnLoaded = CargoCheck:IsUnLoaded() } )
|
||||
if CargoCheck:IsUnLoaded() == false then
|
||||
AllUnloaded = false
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if AllUnloaded == true then
|
||||
if Deployed == true then
|
||||
for HelicopterUnit, Cargo in pairs( self.Helicopter_Cargo ) do
|
||||
local Cargo = Cargo -- Cargo.Cargo#CARGO
|
||||
end
|
||||
self.Helicopter_Cargo = {}
|
||||
end
|
||||
self.Helicopter = Helicopter
|
||||
end
|
||||
end
|
||||
|
||||
self:F( { AllUnloaded = AllUnloaded } )
|
||||
return AllUnloaded
|
||||
|
||||
end
|
||||
|
||||
--- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param Wrapper.Group#GROUP Helicopter
|
||||
function AI_CARGO_HELICOPTER:onafterUnloaded( Helicopter, From, Event, To, Cargo, Deployed )
|
||||
|
||||
self:Orbit( Helicopter:GetCoordinate(), 50 )
|
||||
|
||||
AI_CARGO_QUEUE[Helicopter] = nil
|
||||
|
||||
end
|
||||
|
||||
--- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param Wrapper.Group#GROUP Helicopter
|
||||
-- @param From
|
||||
-- @param Event
|
||||
-- @param To
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @param #number Speed
|
||||
function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordinate )
|
||||
|
||||
if Helicopter and Helicopter:IsAlive() ~= nil then
|
||||
|
||||
Helicopter:Activate()
|
||||
|
||||
self.RoutePickup = true
|
||||
Coordinate.y = math.random( 50, 200 )
|
||||
|
||||
local Route = {}
|
||||
|
||||
--- Calculate the target route point.
|
||||
local CoordinateFrom = Helicopter:GetCoordinate()
|
||||
local CoordinateTo = Coordinate
|
||||
|
||||
--- Create a route point of type air.
|
||||
local WaypointFrom = CoordinateFrom:WaypointAir(
|
||||
"RADIO",
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
150,
|
||||
true
|
||||
)
|
||||
|
||||
--- Create a route point of type air.
|
||||
local WaypointTo = CoordinateTo:WaypointAir(
|
||||
"RADIO",
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
150,
|
||||
true
|
||||
)
|
||||
|
||||
Route[#Route+1] = WaypointFrom
|
||||
Route[#Route+1] = WaypointTo
|
||||
|
||||
--- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable...
|
||||
Helicopter:WayPointInitialize( Route )
|
||||
|
||||
local Tasks = {}
|
||||
|
||||
Tasks[#Tasks+1] = Helicopter:TaskLandAtVec2( CoordinateTo:GetVec2() )
|
||||
Route[#Route].task = Helicopter:TaskCombo( Tasks )
|
||||
|
||||
Route[#Route+1] = WaypointTo
|
||||
|
||||
-- Now route the helicopter
|
||||
Helicopter:Route( Route, 1 )
|
||||
|
||||
self.Transporting = true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
function AI_CARGO_HELICOPTER:_Deploy( AICargoHelicopter, Coordinate )
|
||||
AICargoHelicopter:__Queue( -10, Coordinate, 100 )
|
||||
end
|
||||
|
||||
--- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param Wrapper.Group#GROUP Helicopter
|
||||
-- @param From
|
||||
-- @param Event
|
||||
-- @param To
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @param #number Speed
|
||||
function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordinate )
|
||||
|
||||
if Helicopter and Helicopter:IsAlive() ~= nil then
|
||||
|
||||
self.RouteDeploy = true
|
||||
|
||||
|
||||
local Route = {}
|
||||
|
||||
--- Calculate the target route point.
|
||||
|
||||
Coordinate.y = math.random( 50, 200 )
|
||||
|
||||
--- Create a route point of type air.
|
||||
local CoordinateFrom = Helicopter:GetCoordinate()
|
||||
local WaypointFrom = CoordinateFrom:WaypointAir(
|
||||
"RADIO",
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
150,
|
||||
true
|
||||
)
|
||||
Route[#Route+1] = WaypointFrom
|
||||
Route[#Route+1] = WaypointFrom
|
||||
|
||||
--- Create a route point of type air.
|
||||
local CoordinateTo = Coordinate
|
||||
local WaypointTo = CoordinateTo:WaypointAir(
|
||||
"RADIO",
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
150,
|
||||
true
|
||||
)
|
||||
|
||||
Route[#Route+1] = WaypointTo
|
||||
Route[#Route+1] = WaypointTo
|
||||
|
||||
--- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable...
|
||||
Helicopter:WayPointInitialize( Route )
|
||||
|
||||
local Tasks = {}
|
||||
|
||||
Tasks[#Tasks+1] = Helicopter:TaskFunction( "AI_CARGO_HELICOPTER._Deploy", self, Coordinate )
|
||||
Tasks[#Tasks+1] = Helicopter:TaskOrbitCircle( math.random( 30, 100 ), 0, CoordinateTo:GetRandomCoordinateInRadius( 800, 500 ) )
|
||||
|
||||
--Tasks[#Tasks+1] = Helicopter:TaskLandAtVec2( CoordinateTo:GetVec2() )
|
||||
Route[#Route].task = Helicopter:TaskCombo( Tasks )
|
||||
|
||||
Route[#Route+1] = WaypointTo
|
||||
|
||||
-- Now route the helicopter
|
||||
Helicopter:Route( Route, 1 )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param Wrapper.Group#GROUP Helicopter
|
||||
-- @param From
|
||||
-- @param Event
|
||||
-- @param To
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @param #number Speed
|
||||
function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinate )
|
||||
|
||||
if Helicopter and Helicopter:IsAlive() ~= nil then
|
||||
|
||||
self.RouteHome = true
|
||||
|
||||
local Route = {}
|
||||
|
||||
--- Calculate the target route point.
|
||||
|
||||
Coordinate.y = math.random( 50, 200 )
|
||||
|
||||
--- Create a route point of type air.
|
||||
local CoordinateFrom = Helicopter:GetCoordinate()
|
||||
local WaypointFrom = CoordinateFrom:WaypointAir(
|
||||
"RADIO",
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
150,
|
||||
true
|
||||
)
|
||||
Route[#Route+1] = WaypointFrom
|
||||
|
||||
--- Create a route point of type air.
|
||||
local CoordinateTo = Coordinate
|
||||
local WaypointTo = CoordinateTo:WaypointAir(
|
||||
"RADIO",
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
150,
|
||||
true
|
||||
)
|
||||
|
||||
Route[#Route+1] = WaypointTo
|
||||
|
||||
--- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable...
|
||||
Helicopter:WayPointInitialize( Route )
|
||||
|
||||
local Tasks = {}
|
||||
|
||||
Tasks[#Tasks+1] = Helicopter:TaskLandAtVec2( CoordinateTo:GetVec2() )
|
||||
Route[#Route].task = Helicopter:TaskCombo( Tasks )
|
||||
|
||||
Route[#Route+1] = WaypointTo
|
||||
|
||||
-- Now route the helicopter
|
||||
Helicopter:Route( Route, 0 )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
--- **AI** -- (R2.1) Build large **formations** of AI @{Group}s flying together.
|
||||
--- **AI** -- (R2.2) - Build large airborne formations of aircraft.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- 
|
||||
--
|
||||
@@ -28,31 +30,20 @@
|
||||
--
|
||||
-- * @{#AI_FORMATION}: Create a formation from several @{GROUP}s.
|
||||
--
|
||||
-- ====
|
||||
-- ===
|
||||
--
|
||||
-- # Demo Missions
|
||||
--
|
||||
-- ### [AI_FORMATION Demo Missions source code]()
|
||||
--
|
||||
-- ### [AI_FORMATION Demo Missions, only for beta testers]()
|
||||
--
|
||||
-- ### [ALL Demo Missions pack of the last release]()
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- # YouTube Channel
|
||||
--
|
||||
--- ### [AI_FORMATION YouTube Channel]()
|
||||
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/FOR%20-%20Formation)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # **AUTHORS and CONTRIBUTIONS**
|
||||
--
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ### Authors:
|
||||
--
|
||||
-- * **FlightControl**: Concept, Design & Programming.
|
||||
-- ### [YouTube Playlist](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl0bFIJ9jIdYM22uaWmIN4oz)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI_Formation
|
||||
|
||||
@@ -68,7 +59,6 @@
|
||||
-- @field #boolean ReportTargets If true, nearby targets are reported.
|
||||
-- @Field DCSTypes#AI.Option.Air.val.ROE OptionROE Which ROE is set to the FollowGroup.
|
||||
-- @field DCSTypes#AI.Option.Air.val.REACTION_ON_THREAT OptionReactionOnThreat Which REACTION_ON_THREAT is set to the FollowGroup.
|
||||
-- @field Menu#MENU_CLIENT FollowMenuResumeMission
|
||||
|
||||
|
||||
--- # AI_FORMATION class, extends @{Fsm#FSM_SET}
|
||||
@@ -681,7 +671,7 @@ end
|
||||
function AI_FORMATION:onafterFormationLine( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace ) --R2.1
|
||||
self:F( { FollowGroupSet, From , Event ,To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace } )
|
||||
|
||||
FollowGroupSet:Flush()
|
||||
FollowGroupSet:Flush( self )
|
||||
|
||||
local FollowSet = FollowGroupSet:GetSet()
|
||||
|
||||
@@ -960,7 +950,7 @@ function AI_FORMATION:onenterFollowing( FollowGroupSet ) --R2.1
|
||||
-- @param Wrapper.Unit#UNIT ClientUnit
|
||||
function( FollowGroup, Formation, ClientUnit, CT1, CV1, CT2, CV2 )
|
||||
|
||||
FollowGroup:OptionROTPassiveDefense()
|
||||
FollowGroup:OptionROTEvadeFire()
|
||||
FollowGroup:OptionROEReturnFire()
|
||||
|
||||
local GroupUnit = FollowGroup:GetUnit( 1 )
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
--- **AI** -- **Air Patrolling or Staging.**
|
||||
--- **AI** -- (R2.1) - Manages the independent process of Air Patrol for airplanes.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- 
|
||||
--
|
||||
@@ -10,65 +12,27 @@
|
||||
--
|
||||
-- * @{#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)
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- # **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.
|
||||
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/PAT%20-%20Patrolling)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # **AUTHORS and CONTRIBUTIONS**
|
||||
-- ### [YouTube Playlist](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl35HvYZKA6G22WMt7iI3zky)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **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.
|
||||
--
|
||||
-- ### Authors:
|
||||
--
|
||||
-- * **FlightControl**: Design & Programming.
|
||||
-- ===
|
||||
--
|
||||
-- @module AI_Patrol
|
||||
|
||||
|
||||
--- AI_PATROL_ZONE class
|
||||
-- @type AI_PATROL_ZONE
|
||||
-- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling.
|
||||
@@ -77,7 +41,7 @@
|
||||
-- @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
|
||||
-- @field Core.Spawn#SPAWN CoordTest
|
||||
-- @extends Core.Fsm#FSM_CONTROLLABLE
|
||||
|
||||
--- # AI_PATROL_ZONE class, extends @{Fsm#FSM_CONTROLLABLE}
|
||||
@@ -158,7 +122,7 @@
|
||||
-- * @{#AI_PATROL_ZONE.SetDetectionOn}(): Set the detection on. The AI will detect for targets.
|
||||
-- * @{#AI_PATROL_ZONE.SetDetectionOff}(): Set the detection off, the AI will not detect for targets. The existing target list will NOT be erased.
|
||||
--
|
||||
-- The detection frequency can be set with @{#AI_PATROL_ZONE.SetDetectionInterval}( seconds ), where the amount of seconds specify how much seconds will be waited before the next detection.
|
||||
-- The detection frequency can be set with @{#AI_PATROL_ZONE.SetRefreshTimeInterval}( seconds ), where the amount of seconds specify how much seconds will be waited before the next detection.
|
||||
-- Use the method @{#AI_PATROL_ZONE.GetDetectedUnits}() to obtain a list of the @{Unit}s detected by the AI.
|
||||
--
|
||||
-- The detection can be filtered to potential targets in a specific zone.
|
||||
@@ -217,7 +181,7 @@ function AI_PATROL_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltit
|
||||
-- defafult PatrolAltType to "RADIO" if not specified
|
||||
self.PatrolAltType = PatrolAltType or "RADIO"
|
||||
|
||||
self:SetDetectionInterval( 30 )
|
||||
self:SetRefreshTimeInterval( 30 )
|
||||
|
||||
self.CheckStatus = true
|
||||
|
||||
@@ -574,7 +538,7 @@ end
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param #number Seconds The interval in seconds.
|
||||
-- @return #AI_PATROL_ZONE self
|
||||
function AI_PATROL_ZONE:SetDetectionInterval( Seconds )
|
||||
function AI_PATROL_ZONE:SetRefreshTimeInterval( Seconds )
|
||||
self:F2()
|
||||
|
||||
if Seconds then
|
||||
@@ -621,13 +585,13 @@ end
|
||||
-- When the fuel treshold is reached, the AI will continue for a given time its patrol task in orbit, while a new AIControllable is targetted to the AI_PATROL_ZONE.
|
||||
-- Once the time is finished, the old AI will return to the base.
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param #number PatrolFuelTresholdPercentage The treshold in percentage (between 0 and 1) when the AIControllable is considered to get out of fuel.
|
||||
-- @param #number PatrolFuelThresholdPercentage 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_PATROL_ZONE self
|
||||
function AI_PATROL_ZONE:ManageFuel( PatrolFuelTresholdPercentage, PatrolOutOfFuelOrbitTime )
|
||||
function AI_PATROL_ZONE:ManageFuel( PatrolFuelThresholdPercentage, PatrolOutOfFuelOrbitTime )
|
||||
|
||||
self.PatrolManageFuel = true
|
||||
self.PatrolFuelTresholdPercentage = PatrolFuelTresholdPercentage
|
||||
self.PatrolFuelThresholdPercentage = PatrolFuelThresholdPercentage
|
||||
self.PatrolOutOfFuelOrbitTime = PatrolOutOfFuelOrbitTime
|
||||
|
||||
return self
|
||||
@@ -640,12 +604,12 @@ end
|
||||
-- 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_PATROL_ZONE self
|
||||
-- @param #number PatrolDamageTreshold The treshold in percentage (between 0 and 1) when the AI is considered to be damaged.
|
||||
-- @param #number PatrolDamageThreshold The treshold in percentage (between 0 and 1) when the AI is considered to be damaged.
|
||||
-- @return #AI_PATROL_ZONE self
|
||||
function AI_PATROL_ZONE:ManageDamage( PatrolDamageTreshold )
|
||||
function AI_PATROL_ZONE:ManageDamage( PatrolDamageThreshold )
|
||||
|
||||
self.PatrolManageDamage = true
|
||||
self.PatrolDamageTreshold = PatrolDamageTreshold
|
||||
self.PatrolDamageThreshold = PatrolDamageThreshold
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -778,7 +742,7 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
|
||||
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
|
||||
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||
local ToPatrolZoneSpeed = self.PatrolMaxSpeed
|
||||
local CurrentRoutePoint = CurrentPointVec3:RoutePointAir(
|
||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TakeOffParking,
|
||||
POINT_VEC3.RoutePointAction.FromParkingArea,
|
||||
@@ -793,7 +757,7 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
|
||||
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
|
||||
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||
local ToPatrolZoneSpeed = self.PatrolMaxSpeed
|
||||
local CurrentRoutePoint = CurrentPointVec3:RoutePointAir(
|
||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
@@ -819,7 +783,7 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
|
||||
local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, ToTargetAltitude, ToTargetVec2.y )
|
||||
|
||||
--- Create a route point of type air.
|
||||
local ToTargetRoutePoint = ToTargetPointVec3:RoutePointAir(
|
||||
local ToTargetRoutePoint = ToTargetPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
@@ -861,10 +825,9 @@ function AI_PATROL_ZONE:onafterStatus()
|
||||
local RTB = false
|
||||
|
||||
local Fuel = self.Controllable:GetUnit(1):GetFuel()
|
||||
if Fuel < self.PatrolFuelTresholdPercentage then
|
||||
if Fuel < self.PatrolFuelThresholdPercentage 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 ) )
|
||||
@@ -876,7 +839,7 @@ function AI_PATROL_ZONE:onafterStatus()
|
||||
|
||||
-- TODO: Check GROUP damage function.
|
||||
local Damage = self.Controllable:GetLife()
|
||||
if Damage <= self.PatrolDamageTreshold then
|
||||
if Damage <= self.PatrolDamageThreshold then
|
||||
self:E( self.Controllable:GetName() .. " is damaged:" .. Damage .. ", RTB!" )
|
||||
RTB = true
|
||||
end
|
||||
@@ -907,7 +870,7 @@ function AI_PATROL_ZONE:onafterRTB()
|
||||
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
|
||||
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||
local ToPatrolZoneSpeed = self.PatrolMaxSpeed
|
||||
local CurrentRoutePoint = CurrentPointVec3:RoutePointAir(
|
||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
|
||||
@@ -70,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
|
||||
|
||||
@@ -90,13 +91,15 @@ do -- ACT_ACCOUNT
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ACCOUNT self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
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
|
||||
@@ -104,7 +107,7 @@ do -- ACT_ACCOUNT
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ACCOUNT self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
@@ -122,7 +125,7 @@ do -- ACT_ACCOUNT
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ACCOUNT self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
@@ -152,7 +155,6 @@ do -- ACT_ACCOUNT_DEADS
|
||||
-- @extends #ACT_ACCOUNT
|
||||
ACT_ACCOUNT_DEADS = {
|
||||
ClassName = "ACT_ACCOUNT_DEADS",
|
||||
TargetSetUnit = nil,
|
||||
}
|
||||
|
||||
|
||||
@@ -160,13 +162,10 @@ do -- ACT_ACCOUNT_DEADS
|
||||
-- @param #ACT_ACCOUNT_DEADS self
|
||||
-- @param Set#SET_UNIT TargetSetUnit
|
||||
-- @param #string TaskName
|
||||
function ACT_ACCOUNT_DEADS:New( TargetSetUnit, TaskName )
|
||||
function ACT_ACCOUNT_DEADS:New()
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, ACT_ACCOUNT:New() ) -- #ACT_ACCOUNT_DEADS
|
||||
|
||||
self.TargetSetUnit = TargetSetUnit
|
||||
self.TaskName = TaskName
|
||||
|
||||
self.DisplayInterval = 30
|
||||
self.DisplayCount = 30
|
||||
self.DisplayMessage = true
|
||||
@@ -178,65 +177,113 @@ do -- ACT_ACCOUNT_DEADS
|
||||
|
||||
function ACT_ACCOUNT_DEADS:Init( FsmAccount )
|
||||
|
||||
self.TargetSetUnit = FsmAccount.TargetSetUnit
|
||||
self.TaskName = FsmAccount.TaskName
|
||||
self.Task = self:GetTask()
|
||||
self.TaskName = self.Task:GetName()
|
||||
end
|
||||
|
||||
--- Process Events
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ACCOUNT_DEADS self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string 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." )
|
||||
local MessageText = "Your group with assigned " .. self.TaskName .. " task has " .. Task.TargetSetUnit:GetUnitTypesText() .. " targets left to be destroyed."
|
||||
self:GetCommandCenter():MessageTypeToGroup( MessageText, ProcessUnit:GetGroup(), MESSAGE.Type.Information )
|
||||
end
|
||||
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ACCOUNT_DEADS self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param Tasking.Task#TASK Task
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
function ACT_ACCOUNT_DEADS:onenterAccount( ProcessUnit, Task, From, Event, To, EventData )
|
||||
self:T( { ProcessUnit, EventData, From, Event, To } )
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function ACT_ACCOUNT_DEADS:onafterEvent( ProcessUnit, Task, From, Event, To, EventData )
|
||||
self:T( { ProcessUnit:GetName(), Task:GetName(), From, Event, To, EventData } )
|
||||
|
||||
self:T({self.Controllable})
|
||||
|
||||
self.TargetSetUnit:Flush()
|
||||
|
||||
self:T( { "Before sending Message", EventData.IniUnitName, self.TargetSetUnit:FindUnit( EventData.IniUnitName ) } )
|
||||
if self.TargetSetUnit:FindUnit( EventData.IniUnitName ) then
|
||||
self:T( "Sending Message" )
|
||||
local TaskGroup = ProcessUnit:GetGroup()
|
||||
self.TargetSetUnit:Remove( EventData.IniUnitName )
|
||||
self:Message( "You hit a target. Your group with assigned " .. self.TaskName .. " task has " .. self.TargetSetUnit:Count() .. " targets ( " .. self.TargetSetUnit:GetUnitTypesText() .. " ) left to be destroyed." )
|
||||
if Task.TargetSetUnit:FindUnit( EventData.IniUnitName ) then
|
||||
local PlayerName = ProcessUnit:GetPlayerName()
|
||||
local PlayerHit = self.PlayerHits and self.PlayerHits[EventData.IniUnitName]
|
||||
if PlayerHit == PlayerName then
|
||||
self:Player( EventData )
|
||||
else
|
||||
self:Other( EventData )
|
||||
end
|
||||
end
|
||||
self:T( { "After sending Message" } )
|
||||
end
|
||||
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ACCOUNT_DEADS self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param Tasking.Task#TASK Task
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
function ACT_ACCOUNT_DEADS:onafterEvent( ProcessUnit, Task, From, Event, To )
|
||||
|
||||
if self.TargetSetUnit:Count() > 0 then
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function ACT_ACCOUNT_DEADS:onenterAccountForPlayer( ProcessUnit, Task, From, Event, To, EventData )
|
||||
self:T( { ProcessUnit:GetName(), Task:GetName(), From, Event, To, EventData } )
|
||||
|
||||
local TaskGroup = ProcessUnit:GetGroup()
|
||||
|
||||
Task.TargetSetUnit:Remove( EventData.IniUnitName )
|
||||
|
||||
local MessageText = "You have destroyed a target.\nYour group assigned with task " .. self.TaskName .. " has\n" .. Task.TargetSetUnit:Count() .. " targets ( " .. Task.TargetSetUnit:GetUnitTypesText() .. " ) left to be destroyed."
|
||||
self:GetCommandCenter():MessageTypeToGroup( MessageText, ProcessUnit:GetGroup(), MESSAGE.Type.Information )
|
||||
|
||||
local PlayerName = ProcessUnit:GetPlayerName()
|
||||
Task:AddProgress( PlayerName, "Destroyed " .. EventData.IniTypeName, timer.getTime(), 1 )
|
||||
|
||||
if Task.TargetSetUnit:Count() > 0 then
|
||||
self:__More( 1 )
|
||||
else
|
||||
self:__NoMore( 1 )
|
||||
end
|
||||
end
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ACCOUNT_DEADS self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param Tasking.Task#TASK Task
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function ACT_ACCOUNT_DEADS:onenterAccountForOther( ProcessUnit, Task, From, Event, To, EventData )
|
||||
self:T( { ProcessUnit:GetName(), Task:GetName(), From, Event, To, EventData } )
|
||||
|
||||
local TaskGroup = ProcessUnit:GetGroup()
|
||||
Task.TargetSetUnit:Remove( EventData.IniUnitName )
|
||||
|
||||
local MessageText = "One of the task targets has been destroyed.\nYour group assigned with task " .. self.TaskName .. " has\n" .. Task.TargetSetUnit:Count() .. " targets ( " .. Task.TargetSetUnit:GetUnitTypesText() .. " ) left to be destroyed."
|
||||
self:GetCommandCenter():MessageTypeToGroup( MessageText, ProcessUnit:GetGroup(), MESSAGE.Type.Information )
|
||||
|
||||
if Task.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 )
|
||||
@@ -247,4 +294,16 @@ do -- ACT_ACCOUNT_DEADS
|
||||
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
|
||||
|
||||
end -- ACT_ACCOUNT DEADS
|
||||
|
||||
@@ -155,8 +155,7 @@ do -- ACT_ASSIGN_ACCEPT
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ASSIGN_ACCEPT:onafterStart( ProcessUnit, From, Event, To )
|
||||
self:E( { ProcessUnit, From, Event, To } )
|
||||
function ACT_ASSIGN_ACCEPT:onafterStart( ProcessUnit, Task, From, Event, To )
|
||||
|
||||
self:__Assign( 1 )
|
||||
end
|
||||
@@ -167,12 +166,8 @@ do -- ACT_ASSIGN_ACCEPT
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ASSIGN_ACCEPT:onenterAssigned( ProcessUnit, From, Event, To )
|
||||
env.info( "in here" )
|
||||
self:E( { ProcessUnit, From, Event, To } )
|
||||
function ACT_ASSIGN_ACCEPT:onenterAssigned( ProcessUnit, Task, From, Event, To )
|
||||
|
||||
local ProcessGroup = ProcessUnit:GetGroup()
|
||||
|
||||
self.Task:Assign( ProcessUnit, ProcessUnit:GetPlayerName() )
|
||||
end
|
||||
|
||||
@@ -193,99 +188,99 @@ do -- ACT_ASSIGN_MENU_ACCEPT
|
||||
|
||||
--- Init.
|
||||
-- @param #ACT_ASSIGN_MENU_ACCEPT self
|
||||
-- @param #string TaskName
|
||||
-- @param #string TaskBriefing
|
||||
-- @return #ACT_ASSIGN_MENU_ACCEPT self
|
||||
function ACT_ASSIGN_MENU_ACCEPT:New( TaskName, TaskBriefing )
|
||||
function ACT_ASSIGN_MENU_ACCEPT:New( TaskBriefing )
|
||||
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, ACT_ASSIGN:New() ) -- #ACT_ASSIGN_MENU_ACCEPT
|
||||
|
||||
self.TaskName = TaskName
|
||||
self.TaskBriefing = TaskBriefing
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function ACT_ASSIGN_MENU_ACCEPT:Init( FsmAssign )
|
||||
|
||||
self.TaskName = FsmAssign.TaskName
|
||||
self.TaskBriefing = FsmAssign.TaskBriefing
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Creates a new task assignment state machine. The process will request from the menu if it accepts the task, if not, the unit is removed from the simulator.
|
||||
-- @param #ACT_ASSIGN_MENU_ACCEPT self
|
||||
-- @param #string TaskName
|
||||
-- @param #string TaskBriefing
|
||||
-- @return #ACT_ASSIGN_MENU_ACCEPT self
|
||||
function ACT_ASSIGN_MENU_ACCEPT:Init( TaskName, TaskBriefing )
|
||||
function ACT_ASSIGN_MENU_ACCEPT:Init( TaskBriefing )
|
||||
|
||||
self.TaskBriefing = TaskBriefing
|
||||
self.TaskName = TaskName
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ASSIGN_MENU_ACCEPT self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ASSIGN_MENU_ACCEPT:onafterStart( ProcessUnit, From, Event, To )
|
||||
self:E( { ProcessUnit, From, Event, To } )
|
||||
function ACT_ASSIGN_MENU_ACCEPT:onafterStart( ProcessUnit, Task, From, Event, To )
|
||||
|
||||
self:Message( "Access the radio menu to accept the task. You have 30 seconds or the assignment will be cancelled." )
|
||||
self:GetCommandCenter():MessageToGroup( "Task " .. self.Task:GetName() .. " has been assigned to you and your group!\nRead the briefing and use the Radio Menu (F10) / Task ... CONFIRMATION menu to accept or reject the task.\nYou have 2 minutes to accept, or the task assignment will be cancelled!", ProcessUnit:GetGroup(), 120 )
|
||||
|
||||
local ProcessGroup = ProcessUnit:GetGroup()
|
||||
local TaskGroup = ProcessUnit:GetGroup()
|
||||
|
||||
self.Menu = MENU_GROUP:New( TaskGroup, "Task " .. self.Task:GetName() .. " CONFIRMATION" )
|
||||
self.MenuAcceptTask = MENU_GROUP_COMMAND:New( TaskGroup, "Accept task " .. self.Task:GetName(), self.Menu, self.MenuAssign, self, TaskGroup )
|
||||
self.MenuRejectTask = MENU_GROUP_COMMAND:New( TaskGroup, "Reject task " .. self.Task:GetName(), self.Menu, self.MenuReject, self, TaskGroup )
|
||||
|
||||
self.Menu = MENU_GROUP:New( ProcessGroup, "Task " .. self.TaskName .. " acceptance" )
|
||||
self.MenuAcceptTask = MENU_GROUP_COMMAND:New( ProcessGroup, "Accept task " .. self.TaskName, self.Menu, self.MenuAssign, self )
|
||||
self.MenuRejectTask = MENU_GROUP_COMMAND:New( ProcessGroup, "Reject task " .. self.TaskName, self.Menu, self.MenuReject, self )
|
||||
self:__Reject( 120, TaskGroup )
|
||||
end
|
||||
|
||||
--- Menu function.
|
||||
-- @param #ACT_ASSIGN_MENU_ACCEPT self
|
||||
function ACT_ASSIGN_MENU_ACCEPT:MenuAssign()
|
||||
self:E( )
|
||||
function ACT_ASSIGN_MENU_ACCEPT:MenuAssign( TaskGroup )
|
||||
|
||||
self:__Assign( 1 )
|
||||
self:__Assign( -1, TaskGroup )
|
||||
end
|
||||
|
||||
--- Menu function.
|
||||
-- @param #ACT_ASSIGN_MENU_ACCEPT self
|
||||
function ACT_ASSIGN_MENU_ACCEPT:MenuReject()
|
||||
self:E( )
|
||||
function ACT_ASSIGN_MENU_ACCEPT:MenuReject( TaskGroup )
|
||||
|
||||
self:__Reject( 1 )
|
||||
self:__Reject( -1, TaskGroup )
|
||||
end
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ASSIGN_MENU_ACCEPT self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ASSIGN_MENU_ACCEPT:onafterAssign( ProcessUnit, From, Event, To )
|
||||
self:E( { ProcessUnit.UnitNameFrom, Event, To } )
|
||||
function ACT_ASSIGN_MENU_ACCEPT:onafterAssign( ProcessUnit, Task, From, Event, To, TaskGroup )
|
||||
|
||||
self.Menu:Remove()
|
||||
end
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ASSIGN_MENU_ACCEPT self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ASSIGN_MENU_ACCEPT:onafterReject( ProcessUnit, From, Event, To )
|
||||
self:E( { ProcessUnit.UnitName, From, Event, To } )
|
||||
function ACT_ASSIGN_MENU_ACCEPT:onafterReject( ProcessUnit, Task, From, Event, To, TaskGroup )
|
||||
self:F( { TaskGroup = TaskGroup } )
|
||||
|
||||
self.Menu:Remove()
|
||||
--TODO: need to resolve this problem ... it has to do with the events ...
|
||||
--self.Task:UnAssignFromUnit( ProcessUnit )needs to become a callback funtion call upon the event
|
||||
ProcessUnit:Destroy()
|
||||
self.Task:RejectGroup( TaskGroup )
|
||||
end
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ASSIGN_ACCEPT self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ASSIGN_MENU_ACCEPT:onenterAssigned( ProcessUnit, Task, From, Event, To, TaskGroup )
|
||||
|
||||
--self.Task:AssignToGroup( TaskGroup )
|
||||
self.Task:Assign( ProcessUnit, ProcessUnit:GetPlayerName() )
|
||||
end
|
||||
|
||||
end -- ACT_ASSIGN_MENU_ACCEPT
|
||||
|
||||
@@ -111,7 +111,6 @@ do -- ACT_ASSIST
|
||||
local MissionMenu = self:GetMission():GetMenu( ProcessGroup )
|
||||
|
||||
local function MenuSmoke( MenuParam )
|
||||
self:E( MenuParam )
|
||||
local self = MenuParam.self
|
||||
local SmokeColor = MenuParam.SmokeColor
|
||||
self.SmokeColor = SmokeColor
|
||||
|
||||
@@ -83,7 +83,7 @@ end
|
||||
function PROCESS_JTAC:OnJTACMenuUpdate( Fsm, From, Event, To )
|
||||
|
||||
local function JTACMenuSpot( MenuParam )
|
||||
self:E( MenuParam.TargetUnit.UnitName )
|
||||
self:F( MenuParam.TargetUnit.UnitName )
|
||||
local self = MenuParam.self
|
||||
local TargetUnit = MenuParam.TargetUnit
|
||||
|
||||
@@ -91,7 +91,7 @@ function PROCESS_JTAC:OnJTACMenuUpdate( Fsm, From, Event, To )
|
||||
end
|
||||
|
||||
local function JTACMenuCancel( MenuParam )
|
||||
self:E( MenuParam )
|
||||
self:F( MenuParam )
|
||||
local self = MenuParam.self
|
||||
local TargetUnit = MenuParam.TargetUnit
|
||||
|
||||
@@ -102,7 +102,7 @@ function PROCESS_JTAC:OnJTACMenuUpdate( Fsm, From, Event, To )
|
||||
-- Loop each unit in the target set, and determine the threat levels map table.
|
||||
local UnitThreatLevels = self.TargetSetUnit:GetUnitThreatLevels()
|
||||
|
||||
self:E( {"UnitThreadLevels", UnitThreatLevels } )
|
||||
self:F( {"UnitThreadLevels", UnitThreatLevels } )
|
||||
|
||||
local JTACMenu = self.ProcessGroup:GetState( self.ProcessGroup, "JTACMenu" )
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ end
|
||||
function PROCESS_PICKUP:OnHitTarget( Fsm, From, Event, To, Event )
|
||||
|
||||
|
||||
self.TargetSetUnit:Flush()
|
||||
self.TargetSetUnit:Flush( self )
|
||||
|
||||
if self.TargetSetUnit:FindUnit( Event.IniUnitName ) then
|
||||
self.TargetSetUnit:RemoveUnitsByName( Event.IniUnitName )
|
||||
|
||||
@@ -123,16 +123,20 @@ do -- ACT_ROUTE
|
||||
--- Set a Cancel Menu item.
|
||||
-- @param #ACT_ROUTE self
|
||||
-- @return #ACT_ROUTE
|
||||
function ACT_ROUTE:SetMenuCancel( MenuGroup, MenuText, ParentMenu, MenuTime )
|
||||
function ACT_ROUTE:SetMenuCancel( MenuGroup, MenuText, ParentMenu, MenuTime, MenuTag )
|
||||
|
||||
MENU_GROUP_COMMAND:New(
|
||||
self.CancelMenuGroupCommand = MENU_GROUP_COMMAND:New(
|
||||
MenuGroup,
|
||||
MenuText,
|
||||
ParentMenu,
|
||||
self.MenuCancel,
|
||||
self
|
||||
):SetTime(MenuTime)
|
||||
):SetTime( MenuTime ):SetTag( MenuTag )
|
||||
|
||||
ParentMenu:SetTime( MenuTime )
|
||||
|
||||
ParentMenu:Remove( MenuTime, MenuTag )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -154,42 +158,68 @@ do -- ACT_ROUTE
|
||||
--- Get the routing text to be displayed.
|
||||
-- The route mode determines the text displayed.
|
||||
-- @param #ACT_ROUTE self
|
||||
-- @param Wrapper.Unit#UNIT Controllable
|
||||
-- @return #string
|
||||
function ACT_ROUTE:GetRouteText( FromCoordinate )
|
||||
|
||||
function ACT_ROUTE:GetRouteText( Controllable )
|
||||
|
||||
local RouteText = ""
|
||||
|
||||
if self.Coordinate and self.RouteMode == "B" then
|
||||
RouteText = "Route to " .. FromCoordinate:GetBRText( self.Coordinate ) .. " km."
|
||||
local Coordinate = nil -- Core.Point#COORDINATE
|
||||
|
||||
if self.Coordinate then
|
||||
Coordinate = self.Coordinate
|
||||
end
|
||||
|
||||
if self.Coordinate and self.RouteMode == "C" then
|
||||
RouteText = "Route to " .. self.Coordinate:ToString()
|
||||
if self.Zone then
|
||||
Coordinate = self.Zone:GetPointVec3( self.Altitude )
|
||||
Coordinate:SetHeading( self.Heading )
|
||||
end
|
||||
|
||||
if self.Zone and self.RouteMode == "B" then
|
||||
local Coordinate = self.Zone:GetCoordinate()
|
||||
RouteText = "Route to zone bearing " .. FromCoordinate:GetBRText( Coordinate ) .. " km."
|
||||
|
||||
local Task = self:GetTask() -- This is to dermine that the coordinates are for a specific task mode (A2A or A2G).
|
||||
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:F( { CC.ReferencePoints } )
|
||||
for ZoneName, Zone in pairs( CC.ReferencePoints ) do
|
||||
self:F( { ZoneName = ZoneName } )
|
||||
local Zone = Zone -- Core.Zone#ZONE
|
||||
local ZoneCoord = Zone:GetCoordinate()
|
||||
local ZoneDistance = ZoneCoord:Get2DDistance( self.Coordinate )
|
||||
self:F( { 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, nil, Task )
|
||||
end
|
||||
end
|
||||
|
||||
if self.Zone and self.RouteMode == "C" then
|
||||
local Coordinate = self.Zone:GetCoordinate()
|
||||
RouteText = "Route to zone at " .. Coordinate:ToString()
|
||||
end
|
||||
|
||||
return RouteText
|
||||
end
|
||||
|
||||
|
||||
function ACT_ROUTE:MenuCancel()
|
||||
self:Cancel()
|
||||
self:F("Cancelled")
|
||||
self.CancelMenuGroupCommand:Remove()
|
||||
self:__Cancel( 1 )
|
||||
end
|
||||
|
||||
--- Task Events
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ROUTE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
@@ -201,7 +231,7 @@ do -- ACT_ROUTE
|
||||
|
||||
--- Check if the controllable has arrived.
|
||||
-- @param #ACT_ROUTE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @return #boolean
|
||||
function ACT_ROUTE:onfuncHasArrived( ProcessUnit )
|
||||
return false
|
||||
@@ -209,15 +239,13 @@ do -- ACT_ROUTE
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ROUTE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ROUTE:onbeforeRoute( ProcessUnit, From, Event, To )
|
||||
self:F( { "BeforeRoute 1", self.DisplayCount, self.DisplayInterval } )
|
||||
|
||||
if ProcessUnit:IsAlive() then
|
||||
self:F( "BeforeRoute 2" )
|
||||
local HasArrived = self:onfuncHasArrived( ProcessUnit ) -- Polymorphic
|
||||
if self.DisplayCount >= self.DisplayInterval then
|
||||
self:T( { HasArrived = HasArrived } )
|
||||
@@ -229,8 +257,6 @@ do -- ACT_ROUTE
|
||||
self.DisplayCount = self.DisplayCount + 1
|
||||
end
|
||||
|
||||
self:T( { DisplayCount = self.DisplayCount } )
|
||||
|
||||
if HasArrived then
|
||||
self:__Arrive( 1 )
|
||||
else
|
||||
@@ -313,7 +339,7 @@ do -- ACT_ROUTE_POINT
|
||||
-- @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:F2( { Range } )
|
||||
self.Range = Range or 10000
|
||||
end
|
||||
|
||||
@@ -321,12 +347,13 @@ do -- ACT_ROUTE_POINT
|
||||
-- @param #ACT_ROUTE_POINT self
|
||||
-- @return #number The Range to consider the arrival. Default is 10000 meters.
|
||||
function ACT_ROUTE_POINT:GetRange()
|
||||
self:F2( { self.Range } )
|
||||
return self.Range
|
||||
end
|
||||
|
||||
--- Method override to check if the controllable has arrived.
|
||||
-- @param #ACT_ROUTE_POINT self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @return #boolean
|
||||
function ACT_ROUTE_POINT:onfuncHasArrived( ProcessUnit )
|
||||
|
||||
@@ -335,7 +362,7 @@ do -- ACT_ROUTE_POINT
|
||||
|
||||
if Distance <= self.Range then
|
||||
local RouteText = "You have arrived."
|
||||
self:Message( RouteText )
|
||||
self:GetCommandCenter():MessageTypeToGroup( RouteText, ProcessUnit:GetGroup(), MESSAGE.Type.Information )
|
||||
return true
|
||||
end
|
||||
end
|
||||
@@ -347,15 +374,15 @@ do -- ACT_ROUTE_POINT
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ROUTE_POINT self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ROUTE_POINT:onafterReport( ProcessUnit, From, Event, To )
|
||||
|
||||
local TaskUnitCoordinate = ProcessUnit:GetCoordinate()
|
||||
local RouteText = self:GetRouteText( TaskUnitCoordinate )
|
||||
self:Message( RouteText )
|
||||
local RouteText = self:GetRouteText( ProcessUnit )
|
||||
|
||||
self:GetCommandCenter():MessageTypeToGroup( RouteText, ProcessUnit:GetGroup(), MESSAGE.Type.Update )
|
||||
end
|
||||
|
||||
end -- ACT_ROUTE_POINT
|
||||
@@ -403,8 +430,12 @@ do -- ACT_ROUTE_ZONE
|
||||
--- Set Zone
|
||||
-- @param #ACT_ROUTE_ZONE self
|
||||
-- @param Core.Zone#ZONE_BASE Zone The Zone object where to route to.
|
||||
function ACT_ROUTE_ZONE:SetZone( Zone )
|
||||
-- @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
|
||||
@@ -416,13 +447,13 @@ do -- ACT_ROUTE_ZONE
|
||||
|
||||
--- Method override to check if the controllable has arrived.
|
||||
-- @param #ACT_ROUTE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @return #boolean
|
||||
function ACT_ROUTE_ZONE:onfuncHasArrived( ProcessUnit )
|
||||
|
||||
if ProcessUnit:IsInZone( self.Zone ) then
|
||||
local RouteText = "You have arrived within the zone."
|
||||
self:Message( RouteText )
|
||||
self:GetCommandCenter():MessageTypeToGroup( RouteText, ProcessUnit:GetGroup(), MESSAGE.Type.Information )
|
||||
end
|
||||
|
||||
return ProcessUnit:IsInZone( self.Zone )
|
||||
@@ -432,18 +463,15 @@ do -- ACT_ROUTE_ZONE
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ROUTE_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ROUTE_ZONE:onafterReport( ProcessUnit, From, Event, To )
|
||||
self:F( { ProcessUnit = ProcessUnit } )
|
||||
|
||||
local ZoneVec2 = self.Zone:GetVec2()
|
||||
local ZoneCoordinate = COORDINATE:New( ZoneVec2.x, ZoneVec2.y )
|
||||
local TaskUnitVec2 = ProcessUnit:GetVec2()
|
||||
local TaskUnitCoordinate = COORDINATE:New( TaskUnitVec2.x, TaskUnitVec2.y )
|
||||
local RouteText = self:GetRouteText( TaskUnitCoordinate )
|
||||
self:Message( RouteText )
|
||||
local RouteText = self:GetRouteText( ProcessUnit )
|
||||
self:GetCommandCenter():MessageTypeToGroup( RouteText, ProcessUnit:GetGroup(), MESSAGE.Type.Update )
|
||||
end
|
||||
|
||||
end -- ACT_ROUTE_ZONE
|
||||
|
||||
1142
Moose Development/Moose/Cargo/Cargo.lua
Normal file
1142
Moose Development/Moose/Cargo/Cargo.lua
Normal file
File diff suppressed because it is too large
Load Diff
316
Moose Development/Moose/Cargo/CargoCrate.lua
Normal file
316
Moose Development/Moose/Cargo/CargoCrate.lua
Normal file
@@ -0,0 +1,316 @@
|
||||
--- **Cargo** -- Management of single cargo crates, which are based on a @{Static} object.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### [Demo Missions]()
|
||||
--
|
||||
-- ### [YouTube Playlist]()
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module CargoCrate
|
||||
|
||||
do -- CARGO_CRATE
|
||||
|
||||
--- Models the behaviour of cargo crates, which can be slingloaded and boarded on helicopters.
|
||||
-- @type CARGO_CRATE
|
||||
-- @extends Cargo.Cargo#CARGO_REPRESENTABLE
|
||||
|
||||
--- # CARGO\_CRATE class, extends @{#CARGO_REPRESENTABLE}
|
||||
--
|
||||
-- The CARGO\_CRATE class defines a cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier.
|
||||
-- Use the event functions as described above to Load, UnLoad, Board, UnBoard the CARGO\_CRATE objects to and from carriers.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #CARGO_CRATE
|
||||
CARGO_CRATE = {
|
||||
ClassName = "CARGO_CRATE"
|
||||
}
|
||||
|
||||
--- CARGO_CRATE Constructor.
|
||||
-- @param #CARGO_CRATE self
|
||||
-- @param Wrapper.Static#STATIC CargoStatic
|
||||
-- @param #string Type
|
||||
-- @param #string Name
|
||||
-- @param #number LoadRadius (optional)
|
||||
-- @param #number NearRadius (optional)
|
||||
-- @return #CARGO_CRATE
|
||||
function CARGO_CRATE:New( CargoStatic, Type, Name, LoadRadius, NearRadius )
|
||||
local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoStatic, Type, Name, nil, LoadRadius, NearRadius ) ) -- #CARGO_CRATE
|
||||
self:F( { Type, Name, NearRadius } )
|
||||
|
||||
self.CargoObject = CargoStatic -- Wrapper.Static#STATIC
|
||||
|
||||
-- Cargo objects are added to the _DATABASE and SET_CARGO objects.
|
||||
_EVENTDISPATCHER:CreateEventNewCargo( self )
|
||||
|
||||
self:HandleEvent( EVENTS.Dead, self.OnEventCargoDead )
|
||||
self:HandleEvent( EVENTS.Crash, self.OnEventCargoDead )
|
||||
self:HandleEvent( EVENTS.PlayerLeaveUnit, self.OnEventCargoDead )
|
||||
|
||||
self:SetEventPriority( 4 )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- @param #CARGO_CRATE self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function CARGO_CRATE:OnEventCargoDead( EventData )
|
||||
|
||||
local Destroyed = false
|
||||
|
||||
if self:IsDestroyed() or self:IsUnLoaded() or self:IsBoarding() then
|
||||
if self.CargoObject:GetName() == EventData.IniUnitName then
|
||||
if not self.NoDestroy then
|
||||
Destroyed = true
|
||||
end
|
||||
end
|
||||
else
|
||||
if self:IsLoaded() then
|
||||
local CarrierName = self.CargoCarrier:GetName()
|
||||
if CarrierName == EventData.IniDCSUnitName then
|
||||
MESSAGE:New( "Cargo is lost from carrier " .. CarrierName, 15 ):ToAll()
|
||||
Destroyed = true
|
||||
self.CargoCarrier:ClearCargo()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if Destroyed then
|
||||
self:I( { "Cargo crate destroyed: " .. self.CargoObject:GetName() } )
|
||||
self:Destroyed()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Enter UnLoaded State.
|
||||
-- @param #CARGO_CRATE self
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Core.Point#POINT_VEC2
|
||||
function CARGO_CRATE:onenterUnLoaded( From, Event, To, ToPointVec2 )
|
||||
--self:F( { ToPointVec2, From, Event, To } )
|
||||
|
||||
local Angle = 180
|
||||
local Speed = 10
|
||||
local Distance = 10
|
||||
|
||||
if From == "Loaded" then
|
||||
local StartCoordinate = self.CargoCarrier:GetCoordinate()
|
||||
local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees.
|
||||
local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle )
|
||||
local CargoDeployCoord = StartCoordinate:Translate( Distance, CargoDeployHeading )
|
||||
|
||||
ToPointVec2 = ToPointVec2 or COORDINATE:NewFromVec2( { x= CargoDeployCoord.x, y = CargoDeployCoord.z } )
|
||||
|
||||
-- Respawn the group...
|
||||
if self.CargoObject then
|
||||
self.CargoObject:ReSpawnAt( ToPointVec2, 0 )
|
||||
self.CargoCarrier = nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if self.OnUnLoadedCallBack then
|
||||
self.OnUnLoadedCallBack( self, unpack( self.OnUnLoadedParameters ) )
|
||||
self.OnUnLoadedCallBack = nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Loaded State.
|
||||
-- @param #CARGO_CRATE self
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Wrapper.Unit#UNIT CargoCarrier
|
||||
function CARGO_CRATE:onenterLoaded( From, Event, To, CargoCarrier )
|
||||
--self:F( { From, Event, To, CargoCarrier } )
|
||||
|
||||
self.CargoCarrier = CargoCarrier
|
||||
|
||||
-- Only destroy the CargoObject is if there is a CargoObject (packages don't have CargoObjects).
|
||||
if self.CargoObject then
|
||||
self:T("Destroying")
|
||||
self.NoDestroy = true
|
||||
self.CargoObject:Destroy()
|
||||
--local Coordinate = self.CargoObject:GetCoordinate():GetRandomCoordinateInRadius( 50, 20 )
|
||||
--self.CargoObject:ReSpawnAt( Coordinate, 0 )
|
||||
end
|
||||
end
|
||||
|
||||
--- Check if the cargo can be Boarded.
|
||||
-- @param #CARGO_CRATE self
|
||||
function CARGO_CRATE:CanBoard()
|
||||
return false
|
||||
end
|
||||
|
||||
--- Check if the cargo can be Unboarded.
|
||||
-- @param #CARGO_CRATE self
|
||||
function CARGO_CRATE:CanUnboard()
|
||||
return false
|
||||
end
|
||||
|
||||
--- Check if Cargo Crate is in the radius for the Cargo to be reported.
|
||||
-- @param #CARGO self
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @return #boolean true if the Cargo Crate is within the report radius.
|
||||
function CARGO_CRATE:IsInReportRadius( Coordinate )
|
||||
--self:F( { Coordinate, LoadRadius = self.LoadRadius } )
|
||||
|
||||
local Distance = 0
|
||||
if self:IsUnLoaded() then
|
||||
Distance = Coordinate:Get2DDistance( self.CargoObject:GetCoordinate() )
|
||||
--self:T( Distance )
|
||||
if Distance <= self.LoadRadius then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
--- Check if Cargo Crate is in the radius for the Cargo to be Boarded or Loaded.
|
||||
-- @param #CARGO self
|
||||
-- @param Core.Point#Coordinate Coordinate
|
||||
-- @return #boolean true if the Cargo Crate is within the loading radius.
|
||||
function CARGO_CRATE:IsInLoadRadius( Coordinate )
|
||||
--self:F( { Coordinate, LoadRadius = self.NearRadius } )
|
||||
|
||||
local Distance = 0
|
||||
if self:IsUnLoaded() then
|
||||
Distance = Coordinate:Get2DDistance( self.CargoObject:GetCoordinate() )
|
||||
--self:T( Distance )
|
||||
if Distance <= self.NearRadius then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Get the current Coordinate of the CargoGroup.
|
||||
-- @param #CARGO_CRATE self
|
||||
-- @return Core.Point#COORDINATE The current Coordinate of the first Cargo of the CargoGroup.
|
||||
-- @return #nil There is no valid Cargo in the CargoGroup.
|
||||
function CARGO_CRATE:GetCoordinate()
|
||||
--self:F()
|
||||
|
||||
return self.CargoObject:GetCoordinate()
|
||||
end
|
||||
|
||||
--- Check if the CargoGroup is alive.
|
||||
-- @param #CARGO_CRATE self
|
||||
-- @return #boolean true if the CargoGroup is alive.
|
||||
-- @return #boolean false if the CargoGroup is dead.
|
||||
function CARGO_CRATE:IsAlive()
|
||||
|
||||
local Alive = true
|
||||
|
||||
-- When the Cargo is Loaded, the Cargo is in the CargoCarrier, so we check if the CargoCarrier is alive.
|
||||
-- When the Cargo is not Loaded, the Cargo is the CargoObject, so we check if the CargoObject is alive.
|
||||
if self:IsLoaded() then
|
||||
Alive = Alive == true and self.CargoCarrier:IsAlive()
|
||||
else
|
||||
Alive = Alive == true and self.CargoObject:IsAlive()
|
||||
end
|
||||
|
||||
return Alive
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Route Cargo to Coordinate and randomize locations.
|
||||
-- @param #CARGO_CRATE self
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
function CARGO_CRATE:RouteTo( Coordinate )
|
||||
self:F( {Coordinate = Coordinate } )
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Check if Cargo is near to the Carrier.
|
||||
-- The Cargo is near to the Carrier within NearRadius.
|
||||
-- @param #CARGO_CRATE self
|
||||
-- @param Wrapper.Group#GROUP CargoCarrier
|
||||
-- @param #number NearRadius
|
||||
-- @return #boolean The Cargo is near to the Carrier.
|
||||
-- @return #nil The Cargo is not near to the Carrier.
|
||||
function CARGO_CRATE:IsNear( CargoCarrier, NearRadius )
|
||||
self:F( {NearRadius = NearRadius } )
|
||||
|
||||
return self:IsNear( CargoCarrier:GetCoordinate(), NearRadius )
|
||||
end
|
||||
|
||||
--- Respawn the CargoGroup.
|
||||
-- @param #CARGO_CRATE self
|
||||
function CARGO_CRATE:Respawn()
|
||||
|
||||
self:F( { "Respawning crate " .. self:GetName() } )
|
||||
|
||||
|
||||
-- Respawn the group...
|
||||
if self.CargoObject then
|
||||
self.CargoObject:ReSpawn() -- A cargo destroy crates a DEAD event.
|
||||
self:__Reset( -0.1 )
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Respawn the CargoGroup.
|
||||
-- @param #CARGO_CRATE self
|
||||
function CARGO_CRATE:onafterReset()
|
||||
|
||||
self:F( { "Reset crate " .. self:GetName() } )
|
||||
|
||||
|
||||
-- Respawn the group...
|
||||
if self.CargoObject then
|
||||
self:SetDeployed( false )
|
||||
self:SetStartState( "UnLoaded" )
|
||||
self.CargoCarrier = nil
|
||||
-- Cargo objects are added to the _DATABASE and SET_CARGO objects.
|
||||
_EVENTDISPATCHER:CreateEventNewCargo( self )
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- Get the transportation method of the Cargo.
|
||||
-- @param #CARGO_CRATE self
|
||||
-- @return #string The transportation method of the Cargo.
|
||||
function CARGO_CRATE:GetTransportationMethod()
|
||||
if self:IsLoaded() then
|
||||
return "for unloading"
|
||||
else
|
||||
if self:IsUnLoaded() then
|
||||
return "for loading"
|
||||
else
|
||||
if self:IsDeployed() then
|
||||
return "delivered"
|
||||
end
|
||||
end
|
||||
end
|
||||
return ""
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
770
Moose Development/Moose/Cargo/CargoGroup.lua
Normal file
770
Moose Development/Moose/Cargo/CargoGroup.lua
Normal file
@@ -0,0 +1,770 @@
|
||||
--- **Cargo** -- Management of grouped cargo logistics, which are based on a @{Group} object.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### [Demo Missions]()
|
||||
--
|
||||
-- ### [YouTube Playlist]()
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module CargoGroup
|
||||
|
||||
|
||||
do -- CARGO_GROUP
|
||||
|
||||
--- @type CARGO_GROUP
|
||||
-- @extends Cargo.Cargo#CARGO_REPORTABLE
|
||||
-- @field Core.Set#SET_CARGO CargoSet The collection of derived CARGO objects.
|
||||
-- @field #string GroupName The name of the CargoGroup.
|
||||
|
||||
--- # CARGO\_GROUP class
|
||||
--
|
||||
-- The CARGO\_GROUP class defines a cargo that is represented by a @{Group} object within the simulator.
|
||||
-- The cargo can be Loaded, UnLoaded, Boarded, UnBoarded to and from Carriers.
|
||||
--
|
||||
-- The above cargo classes are used by the AI\_CARGO\_ classes to allow AI groups to transport cargo:
|
||||
--
|
||||
-- * AI Armoured Personnel Carriers to transport cargo and engage in battles, using the @{AI.AI_Cargo_APC#AI_CARGO_APC} class.
|
||||
-- * AI Helicopters to transport cargo, using the @{AI.AI_Cargo_Helicopter#AI_CARGO_HELICOPTER} class.
|
||||
-- * AI Planes to transport cargo, using the @{AI.AI_Cargo_Plane#AI_CARGO_PLANE} class.
|
||||
-- * AI Ships is planned.
|
||||
--
|
||||
-- The above cargo classes are also used by the TASK\_CARGO\_ classes to allow human players to transport cargo as part of a tasking:
|
||||
--
|
||||
-- * @{Tasking.Task_Cargo_Transport#TASK_CARGO_TRANSPORT} to transport cargo by human players.
|
||||
-- * @{Tasking.Task_Cargo_Transport#TASK_CARGO_CSAR} to transport downed pilots by human players.
|
||||
--
|
||||
-- The
|
||||
--
|
||||
-- @field #CARGO_GROUP CARGO_GROUP
|
||||
--
|
||||
CARGO_GROUP = {
|
||||
ClassName = "CARGO_GROUP",
|
||||
}
|
||||
|
||||
--- CARGO_GROUP constructor.
|
||||
-- This make a new CARGO_GROUP from a @{Group} object.
|
||||
-- It will "ungroup" the group object within the sim, and will create a @{Set} of individual Unit objects.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @param Wrapper.Group#GROUP CargoGroup
|
||||
-- @param #string Type
|
||||
-- @param #string Name
|
||||
-- @param #number LoadRadius (optional)
|
||||
-- @param #number NearRadius (optional)
|
||||
-- @return #CARGO_GROUP
|
||||
function CARGO_GROUP:New( CargoGroup, Type, Name, LoadRadius )
|
||||
local self = BASE:Inherit( self, CARGO_REPORTABLE:New( Type, Name, 0, LoadRadius ) ) -- #CARGO_GROUP
|
||||
self:F( { Type, Name, LoadRadius } )
|
||||
|
||||
self.CargoSet = SET_CARGO:New()
|
||||
self.CargoGroup = CargoGroup
|
||||
self.Grouped = true
|
||||
self.CargoUnitTemplate = {}
|
||||
|
||||
self:SetDeployed( false )
|
||||
|
||||
local WeightGroup = 0
|
||||
|
||||
self.CargoGroup:Destroy()
|
||||
|
||||
local GroupName = CargoGroup:GetName()
|
||||
self.CargoName = GroupName:match("(.*)~CARGO") or GroupName
|
||||
self.CargoTemplate = UTILS.DeepCopy( _DATABASE:GetGroupTemplate( GroupName ) )
|
||||
|
||||
local GroupTemplate = UTILS.DeepCopy( self.CargoTemplate )
|
||||
GroupTemplate.name = self.CargoName .. "#CARGO"
|
||||
GroupTemplate.groupId = nil
|
||||
|
||||
GroupTemplate.units = {}
|
||||
|
||||
for UnitID, UnitTemplate in pairs( self.CargoTemplate.units ) do
|
||||
UnitTemplate.name = UnitTemplate.name .. "#CARGO"
|
||||
local CargoUnitName = UnitTemplate.name
|
||||
self.CargoUnitTemplate[CargoUnitName] = UnitTemplate
|
||||
|
||||
GroupTemplate.units[#GroupTemplate.units+1] = self.CargoUnitTemplate[CargoUnitName]
|
||||
GroupTemplate.units[#GroupTemplate.units].unitId = nil
|
||||
|
||||
-- And we register the spawned unit as part of the CargoSet.
|
||||
local Unit = UNIT:Register( CargoUnitName )
|
||||
--local WeightUnit = Unit:GetDesc().massEmpty
|
||||
--WeightGroup = WeightGroup + WeightUnit
|
||||
local CargoUnit = CARGO_UNIT:New( Unit, Type, CargoUnitName, 10 )
|
||||
self.CargoSet:Add( CargoUnitName, CargoUnit )
|
||||
end
|
||||
|
||||
-- Then we register the new group in the database
|
||||
self.CargoGroup = GROUP:NewTemplate( GroupTemplate, GroupTemplate.CoalitionID, GroupTemplate.CategoryID, GroupTemplate.CountryID)
|
||||
|
||||
-- Now we spawn the new group based on the template created.
|
||||
_DATABASE:Spawn( GroupTemplate )
|
||||
|
||||
self:SetWeight( WeightGroup )
|
||||
self.CargoLimit = 10
|
||||
|
||||
self:T( { "Weight Cargo", WeightGroup } )
|
||||
|
||||
-- Cargo objects are added to the _DATABASE and SET_CARGO objects.
|
||||
_EVENTDISPATCHER:CreateEventNewCargo( self )
|
||||
|
||||
self:HandleEvent( EVENTS.Dead, self.OnEventCargoDead )
|
||||
self:HandleEvent( EVENTS.Crash, self.OnEventCargoDead )
|
||||
self:HandleEvent( EVENTS.PlayerLeaveUnit, self.OnEventCargoDead )
|
||||
|
||||
self:SetEventPriority( 4 )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Ungroup the cargo group into individual groups with one unit.
|
||||
-- This is required because by default a group will move in formation and this is really an issue for group control.
|
||||
-- Therefore this method is made to be able to ungroup a group.
|
||||
-- This works for ground only groups.
|
||||
-- @param #CARGO_GROUP self
|
||||
function CARGO_GROUP:Ungroup()
|
||||
|
||||
if self.Grouped == true then
|
||||
|
||||
self.Grouped = false
|
||||
|
||||
self.CargoGroup:Destroy()
|
||||
|
||||
for CargoUnitName, CargoUnit in pairs( self.CargoSet:GetSet() ) do
|
||||
local CargoUnit = CargoUnit -- Cargo.CargoUnit#CARGO_UNIT
|
||||
|
||||
if CargoUnit:IsUnLoaded() then
|
||||
local GroupTemplate = UTILS.DeepCopy( self.CargoTemplate )
|
||||
--local GroupName = env.getValueDictByKey( GroupTemplate.name )
|
||||
|
||||
-- We create a new group object with one unit...
|
||||
-- First we prepare the template...
|
||||
GroupTemplate.name = self.CargoName .. "#CARGO#" .. CargoUnitName
|
||||
GroupTemplate.groupId = nil
|
||||
|
||||
if CargoUnit:IsUnLoaded() then
|
||||
GroupTemplate.units = {}
|
||||
GroupTemplate.units[1] = self.CargoUnitTemplate[CargoUnitName]
|
||||
GroupTemplate.units[#GroupTemplate.units].unitId = nil
|
||||
GroupTemplate.units[#GroupTemplate.units].x = CargoUnit:GetX()
|
||||
GroupTemplate.units[#GroupTemplate.units].y = CargoUnit:GetY()
|
||||
GroupTemplate.units[#GroupTemplate.units].heading = CargoUnit:GetHeading()
|
||||
end
|
||||
|
||||
|
||||
-- Then we register the new group in the database
|
||||
local CargoGroup = GROUP:NewTemplate( GroupTemplate, GroupTemplate.CoalitionID, GroupTemplate.CategoryID, GroupTemplate.CountryID)
|
||||
|
||||
-- Now we spawn the new group based on the template created.
|
||||
_DATABASE:Spawn( GroupTemplate )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Regroup the cargo group into one group with multiple unit.
|
||||
-- This is required because by default a group will move in formation and this is really an issue for group control.
|
||||
-- Therefore this method is made to be able to regroup a group.
|
||||
-- This works for ground only groups.
|
||||
-- @param #CARGO_GROUP self
|
||||
function CARGO_GROUP:Regroup()
|
||||
|
||||
self:F("Regroup")
|
||||
|
||||
if self.Grouped == false then
|
||||
|
||||
self.Grouped = true
|
||||
|
||||
local GroupTemplate = UTILS.DeepCopy( self.CargoTemplate )
|
||||
GroupTemplate.name = self.CargoName .. "#CARGO"
|
||||
GroupTemplate.groupId = nil
|
||||
GroupTemplate.units = {}
|
||||
|
||||
for CargoUnitName, CargoUnit in pairs( self.CargoSet:GetSet() ) do
|
||||
local CargoUnit = CargoUnit -- Cargo.CargoUnit#CARGO_UNIT
|
||||
|
||||
self:F( { CargoUnit:GetName(), UnLoaded = CargoUnit:IsUnLoaded() } )
|
||||
|
||||
if CargoUnit:IsUnLoaded() then
|
||||
|
||||
CargoUnit.CargoObject:Destroy()
|
||||
|
||||
GroupTemplate.units[#GroupTemplate.units+1] = self.CargoUnitTemplate[CargoUnitName]
|
||||
GroupTemplate.units[#GroupTemplate.units].unitId = nil
|
||||
GroupTemplate.units[#GroupTemplate.units].x = CargoUnit:GetX()
|
||||
GroupTemplate.units[#GroupTemplate.units].y = CargoUnit:GetY()
|
||||
GroupTemplate.units[#GroupTemplate.units].heading = CargoUnit:GetHeading()
|
||||
end
|
||||
end
|
||||
|
||||
-- Then we register the new group in the database
|
||||
self.CargoGroup = GROUP:NewTemplate( GroupTemplate, GroupTemplate.CoalitionID, GroupTemplate.CategoryID, GroupTemplate.CountryID)
|
||||
|
||||
self:F( { "Regroup", GroupTemplate } )
|
||||
|
||||
-- Now we spawn the new group based on the template created.
|
||||
_DATABASE:Spawn( GroupTemplate )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- @param #CARGO_GROUP self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function CARGO_GROUP:OnEventCargoDead( EventData )
|
||||
self:I( EventData )
|
||||
|
||||
local Destroyed = false
|
||||
|
||||
if self:IsDestroyed() or self:IsUnLoaded() or self:IsBoarding() or self:IsUnboarding() then
|
||||
Destroyed = true
|
||||
for CargoID, CargoData in pairs( self.CargoSet:GetSet() ) do
|
||||
local Cargo = CargoData -- #CARGO
|
||||
if Cargo:IsAlive() then
|
||||
Destroyed = false
|
||||
else
|
||||
Cargo:Destroyed()
|
||||
end
|
||||
end
|
||||
else
|
||||
local CarrierName = self.CargoCarrier:GetName()
|
||||
if CarrierName == EventData.IniDCSUnitName then
|
||||
MESSAGE:New( "Cargo is lost from carrier " .. CarrierName, 15 ):ToAll()
|
||||
Destroyed = true
|
||||
self.CargoCarrier:ClearCargo()
|
||||
end
|
||||
end
|
||||
|
||||
if Destroyed then
|
||||
self:Destroyed()
|
||||
self:E( { "Cargo group destroyed" } )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Enter Boarding State.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @param Wrapper.Unit#UNIT CargoCarrier
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function CARGO_GROUP:onenterBoarding( From, Event, To, CargoCarrier, NearRadius, ... )
|
||||
--self:F( { CargoCarrier.UnitName, From, Event, To } )
|
||||
|
||||
local NearRadius = NearRadius or 25
|
||||
|
||||
if From == "UnLoaded" then
|
||||
|
||||
-- For each Cargo object within the CARGO_GROUPED, route each object to the CargoLoadPointVec2
|
||||
self.CargoSet:ForEach(
|
||||
function( Cargo, ... )
|
||||
Cargo:__Board( 1, CargoCarrier, NearRadius, ... )
|
||||
end, ...
|
||||
)
|
||||
|
||||
self:__Boarding( 1, CargoCarrier, NearRadius, ... )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Enter Loaded State.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Wrapper.Unit#UNIT CargoCarrier
|
||||
function CARGO_GROUP:onenterLoaded( From, Event, To, CargoCarrier, ... )
|
||||
--self:F( { From, Event, To, CargoCarrier, ...} )
|
||||
|
||||
if From == "UnLoaded" then
|
||||
-- For each Cargo object within the CARGO_GROUP, load each cargo to the CargoCarrier.
|
||||
for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do
|
||||
Cargo:Load( CargoCarrier )
|
||||
end
|
||||
end
|
||||
|
||||
--self.CargoObject:Destroy()
|
||||
self.CargoCarrier = CargoCarrier
|
||||
self.CargoCarrier:AddCargo( self )
|
||||
|
||||
end
|
||||
|
||||
--- Leave Boarding State.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @param Wrapper.Unit#UNIT CargoCarrier
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function CARGO_GROUP:onafterBoarding( From, Event, To, CargoCarrier, NearRadius, ... )
|
||||
--self:F( { CargoCarrier.UnitName, From, Event, To } )
|
||||
|
||||
local NearRadius = NearRadius or 100
|
||||
|
||||
local Boarded = true
|
||||
local Cancelled = false
|
||||
local Dead = true
|
||||
|
||||
self.CargoSet:Flush()
|
||||
|
||||
-- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2
|
||||
for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do
|
||||
self:T( { Cargo:GetName(), Cargo.current } )
|
||||
|
||||
|
||||
if not Cargo:is( "Loaded" )
|
||||
and (not Cargo:is( "Destroyed" )) then -- If one or more units of a group defined as CARGO_GROUP died, the CARGO_GROUP:Board() command does not trigger the CARGO_GRUOP:OnEnterLoaded() function.
|
||||
Boarded = false
|
||||
end
|
||||
|
||||
if Cargo:is( "UnLoaded" ) then
|
||||
Cancelled = true
|
||||
end
|
||||
|
||||
if not Cargo:is( "Destroyed" ) then
|
||||
Dead = false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if not Dead then
|
||||
|
||||
if not Cancelled then
|
||||
if not Boarded then
|
||||
self:__Boarding( 1, CargoCarrier, NearRadius, ... )
|
||||
else
|
||||
self:F("Group Cargo is loaded")
|
||||
self:__Load( 1, CargoCarrier, ... )
|
||||
end
|
||||
else
|
||||
self:__CancelBoarding( 1, CargoCarrier, NearRadius, ... )
|
||||
end
|
||||
else
|
||||
self:__Destroyed( 1, CargoCarrier, NearRadius, ... )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Enter UnBoarding State.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @param Core.Point#POINT_VEC2 ToPointVec2
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function CARGO_GROUP:onenterUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... )
|
||||
--self:F( {From, Event, To, ToPointVec2, NearRadius } )
|
||||
|
||||
NearRadius = NearRadius or 25
|
||||
|
||||
local Timer = 1
|
||||
|
||||
if From == "Loaded" then
|
||||
|
||||
if self.CargoObject then
|
||||
self.CargoObject:Destroy()
|
||||
end
|
||||
|
||||
-- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2
|
||||
self.CargoSet:ForEach(
|
||||
--- @param Cargo.Cargo#CARGO Cargo
|
||||
function( Cargo, NearRadius )
|
||||
if not Cargo:IsDestroyed() then
|
||||
Cargo:__UnBoard( Timer, ToPointVec2, NearRadius )
|
||||
Timer = Timer + 3
|
||||
end
|
||||
end, { NearRadius }
|
||||
)
|
||||
|
||||
|
||||
self:__UnBoarding( 1, ToPointVec2, NearRadius, ... )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Leave UnBoarding State.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @param Core.Point#POINT_VEC2 ToPointVec2
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function CARGO_GROUP:onleaveUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... )
|
||||
--self:F( { From, Event, To, ToPointVec2, NearRadius } )
|
||||
|
||||
--local NearRadius = NearRadius or 25
|
||||
|
||||
local Angle = 180
|
||||
local Speed = 10
|
||||
local Distance = 5
|
||||
|
||||
if From == "UnBoarding" then
|
||||
local UnBoarded = true
|
||||
|
||||
-- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2
|
||||
for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do
|
||||
self:T( { Cargo:GetName(), Cargo.current } )
|
||||
if not Cargo:is( "UnLoaded" ) and not Cargo:IsDestroyed() then
|
||||
UnBoarded = false
|
||||
end
|
||||
end
|
||||
|
||||
if UnBoarded then
|
||||
return true
|
||||
else
|
||||
self:__UnBoarding( 1, ToPointVec2, NearRadius, ... )
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- UnBoard Event.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @param Core.Point#POINT_VEC2 ToPointVec2
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function CARGO_GROUP:onafterUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... )
|
||||
--self:F( { From, Event, To, ToPointVec2, NearRadius } )
|
||||
|
||||
--local NearRadius = NearRadius or 25
|
||||
|
||||
self:__UnLoad( 1, ToPointVec2, ... )
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Enter UnLoaded State.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @param Core.Point#POINT_VEC2
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function CARGO_GROUP:onenterUnLoaded( From, Event, To, ToPointVec2, ... )
|
||||
--self:F( { From, Event, To, ToPointVec2 } )
|
||||
|
||||
if From == "Loaded" then
|
||||
|
||||
-- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2
|
||||
self.CargoSet:ForEach(
|
||||
function( Cargo )
|
||||
--Cargo:UnLoad( ToPointVec2 )
|
||||
local RandomVec2=ToPointVec2:GetRandomPointVec2InRadius(10)
|
||||
Cargo:UnLoad( RandomVec2 )
|
||||
end
|
||||
)
|
||||
|
||||
end
|
||||
|
||||
self.CargoCarrier:RemoveCargo( self )
|
||||
self.CargoCarrier = nil
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Get the current Coordinate of the CargoGroup.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @return Core.Point#COORDINATE The current Coordinate of the first Cargo of the CargoGroup.
|
||||
-- @return #nil There is no valid Cargo in the CargoGroup.
|
||||
function CARGO_GROUP:GetCoordinate()
|
||||
self:F()
|
||||
|
||||
local Cargo = self:GetFirstAlive() -- Cargo.Cargo#CARGO
|
||||
|
||||
if Cargo then
|
||||
return Cargo.CargoObject:GetCoordinate()
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Get the x position of the cargo.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @return #number
|
||||
function CARGO:GetX()
|
||||
|
||||
local Cargo = self:GetFirstAlive() -- Cargo.Cargo#CARGO
|
||||
|
||||
if Cargo then
|
||||
return Cargo:GetCoordinate().x
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Get the y position of the cargo.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @return #number
|
||||
function CARGO:GetY()
|
||||
|
||||
local Cargo = self:GetFirstAlive() -- Cargo.Cargo#CARGO
|
||||
|
||||
if Cargo then
|
||||
return Cargo:GetCoordinate().z
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Check if the CargoGroup is alive.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @return #boolean true if the CargoGroup is alive.
|
||||
-- @return #boolean false if the CargoGroup is dead.
|
||||
function CARGO_GROUP:IsAlive()
|
||||
|
||||
local Cargo = self:GetFirstAlive() -- Cargo.Cargo#CARGO
|
||||
return Cargo ~= nil
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Get the first alive Cargo Unit of the Cargo Group.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @return #CARGO_GROUP
|
||||
function CARGO_GROUP:GetFirstAlive()
|
||||
|
||||
local CargoFirstAlive = nil
|
||||
|
||||
for _, Cargo in pairs( self.CargoSet:GetSet() ) do
|
||||
if not Cargo:IsDestroyed() then
|
||||
CargoFirstAlive = Cargo
|
||||
break
|
||||
end
|
||||
end
|
||||
return CargoFirstAlive
|
||||
end
|
||||
|
||||
|
||||
--- Get the amount of cargo units in the group.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @return #CARGO_GROUP
|
||||
function CARGO_GROUP:GetCount()
|
||||
return self.CargoSet:Count()
|
||||
end
|
||||
|
||||
|
||||
--- Get the amount of cargo units in the group.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @return #CARGO_GROUP
|
||||
function CARGO_GROUP:GetGroup( Cargo )
|
||||
local Cargo = Cargo or self:GetFirstAlive() -- Cargo.Cargo#CARGO
|
||||
return Cargo.CargoObject:GetGroup()
|
||||
end
|
||||
|
||||
|
||||
--- Route Cargo to Coordinate and randomize locations.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
function CARGO_GROUP:RouteTo( Coordinate )
|
||||
--self:F( {Coordinate = Coordinate } )
|
||||
|
||||
-- For each Cargo within the CargoSet, route each object to the Coordinate
|
||||
self.CargoSet:ForEach(
|
||||
function( Cargo )
|
||||
Cargo.CargoObject:RouteGroundTo( Coordinate, 10, "vee", 0 )
|
||||
end
|
||||
)
|
||||
|
||||
end
|
||||
|
||||
--- Check if Cargo is near to the Carrier.
|
||||
-- The Cargo is near to the Carrier if the first unit of the Cargo Group is within NearRadius.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @param Wrapper.Group#GROUP CargoCarrier
|
||||
-- @param #number NearRadius
|
||||
-- @return #boolean The Cargo is near to the Carrier.
|
||||
-- @return #nil The Cargo is not near to the Carrier.
|
||||
function CARGO_GROUP:IsNear( CargoCarrier, NearRadius )
|
||||
self:F( {NearRadius = NearRadius } )
|
||||
|
||||
for _, Cargo in pairs( self.CargoSet:GetSet() ) do
|
||||
local Cargo = Cargo -- Cargo.Cargo#CARGO
|
||||
if Cargo:IsAlive() then
|
||||
if Cargo:IsNear( CargoCarrier:GetCoordinate(), NearRadius ) then
|
||||
self:F( "Near" )
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Check if Cargo Group is in the radius for the Cargo to be Boarded.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @return #boolean true if the Cargo Group is within the load radius.
|
||||
function CARGO_GROUP:IsInLoadRadius( Coordinate )
|
||||
--self:F( { Coordinate } )
|
||||
|
||||
local Cargo = self:GetFirstAlive() -- Cargo.Cargo#CARGO
|
||||
|
||||
if Cargo then
|
||||
local Distance = 0
|
||||
if Cargo:IsLoaded() then
|
||||
Distance = Coordinate:Get2DDistance( Cargo.CargoCarrier:GetCoordinate() )
|
||||
else
|
||||
Distance = Coordinate:Get2DDistance( Cargo.CargoObject:GetCoordinate() )
|
||||
end
|
||||
|
||||
self:F( { Distance = Distance, LoadRadius = self.LoadRadius } )
|
||||
if Distance <= self.LoadRadius then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Check if Cargo Group is in the report radius.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @param Core.Point#Coordinate Coordinate
|
||||
-- @return #boolean true if the Cargo Group is within the report radius.
|
||||
function CARGO_GROUP:IsInReportRadius( Coordinate )
|
||||
--self:F( { Coordinate } )
|
||||
|
||||
local Cargo = self:GetFirstAlive() -- Cargo.Cargo#CARGO
|
||||
|
||||
if Cargo then
|
||||
self:F( { Cargo } )
|
||||
local Distance = 0
|
||||
if Cargo:IsUnLoaded() then
|
||||
Distance = Coordinate:Get2DDistance( Cargo.CargoObject:GetCoordinate() )
|
||||
--self:T( Distance )
|
||||
if Distance <= self.LoadRadius then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
|
||||
end
|
||||
|
||||
--- Respawn the CargoGroup.
|
||||
-- @param #CARGO_GROUP self
|
||||
function CARGO_GROUP:Respawn()
|
||||
|
||||
self:F( { "Respawning" } )
|
||||
|
||||
for CargoID, CargoData in pairs( self.CargoSet:GetSet() ) do
|
||||
local Cargo = CargoData -- #CARGO
|
||||
Cargo:Destroy()
|
||||
Cargo:SetStartState( "UnLoaded" )
|
||||
end
|
||||
|
||||
|
||||
-- We iterate through the group template and for each unit in the template, we create a new group with one unit.
|
||||
for UnitID, UnitTemplate in pairs( self.CargoTemplate.units ) do
|
||||
|
||||
local GroupTemplate = UTILS.DeepCopy( self.CargoTemplate )
|
||||
local GroupName = env.getValueDictByKey( GroupTemplate.name )
|
||||
|
||||
-- We create a new group object with one unit...
|
||||
-- First we prepare the template...
|
||||
GroupTemplate.name = GroupName .. "#CARGO#" .. UnitID
|
||||
GroupTemplate.groupId = nil
|
||||
GroupTemplate.units = {}
|
||||
GroupTemplate.units[1] = UnitTemplate
|
||||
local UnitName = UnitTemplate.name .. "#CARGO"
|
||||
GroupTemplate.units[1].name = UnitTemplate.name .. "#CARGO"
|
||||
|
||||
|
||||
-- Then we register the new group in the database
|
||||
local CargoGroup = GROUP:NewTemplate( GroupTemplate, GroupTemplate.CoalitionID, GroupTemplate.CategoryID, GroupTemplate.CountryID)
|
||||
|
||||
-- Now we spawn the new group based on the template created.
|
||||
_DATABASE:Spawn( GroupTemplate )
|
||||
|
||||
-- And we register the spawned unit as part of the CargoSet.
|
||||
local Unit = UNIT:FindByName( UnitName )
|
||||
--local WeightUnit = Unit:GetDesc().massEmpty
|
||||
--WeightGroup = WeightGroup + WeightUnit
|
||||
local CargoUnit = CARGO_UNIT:New( Unit, Type, UnitName, 10 )
|
||||
self.CargoSet:Add( UnitName, CargoUnit )
|
||||
end
|
||||
|
||||
self:SetDeployed( false )
|
||||
self:SetStartState( "UnLoaded" )
|
||||
|
||||
end
|
||||
|
||||
--- Signal a flare at the position of the CargoGroup.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @param Utilities.Utils#FLARECOLOR FlareColor
|
||||
function CARGO_GROUP:Flare( FlareColor )
|
||||
|
||||
local Cargo = self.CargoSet:GetFirst() -- #CARGO
|
||||
if Cargo then
|
||||
Cargo:Flare( FlareColor )
|
||||
end
|
||||
end
|
||||
|
||||
--- Smoke the CargoGroup.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @param Utilities.Utils#SMOKECOLOR SmokeColor The color of the smoke.
|
||||
-- @param #number Radius The radius of randomization around the center of the first element of the CargoGroup.
|
||||
function CARGO_GROUP:Smoke( SmokeColor, Radius )
|
||||
|
||||
local Cargo = self.CargoSet:GetFirst() -- #CARGO
|
||||
|
||||
if Cargo then
|
||||
Cargo:Smoke( SmokeColor, Radius )
|
||||
end
|
||||
end
|
||||
|
||||
--- Check if the first element of the CargoGroup is the given @{Zone}.
|
||||
-- @param #CARGO self
|
||||
-- @param Core.Zone#ZONE_BASE Zone
|
||||
-- @return #boolean **true** if the first element of the CargoGroup is in the Zone
|
||||
-- @return #boolean **false** if there is no element of the CargoGroup in the Zone.
|
||||
function CARGO_GROUP:IsInZone( Zone )
|
||||
--self:F( { Zone } )
|
||||
|
||||
local Cargo = self.CargoSet:GetFirst() -- #CARGO
|
||||
|
||||
if Cargo then
|
||||
return Cargo:IsInZone( Zone )
|
||||
end
|
||||
|
||||
return nil
|
||||
|
||||
end
|
||||
|
||||
--- Get the transportation method of the Cargo.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @return #string The transportation method of the Cargo.
|
||||
function CARGO_GROUP:GetTransportationMethod()
|
||||
if self:IsLoaded() then
|
||||
return "for unboarding"
|
||||
else
|
||||
if self:IsUnLoaded() then
|
||||
return "for boarding"
|
||||
else
|
||||
if self:IsDeployed() then
|
||||
return "delivered"
|
||||
end
|
||||
end
|
||||
end
|
||||
return ""
|
||||
end
|
||||
|
||||
|
||||
|
||||
end -- CARGO_GROUP
|
||||
267
Moose Development/Moose/Cargo/CargoSlingload.lua
Normal file
267
Moose Development/Moose/Cargo/CargoSlingload.lua
Normal file
@@ -0,0 +1,267 @@
|
||||
--- **Cargo** -- Management of single cargo crates, which are based on a @{Static} object. The cargo can only be slingloaded.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### [Demo Missions]()
|
||||
--
|
||||
-- ### [YouTube Playlist]()
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module CargoCrate
|
||||
|
||||
|
||||
do -- CARGO_SLINGLOAD
|
||||
|
||||
--- Models the behaviour of cargo crates, which can only be slingloaded.
|
||||
-- @type CARGO_SLINGLOAD
|
||||
-- @extends Cargo.Cargo#CARGO_REPRESENTABLE
|
||||
|
||||
--- # CARGO\_CRATE class, extends @{#CARGO_REPRESENTABLE}
|
||||
--
|
||||
-- The CARGO\_CRATE class defines a cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #CARGO_SLINGLOAD
|
||||
CARGO_SLINGLOAD = {
|
||||
ClassName = "CARGO_SLINGLOAD"
|
||||
}
|
||||
|
||||
--- CARGO_SLINGLOAD Constructor.
|
||||
-- @param #CARGO_SLINGLOAD self
|
||||
-- @param Wrapper.Static#STATIC CargoStatic
|
||||
-- @param #string Type
|
||||
-- @param #string Name
|
||||
-- @param #number LoadRadius (optional)
|
||||
-- @param #number NearRadius (optional)
|
||||
-- @return #CARGO_SLINGLOAD
|
||||
function CARGO_SLINGLOAD:New( CargoStatic, Type, Name, LoadRadius, NearRadius )
|
||||
local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoStatic, Type, Name, nil, LoadRadius, NearRadius ) ) -- #CARGO_SLINGLOAD
|
||||
self:F( { Type, Name, NearRadius } )
|
||||
|
||||
self.CargoObject = CargoStatic
|
||||
|
||||
-- Cargo objects are added to the _DATABASE and SET_CARGO objects.
|
||||
_EVENTDISPATCHER:CreateEventNewCargo( self )
|
||||
|
||||
self:HandleEvent( EVENTS.Dead, self.OnEventCargoDead )
|
||||
self:HandleEvent( EVENTS.Crash, self.OnEventCargoDead )
|
||||
self:HandleEvent( EVENTS.PlayerLeaveUnit, self.OnEventCargoDead )
|
||||
|
||||
self:SetEventPriority( 4 )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- @param #CARGO_SLINGLOAD self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function CARGO_SLINGLOAD:OnEventCargoDead( EventData )
|
||||
|
||||
local Destroyed = false
|
||||
|
||||
if self:IsDestroyed() or self:IsUnLoaded() then
|
||||
if self.CargoObject:GetName() == EventData.IniUnitName then
|
||||
if not self.NoDestroy then
|
||||
Destroyed = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if Destroyed then
|
||||
self:I( { "Cargo crate destroyed: " .. self.CargoObject:GetName() } )
|
||||
self:Destroyed()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Check if the cargo can be Slingloaded.
|
||||
-- @param #CARGO self
|
||||
function CARGO:CanSlingload()
|
||||
return true
|
||||
end
|
||||
|
||||
--- Check if the cargo can be Boarded.
|
||||
-- @param #CARGO_SLINGLOAD self
|
||||
function CARGO_SLINGLOAD:CanBoard()
|
||||
return false
|
||||
end
|
||||
|
||||
--- Check if the cargo can be Unboarded.
|
||||
-- @param #CARGO_SLINGLOAD self
|
||||
function CARGO_SLINGLOAD:CanUnboard()
|
||||
return false
|
||||
end
|
||||
|
||||
--- Check if the cargo can be Loaded.
|
||||
-- @param #CARGO_SLINGLOAD self
|
||||
function CARGO_SLINGLOAD:CanLoad()
|
||||
return false
|
||||
end
|
||||
|
||||
--- Check if the cargo can be Unloaded.
|
||||
-- @param #CARGO_SLINGLOAD self
|
||||
function CARGO_SLINGLOAD:CanUnload()
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
--- Check if Cargo Crate is in the radius for the Cargo to be reported.
|
||||
-- @param #CARGO_SLINGLOAD self
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @return #boolean true if the Cargo Crate is within the report radius.
|
||||
function CARGO_SLINGLOAD:IsInReportRadius( Coordinate )
|
||||
--self:F( { Coordinate, LoadRadius = self.LoadRadius } )
|
||||
|
||||
local Distance = 0
|
||||
if self:IsUnLoaded() then
|
||||
Distance = Coordinate:Get2DDistance( self.CargoObject:GetCoordinate() )
|
||||
if Distance <= self.LoadRadius then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
--- Check if Cargo Slingload is in the radius for the Cargo to be Boarded or Loaded.
|
||||
-- @param #CARGO_SLINGLOAD self
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @return #boolean true if the Cargo Slingload is within the loading radius.
|
||||
function CARGO_SLINGLOAD:IsInLoadRadius( Coordinate )
|
||||
--self:F( { Coordinate } )
|
||||
|
||||
local Distance = 0
|
||||
if self:IsUnLoaded() then
|
||||
Distance = Coordinate:Get2DDistance( self.CargoObject:GetCoordinate() )
|
||||
if Distance <= self.NearRadius then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Get the current Coordinate of the CargoGroup.
|
||||
-- @param #CARGO_SLINGLOAD self
|
||||
-- @return Core.Point#COORDINATE The current Coordinate of the first Cargo of the CargoGroup.
|
||||
-- @return #nil There is no valid Cargo in the CargoGroup.
|
||||
function CARGO_SLINGLOAD:GetCoordinate()
|
||||
--self:F()
|
||||
|
||||
return self.CargoObject:GetCoordinate()
|
||||
end
|
||||
|
||||
--- Check if the CargoGroup is alive.
|
||||
-- @param #CARGO_SLINGLOAD self
|
||||
-- @return #boolean true if the CargoGroup is alive.
|
||||
-- @return #boolean false if the CargoGroup is dead.
|
||||
function CARGO_SLINGLOAD:IsAlive()
|
||||
|
||||
local Alive = true
|
||||
|
||||
-- When the Cargo is Loaded, the Cargo is in the CargoCarrier, so we check if the CargoCarrier is alive.
|
||||
-- When the Cargo is not Loaded, the Cargo is the CargoObject, so we check if the CargoObject is alive.
|
||||
if self:IsLoaded() then
|
||||
Alive = Alive == true and self.CargoCarrier:IsAlive()
|
||||
else
|
||||
Alive = Alive == true and self.CargoObject:IsAlive()
|
||||
end
|
||||
|
||||
return Alive
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Route Cargo to Coordinate and randomize locations.
|
||||
-- @param #CARGO_SLINGLOAD self
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
function CARGO_SLINGLOAD:RouteTo( Coordinate )
|
||||
--self:F( {Coordinate = Coordinate } )
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Check if Cargo is near to the Carrier.
|
||||
-- The Cargo is near to the Carrier within NearRadius.
|
||||
-- @param #CARGO_SLINGLOAD self
|
||||
-- @param Wrapper.Group#GROUP CargoCarrier
|
||||
-- @param #number NearRadius
|
||||
-- @return #boolean The Cargo is near to the Carrier.
|
||||
-- @return #nil The Cargo is not near to the Carrier.
|
||||
function CARGO_SLINGLOAD:IsNear( CargoCarrier, NearRadius )
|
||||
--self:F( {NearRadius = NearRadius } )
|
||||
|
||||
return self:IsNear( CargoCarrier:GetCoordinate(), NearRadius )
|
||||
end
|
||||
|
||||
|
||||
--- Respawn the CargoGroup.
|
||||
-- @param #CARGO_SLINGLOAD self
|
||||
function CARGO_SLINGLOAD:Respawn()
|
||||
|
||||
--self:F( { "Respawning slingload " .. self:GetName() } )
|
||||
|
||||
|
||||
-- Respawn the group...
|
||||
if self.CargoObject then
|
||||
self.CargoObject:ReSpawn() -- A cargo destroy crates a DEAD event.
|
||||
self:__Reset( -0.1 )
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Respawn the CargoGroup.
|
||||
-- @param #CARGO_SLINGLOAD self
|
||||
function CARGO_SLINGLOAD:onafterReset()
|
||||
|
||||
--self:F( { "Reset slingload " .. self:GetName() } )
|
||||
|
||||
|
||||
-- Respawn the group...
|
||||
if self.CargoObject then
|
||||
self:SetDeployed( false )
|
||||
self:SetStartState( "UnLoaded" )
|
||||
self.CargoCarrier = nil
|
||||
-- Cargo objects are added to the _DATABASE and SET_CARGO objects.
|
||||
_EVENTDISPATCHER:CreateEventNewCargo( self )
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- Get the transportation method of the Cargo.
|
||||
-- @param #CARGO_SLINGLOAD self
|
||||
-- @return #string The transportation method of the Cargo.
|
||||
function CARGO_SLINGLOAD:GetTransportationMethod()
|
||||
if self:IsLoaded() then
|
||||
return "for sling loading"
|
||||
else
|
||||
if self:IsUnLoaded() then
|
||||
return "for sling loading"
|
||||
else
|
||||
if self:IsDeployed() then
|
||||
return "delivered"
|
||||
end
|
||||
end
|
||||
end
|
||||
return ""
|
||||
end
|
||||
|
||||
end
|
||||
398
Moose Development/Moose/Cargo/CargoUnit.lua
Normal file
398
Moose Development/Moose/Cargo/CargoUnit.lua
Normal file
@@ -0,0 +1,398 @@
|
||||
--- **Cargo** -- Management of single cargo logistics, which are based on a @{Unit} object.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### [Demo Missions]()
|
||||
--
|
||||
-- ### [YouTube Playlist]()
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module CargoUnit
|
||||
|
||||
do -- CARGO_UNIT
|
||||
|
||||
--- Models CARGO in the form of units, which can be boarded, unboarded, loaded, unloaded.
|
||||
-- @type CARGO_UNIT
|
||||
-- @extends Cargo.Cargo#CARGO_REPRESENTABLE
|
||||
|
||||
--- # CARGO\_UNIT class, extends @{#CARGO_REPRESENTABLE}
|
||||
--
|
||||
-- The CARGO\_UNIT class defines a cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier.
|
||||
-- Use the event functions as described above to Load, UnLoad, Board, UnBoard the CARGO\_UNIT objects to and from carriers.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #CARGO_UNIT CARGO_UNIT
|
||||
--
|
||||
CARGO_UNIT = {
|
||||
ClassName = "CARGO_UNIT"
|
||||
}
|
||||
|
||||
--- CARGO_UNIT Constructor.
|
||||
-- @param #CARGO_UNIT self
|
||||
-- @param Wrapper.Unit#UNIT CargoUnit
|
||||
-- @param #string Type
|
||||
-- @param #string Name
|
||||
-- @param #number Weight
|
||||
-- @param #number LoadRadius (optional)
|
||||
-- @param #number NearRadius (optional)
|
||||
-- @return #CARGO_UNIT
|
||||
function CARGO_UNIT:New( CargoUnit, Type, Name, Weight, NearRadius )
|
||||
local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoUnit, Type, Name, Weight, NearRadius ) ) -- #CARGO_UNIT
|
||||
self:I( { Type, Name, Weight, NearRadius } )
|
||||
|
||||
self:T( CargoUnit )
|
||||
self.CargoObject = CargoUnit
|
||||
|
||||
self:T( self.ClassName )
|
||||
|
||||
self:SetEventPriority( 5 )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Enter UnBoarding State.
|
||||
-- @param #CARGO_UNIT self
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Core.Point#POINT_VEC2 ToPointVec2
|
||||
function CARGO_UNIT:onenterUnBoarding( From, Event, To, ToPointVec2, NearRadius )
|
||||
self:F( { From, Event, To, ToPointVec2, NearRadius } )
|
||||
|
||||
NearRadius = NearRadius or 25
|
||||
|
||||
local Angle = 180
|
||||
local Speed = 60
|
||||
local DeployDistance = 9
|
||||
local RouteDistance = 60
|
||||
|
||||
if From == "Loaded" then
|
||||
|
||||
if not self:IsDestroyed() then
|
||||
|
||||
local CargoCarrier = self.CargoCarrier -- Wrapper.Controllable#CONTROLLABLE
|
||||
|
||||
if CargoCarrier:IsAlive() then
|
||||
|
||||
local CargoCarrierPointVec2 = CargoCarrier:GetPointVec2()
|
||||
local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees.
|
||||
local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle )
|
||||
|
||||
|
||||
local CargoRoutePointVec2 = CargoCarrierPointVec2:Translate( RouteDistance, CargoDeployHeading )
|
||||
|
||||
|
||||
-- if there is no ToPointVec2 given, then use the CargoRoutePointVec2
|
||||
local FromDirectionVec3 = CargoCarrierPointVec2:GetDirectionVec3( ToPointVec2 or CargoRoutePointVec2 )
|
||||
local FromAngle = CargoCarrierPointVec2:GetAngleDegrees(FromDirectionVec3)
|
||||
local FromPointVec2 = CargoCarrierPointVec2:Translate( DeployDistance, FromAngle )
|
||||
--local CargoDeployPointVec2 = CargoCarrierPointVec2:GetRandomCoordinateInRadius( 10, 5 )
|
||||
|
||||
ToPointVec2 = ToPointVec2 or CargoCarrierPointVec2:GetRandomCoordinateInRadius( NearRadius, DeployDistance )
|
||||
|
||||
-- Respawn the group...
|
||||
if self.CargoObject then
|
||||
self.CargoObject:ReSpawnAt( FromPointVec2, CargoDeployHeading )
|
||||
self:F( { "CargoUnits:", self.CargoObject:GetGroup():GetName() } )
|
||||
self.CargoCarrier = nil
|
||||
|
||||
local Points = {}
|
||||
|
||||
-- From
|
||||
Points[#Points+1] = FromPointVec2:WaypointGround( Speed, "Vee" )
|
||||
|
||||
-- To
|
||||
Points[#Points+1] = ToPointVec2:WaypointGround( Speed, "Vee" )
|
||||
|
||||
local TaskRoute = self.CargoObject:TaskRoute( Points )
|
||||
self.CargoObject:SetTask( TaskRoute, 1 )
|
||||
|
||||
|
||||
self:__UnBoarding( 1, ToPointVec2, NearRadius )
|
||||
end
|
||||
else
|
||||
-- the Carrier is dead. This cargo is dead too!
|
||||
self:Destroyed()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Leave UnBoarding State.
|
||||
-- @param #CARGO_UNIT self
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Core.Point#POINT_VEC2 ToPointVec2
|
||||
function CARGO_UNIT:onleaveUnBoarding( From, Event, To, ToPointVec2, NearRadius )
|
||||
self:F( { From, Event, To, ToPointVec2, NearRadius } )
|
||||
|
||||
NearRadius = NearRadius or 100
|
||||
|
||||
local Angle = 180
|
||||
local Speed = 10
|
||||
local Distance = 5
|
||||
|
||||
if From == "UnBoarding" then
|
||||
--if self:IsNear( ToPointVec2, NearRadius ) then
|
||||
return true
|
||||
--else
|
||||
|
||||
--self:__UnBoarding( 1, ToPointVec2, NearRadius )
|
||||
--end
|
||||
--return false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- UnBoard Event.
|
||||
-- @param #CARGO_UNIT self
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Core.Point#POINT_VEC2 ToPointVec2
|
||||
function CARGO_UNIT:onafterUnBoarding( From, Event, To, ToPointVec2, NearRadius )
|
||||
self:F( { From, Event, To, ToPointVec2, NearRadius } )
|
||||
|
||||
NearRadius = NearRadius or 100
|
||||
|
||||
self.CargoInAir = self.CargoObject:InAir()
|
||||
|
||||
self:T( self.CargoInAir )
|
||||
|
||||
-- Only unboard the cargo when the carrier is not in the air.
|
||||
-- (eg. cargo can be on a oil derrick, moving the cargo on the oil derrick will drop the cargo on the sea).
|
||||
if not self.CargoInAir then
|
||||
|
||||
end
|
||||
|
||||
self:__UnLoad( 1, ToPointVec2, NearRadius )
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Enter UnLoaded State.
|
||||
-- @param #CARGO_UNIT self
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Core.Point#POINT_VEC2
|
||||
function CARGO_UNIT:onenterUnLoaded( From, Event, To, ToPointVec2 )
|
||||
self:F( { ToPointVec2, From, Event, To } )
|
||||
|
||||
local Angle = 180
|
||||
local Speed = 10
|
||||
local Distance = 5
|
||||
|
||||
if From == "Loaded" then
|
||||
local StartPointVec2 = self.CargoCarrier:GetPointVec2()
|
||||
local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees.
|
||||
local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle )
|
||||
local CargoDeployCoord = StartPointVec2:Translate( Distance, CargoDeployHeading )
|
||||
|
||||
ToPointVec2 = ToPointVec2 or COORDINATE:New( CargoDeployCoord.x, CargoDeployCoord.z )
|
||||
|
||||
-- Respawn the group...
|
||||
if self.CargoObject then
|
||||
self.CargoObject:ReSpawnAt( ToPointVec2, 0 )
|
||||
self.CargoCarrier = nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if self.OnUnLoadedCallBack then
|
||||
self.OnUnLoadedCallBack( self, unpack( self.OnUnLoadedParameters ) )
|
||||
self.OnUnLoadedCallBack = nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Board Event.
|
||||
-- @param #CARGO_UNIT self
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function CARGO_UNIT:onafterBoard( From, Event, To, CargoCarrier, NearRadius, ... )
|
||||
self:F( { From, Event, To, CargoCarrier, NearRadius } )
|
||||
|
||||
local NearRadius = NearRadius or 25
|
||||
|
||||
self.CargoInAir = self.CargoObject:InAir()
|
||||
|
||||
local Desc = self.CargoObject:GetDesc()
|
||||
local MaxSpeed = Desc.speedMaxOffRoad
|
||||
local TypeName = Desc.typeName
|
||||
|
||||
self:T( self.CargoInAir )
|
||||
|
||||
-- Only move the group to the carrier when the cargo is not in the air
|
||||
-- (eg. cargo can be on a oil derrick, moving the cargo on the oil derrick will drop the cargo on the sea).
|
||||
if not self.CargoInAir then
|
||||
if self:IsNear( CargoCarrier:GetPointVec2(), NearRadius ) then
|
||||
self:Load( CargoCarrier, NearRadius, ... )
|
||||
else
|
||||
if MaxSpeed and MaxSpeed == 0 or TypeName and TypeName == "Stinger comm" then
|
||||
self:Load( CargoCarrier, NearRadius, ... )
|
||||
else
|
||||
|
||||
local Speed = 90
|
||||
local Angle = 180
|
||||
local Distance = 5
|
||||
|
||||
NearRadius = NearRadius or 25
|
||||
|
||||
local CargoCarrierPointVec2 = CargoCarrier:GetPointVec2()
|
||||
local CargoCarrierHeading = CargoCarrier:GetHeading() -- Get Heading of object in degrees.
|
||||
local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle )
|
||||
local CargoDeployPointVec2 = CargoCarrierPointVec2:Translate( Distance, CargoDeployHeading )
|
||||
|
||||
-- Set the CargoObject to state Green to ensure it is boarding!
|
||||
self.CargoObject:OptionAlarmStateGreen()
|
||||
|
||||
local Points = {}
|
||||
|
||||
local PointStartVec2 = self.CargoObject:GetPointVec2()
|
||||
|
||||
Points[#Points+1] = PointStartVec2:WaypointGround( Speed )
|
||||
Points[#Points+1] = CargoDeployPointVec2:WaypointGround( Speed )
|
||||
|
||||
local TaskRoute = self.CargoObject:TaskRoute( Points )
|
||||
self.CargoObject:SetTask( TaskRoute, 2 )
|
||||
self:__Boarding( -1, CargoCarrier, NearRadius )
|
||||
self.RunCount = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Boarding Event.
|
||||
-- @param #CARGO_UNIT self
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Wrapper.Client#CLIENT CargoCarrier
|
||||
-- @param #number NearRadius
|
||||
function CARGO_UNIT:onafterBoarding( From, Event, To, CargoCarrier, NearRadius, ... )
|
||||
--self:F( { From, Event, To, CargoCarrier.UnitName, NearRadius } )
|
||||
|
||||
|
||||
if CargoCarrier and CargoCarrier:IsAlive() and self.CargoObject and self.CargoObject:IsAlive() then
|
||||
if CargoCarrier:InAir() == false then
|
||||
if self:IsNear( CargoCarrier:GetPointVec2(), NearRadius ) then
|
||||
self:__Load( 1, CargoCarrier, ... )
|
||||
else
|
||||
self:__Boarding( -1, CargoCarrier, NearRadius, ... )
|
||||
self.RunCount = self.RunCount + 1
|
||||
if self.RunCount >= 40 then
|
||||
self.RunCount = 0
|
||||
local Speed = 90
|
||||
local Angle = 180
|
||||
local Distance = 5
|
||||
|
||||
NearRadius = NearRadius or 25
|
||||
|
||||
local CargoCarrierPointVec2 = CargoCarrier:GetPointVec2()
|
||||
local CargoCarrierHeading = CargoCarrier:GetHeading() -- Get Heading of object in degrees.
|
||||
local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle )
|
||||
local CargoDeployPointVec2 = CargoCarrierPointVec2:Translate( Distance, CargoDeployHeading )
|
||||
|
||||
-- Set the CargoObject to state Green to ensure it is boarding!
|
||||
self.CargoObject:OptionAlarmStateGreen()
|
||||
|
||||
local Points = {}
|
||||
|
||||
local PointStartVec2 = self.CargoObject:GetPointVec2()
|
||||
|
||||
Points[#Points+1] = PointStartVec2:WaypointGround( Speed, "Off road" )
|
||||
Points[#Points+1] = CargoDeployPointVec2:WaypointGround( Speed, "Off road" )
|
||||
|
||||
local TaskRoute = self.CargoObject:TaskRoute( Points )
|
||||
self.CargoObject:SetTask( TaskRoute, 0.2 )
|
||||
end
|
||||
end
|
||||
else
|
||||
self.CargoObject:MessageToGroup( "Cancelling Boarding... Get back on the ground!", 5, CargoCarrier:GetGroup(), self:GetName() )
|
||||
self:CancelBoarding( CargoCarrier, NearRadius, ... )
|
||||
self.CargoObject:SetCommand( self.CargoObject:CommandStopRoute( true ) )
|
||||
end
|
||||
else
|
||||
self:E("Something is wrong")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Enter Boarding State.
|
||||
-- @param #CARGO_UNIT self
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Wrapper.Unit#UNIT CargoCarrier
|
||||
function CARGO_UNIT:onenterBoarding( From, Event, To, CargoCarrier, NearRadius, ... )
|
||||
--self:F( { From, Event, To, CargoCarrier.UnitName, NearRadius } )
|
||||
|
||||
local Speed = 90
|
||||
local Angle = 180
|
||||
local Distance = 5
|
||||
|
||||
local NearRadius = NearRadius or 25
|
||||
|
||||
if From == "UnLoaded" or From == "Boarding" then
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Loaded State.
|
||||
-- @param #CARGO_UNIT self
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Wrapper.Unit#UNIT CargoCarrier
|
||||
function CARGO_UNIT:onenterLoaded( From, Event, To, CargoCarrier )
|
||||
self:F( { From, Event, To, CargoCarrier } )
|
||||
|
||||
self.CargoCarrier = CargoCarrier
|
||||
|
||||
-- Only destroy the CargoObject is if there is a CargoObject (packages don't have CargoObjects).
|
||||
if self.CargoObject then
|
||||
self:T("Destroying")
|
||||
self.CargoObject:Destroy()
|
||||
--self.CargoObject:ReSpawnAt( COORDINATE:NewFromVec2( {x=0,y=0} ), 0 )
|
||||
end
|
||||
end
|
||||
|
||||
--- Get the transportation method of the Cargo.
|
||||
-- @param #CARGO_UNIT self
|
||||
-- @return #string The transportation method of the Cargo.
|
||||
function CARGO_UNIT:GetTransportationMethod()
|
||||
if self:IsLoaded() then
|
||||
return "for unboarding"
|
||||
else
|
||||
if self:IsUnLoaded() then
|
||||
return "for boarding"
|
||||
else
|
||||
if self:IsDeployed() then
|
||||
return "delivered"
|
||||
end
|
||||
end
|
||||
end
|
||||
return ""
|
||||
end
|
||||
|
||||
end -- CARGO_UNIT
|
||||
@@ -1,36 +1,13 @@
|
||||
--- **Core** - BASE forms **the basis of the MOOSE framework**. Each class within the MOOSE framework derives from BASE.
|
||||
--- **Core** -- BASE forms **the basis of the MOOSE framework**. Each class within the MOOSE framework derives from BASE.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- The @{#BASE} class is the core root class from where every other class in moose is derived.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # **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**
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- * None.
|
||||
--
|
||||
-- ### Authors:
|
||||
--
|
||||
-- * **FlightControl**: Design & Programming
|
||||
-- ===
|
||||
--
|
||||
-- @module Base
|
||||
|
||||
@@ -221,7 +198,17 @@ BASE = {
|
||||
ClassID = 0,
|
||||
Events = {},
|
||||
States = {},
|
||||
_ = {},
|
||||
Debug = debug,
|
||||
Scheduler = nil,
|
||||
}
|
||||
|
||||
|
||||
--- @field #BASE.__
|
||||
BASE.__ = {}
|
||||
|
||||
--- @field #BASE._
|
||||
BASE._ = {
|
||||
Schedules = {} --- Contains the Schedulers Active
|
||||
}
|
||||
|
||||
--- The Formation Class
|
||||
@@ -247,47 +234,19 @@ FORMATION = {
|
||||
-- @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
|
||||
|
||||
|
||||
-- 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
|
||||
|
||||
--- This is the worker method to inherit from a parent class.
|
||||
-- @param #BASE self
|
||||
-- @param Child is the Child class that inherits.
|
||||
@@ -295,18 +254,40 @@ 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
|
||||
|
||||
|
||||
-- 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
|
||||
|
||||
|
||||
local function getParent( Child )
|
||||
local Parent = nil
|
||||
|
||||
if Child.ClassName == 'BASE' then
|
||||
Parent = nil
|
||||
else
|
||||
if rawget( Child, "__" ) then
|
||||
Parent = getmetatable( Child.__ ).__index
|
||||
else
|
||||
Parent = getmetatable( Child ).__index
|
||||
end
|
||||
end
|
||||
return Parent
|
||||
end
|
||||
|
||||
|
||||
--- This is the worker method to retrieve the Parent class.
|
||||
-- Note that the Parent class must be passed to call the parent class method.
|
||||
--
|
||||
@@ -316,12 +297,91 @@ end
|
||||
-- @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 )
|
||||
return Parent
|
||||
function BASE:GetParent( Child, FromClass )
|
||||
|
||||
|
||||
local Parent
|
||||
-- BASE class has no parent
|
||||
if Child.ClassName == 'BASE' then
|
||||
Parent = nil
|
||||
else
|
||||
|
||||
--self:E({FromClass = FromClass})
|
||||
--self:E({Child = Child.ClassName})
|
||||
if FromClass then
|
||||
while( Child.ClassName ~= "BASE" and Child.ClassName ~= FromClass.ClassName ) do
|
||||
Child = getParent( Child )
|
||||
--self:E({Child.ClassName})
|
||||
end
|
||||
end
|
||||
if Child.ClassName == 'BASE' then
|
||||
Parent = nil
|
||||
else
|
||||
Parent = getParent( Child )
|
||||
end
|
||||
end
|
||||
--self:E({Parent.ClassName})
|
||||
return Parent
|
||||
end
|
||||
|
||||
--- This is the worker method to check if an object is an (sub)instance of a class.
|
||||
--
|
||||
-- ### Examples:
|
||||
--
|
||||
-- * ZONE:New( 'some zone' ):IsInstanceOf( ZONE ) will return true
|
||||
-- * ZONE:New( 'some zone' ):IsInstanceOf( 'ZONE' ) will return true
|
||||
-- * ZONE:New( 'some zone' ):IsInstanceOf( 'zone' ) will return true
|
||||
-- * ZONE:New( 'some zone' ):IsInstanceOf( 'BASE' ) will return true
|
||||
--
|
||||
-- * ZONE:New( 'some zone' ):IsInstanceOf( 'GROUP' ) will return false
|
||||
--
|
||||
-- @param #BASE self
|
||||
-- @param ClassName is the name of the class or the class itself to run the check against
|
||||
-- @return #boolean
|
||||
function BASE:IsInstanceOf( ClassName )
|
||||
|
||||
-- Is className NOT a string ?
|
||||
if type( ClassName ) ~= 'string' then
|
||||
|
||||
-- Is className a Moose class ?
|
||||
if type( ClassName ) == 'table' and ClassName.ClassName ~= nil then
|
||||
|
||||
-- Get the name of the Moose class as a string
|
||||
ClassName = ClassName.ClassName
|
||||
|
||||
-- className is neither a string nor a Moose class, throw an error
|
||||
else
|
||||
|
||||
-- I'm not sure if this should take advantage of MOOSE logging function, or throw an error for pcall
|
||||
local err_str = 'className parameter should be a string; parameter received: '..type( ClassName )
|
||||
self:E( err_str )
|
||||
-- error( err_str )
|
||||
return false
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
ClassName = string.upper( ClassName )
|
||||
|
||||
if string.upper( self.ClassName ) == ClassName then
|
||||
return true
|
||||
end
|
||||
|
||||
local Parent = getParent(self)
|
||||
|
||||
while Parent do
|
||||
|
||||
if string.upper( Parent.ClassName ) == ClassName then
|
||||
return true
|
||||
end
|
||||
|
||||
Parent = getParent( Parent )
|
||||
|
||||
end
|
||||
|
||||
return false
|
||||
|
||||
end
|
||||
--- Get the ClassName + ClassID of the class instance.
|
||||
-- The ClassName + ClassID is formatted as '%s#%09d'.
|
||||
-- @param #BASE self
|
||||
@@ -402,7 +462,7 @@ do -- Event Handling
|
||||
-- @return #BASE
|
||||
function BASE:UnHandleEvent( Event )
|
||||
|
||||
self:EventDispatcher():Remove( self, Event )
|
||||
self:EventDispatcher():RemoveEvent( self, Event )
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -585,6 +645,38 @@ function BASE:CreateEventCrash( EventTime, Initiator )
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
--- Creation of a Dead Event.
|
||||
-- @param #BASE self
|
||||
-- @param Dcs.DCSTypes#Time EventTime The time stamp of the event.
|
||||
-- @param Dcs.DCSWrapper.Object#Object Initiator The initiating object of the event.
|
||||
function BASE:CreateEventDead( EventTime, Initiator )
|
||||
self:F( { EventTime, Initiator } )
|
||||
|
||||
local Event = {
|
||||
id = world.event.S_EVENT_DEAD,
|
||||
time = EventTime,
|
||||
initiator = Initiator,
|
||||
}
|
||||
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
--- Creation of a Takeoff Event.
|
||||
-- @param #BASE self
|
||||
-- @param Dcs.DCSTypes#Time EventTime The time stamp of the event.
|
||||
-- @param Dcs.DCSWrapper.Object#Object Initiator The initiating object of the event.
|
||||
function BASE:CreateEventTakeoff( EventTime, Initiator )
|
||||
self:F( { EventTime, Initiator } )
|
||||
|
||||
local Event = {
|
||||
id = world.event.S_EVENT_TAKEOFF,
|
||||
time = EventTime,
|
||||
initiator = Initiator,
|
||||
}
|
||||
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
-- TODO: Complete Dcs.DCSTypes#Event structure.
|
||||
--- The main event handling function... This function captures all events generated for the class.
|
||||
-- @param #BASE self
|
||||
@@ -615,6 +707,96 @@ function BASE:onEvent(event)
|
||||
end
|
||||
end
|
||||
|
||||
do -- Scheduling
|
||||
|
||||
--- Schedule a new time event. Note that the schedule will only take place if the scheduler is *started*. Even for a single schedule event, the scheduler needs to be started also.
|
||||
-- @param #BASE self
|
||||
-- @param #number Start Specifies the amount of seconds that will be waited before the scheduling is started, and the event function is called.
|
||||
-- @param #function SchedulerFunction The event function to be called when a timer event occurs. The event function needs to accept the parameters specified in SchedulerArguments.
|
||||
-- @param #table ... Optional arguments that can be given as part of scheduler. The arguments need to be given as a table { param1, param 2, ... }.
|
||||
-- @return #number The ScheduleID of the planned schedule.
|
||||
function BASE:ScheduleOnce( Start, SchedulerFunction, ... )
|
||||
self:F2( { Start } )
|
||||
self:T3( { ... } )
|
||||
|
||||
local ObjectName = "-"
|
||||
ObjectName = self.ClassName .. self.ClassID
|
||||
|
||||
self:F3( { "ScheduleOnce: ", ObjectName, Start } )
|
||||
|
||||
if not self.Scheduler then
|
||||
self.Scheduler = SCHEDULER:New( self )
|
||||
end
|
||||
|
||||
self.Scheduler.SchedulerObject = self.Scheduler
|
||||
|
||||
local ScheduleID = _SCHEDULEDISPATCHER:AddSchedule(
|
||||
self,
|
||||
SchedulerFunction,
|
||||
{ ... },
|
||||
Start,
|
||||
nil,
|
||||
nil,
|
||||
nil
|
||||
)
|
||||
|
||||
self._.Schedules[#self._.Schedules+1] = ScheduleID
|
||||
|
||||
return self._.Schedules[#self._.Schedules]
|
||||
end
|
||||
|
||||
--- Schedule a new time event. Note that the schedule will only take place if the scheduler is *started*. Even for a single schedule event, the scheduler needs to be started also.
|
||||
-- @param #BASE self
|
||||
-- @param #number Start Specifies the amount of seconds that will be waited before the scheduling is started, and the event function is called.
|
||||
-- @param #number Repeat Specifies the interval in seconds when the scheduler will call the event function.
|
||||
-- @param #number RandomizeFactor Specifies a randomization factor between 0 and 1 to randomize the Repeat.
|
||||
-- @param #number Stop Specifies the amount of seconds when the scheduler will be stopped.
|
||||
-- @param #function SchedulerFunction The event function to be called when a timer event occurs. The event function needs to accept the parameters specified in SchedulerArguments.
|
||||
-- @param #table ... Optional arguments that can be given as part of scheduler. The arguments need to be given as a table { param1, param 2, ... }.
|
||||
-- @return #number The ScheduleID of the planned schedule.
|
||||
function BASE:ScheduleRepeat( Start, Repeat, RandomizeFactor, Stop, SchedulerFunction, ... )
|
||||
self:F2( { Start } )
|
||||
self:T3( { ... } )
|
||||
|
||||
local ObjectName = "-"
|
||||
ObjectName = self.ClassName .. self.ClassID
|
||||
|
||||
self:F3( { "ScheduleRepeat: ", ObjectName, Start, Repeat, RandomizeFactor, Stop } )
|
||||
|
||||
if not self.Scheduler then
|
||||
self.Scheduler = SCHEDULER:New( self )
|
||||
end
|
||||
|
||||
self.Scheduler.SchedulerObject = self.Scheduler
|
||||
|
||||
local ScheduleID = _SCHEDULEDISPATCHER:AddSchedule(
|
||||
self,
|
||||
SchedulerFunction,
|
||||
{ ... },
|
||||
Start,
|
||||
Repeat,
|
||||
RandomizeFactor,
|
||||
Stop
|
||||
)
|
||||
|
||||
self._.Schedules[#self._.Schedules+1] = ScheduleID
|
||||
|
||||
return self._.Schedules[#self._.Schedules]
|
||||
end
|
||||
|
||||
--- Stops the Schedule.
|
||||
-- @param #BASE self
|
||||
-- @param #function SchedulerFunction The event function to be called when a timer event occurs. The event function needs to accept the parameters specified in SchedulerArguments.
|
||||
function BASE:ScheduleStop( SchedulerFunction )
|
||||
|
||||
self:F3( { "ScheduleStop:" } )
|
||||
|
||||
_SCHEDULEDISPATCHER:Stop( self.Scheduler, self._.Schedules[SchedulerFunction] )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Set a state or property of the Object given a Key and a Value.
|
||||
-- Note that if the Object is destroyed, nillified or garbage collected, then the Values and Keys will also be gone.
|
||||
-- @param #BASE self
|
||||
@@ -629,7 +811,6 @@ function BASE:SetState( Object, Key, Value )
|
||||
|
||||
self.States[ClassNameAndID] = self.States[ClassNameAndID] or {}
|
||||
self.States[ClassNameAndID][Key] = Value
|
||||
self:T2( { ClassNameAndID, Key, Value } )
|
||||
|
||||
return self.States[ClassNameAndID][Key]
|
||||
end
|
||||
@@ -640,7 +821,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 )
|
||||
|
||||
@@ -648,7 +828,6 @@ function BASE:GetState( Object, Key )
|
||||
|
||||
if self.States[ClassNameAndID] then
|
||||
local Value = self.States[ClassNameAndID][Key] or false
|
||||
self:T2( { ClassNameAndID, Key, Value } )
|
||||
return Value
|
||||
end
|
||||
|
||||
@@ -669,7 +848,7 @@ end
|
||||
-- TODO: Make trace function using variable parameters.
|
||||
|
||||
--- Set trace on or off
|
||||
-- Note that when trace is off, no debug statement is performed, increasing performance!
|
||||
-- Note that when trace is off, no BASE.Debug statement is performed, increasing performance!
|
||||
-- When Moose is loaded statically, (as one file), tracing is switched off by default.
|
||||
-- So tracing must be switched on manually in your mission if you are using Moose statically.
|
||||
-- When moose is loading dynamically (for moose class development), tracing is switched on by default.
|
||||
@@ -691,7 +870,7 @@ end
|
||||
-- @return #boolean
|
||||
function BASE:IsTrace()
|
||||
|
||||
if debug and ( _TraceAll == true ) or ( _TraceClass[self.ClassName] or _TraceClassMethod[self.ClassName] ) then
|
||||
if BASE.Debug and ( _TraceAll == true ) or ( _TraceClass[self.ClassName] or _TraceClassMethod[self.ClassName] ) then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
@@ -747,10 +926,10 @@ end
|
||||
-- @param Arguments A #table or any field.
|
||||
function BASE:_F( Arguments, DebugInfoCurrentParam, DebugInfoFromParam )
|
||||
|
||||
if debug and ( _TraceAll == true ) or ( _TraceClass[self.ClassName] or _TraceClassMethod[self.ClassName] ) then
|
||||
if BASE.Debug and ( _TraceAll == true ) or ( _TraceClass[self.ClassName] or _TraceClassMethod[self.ClassName] ) then
|
||||
|
||||
local DebugInfoCurrent = DebugInfoCurrentParam and DebugInfoCurrentParam or debug.getinfo( 2, "nl" )
|
||||
local DebugInfoFrom = DebugInfoFromParam and DebugInfoFromParam or debug.getinfo( 3, "l" )
|
||||
local DebugInfoCurrent = DebugInfoCurrentParam and DebugInfoCurrentParam or BASE.Debug.getinfo( 2, "nl" )
|
||||
local DebugInfoFrom = DebugInfoFromParam and DebugInfoFromParam or BASE.Debug.getinfo( 3, "l" )
|
||||
|
||||
local Function = "function"
|
||||
if DebugInfoCurrent.name then
|
||||
@@ -776,9 +955,9 @@ end
|
||||
-- @param Arguments A #table or any field.
|
||||
function BASE:F( Arguments )
|
||||
|
||||
if debug and _TraceOnOff then
|
||||
local DebugInfoCurrent = debug.getinfo( 2, "nl" )
|
||||
local DebugInfoFrom = debug.getinfo( 3, "l" )
|
||||
if BASE.Debug and _TraceOnOff then
|
||||
local DebugInfoCurrent = BASE.Debug.getinfo( 2, "nl" )
|
||||
local DebugInfoFrom = BASE.Debug.getinfo( 3, "l" )
|
||||
|
||||
if _TraceLevel >= 1 then
|
||||
self:_F( Arguments, DebugInfoCurrent, DebugInfoFrom )
|
||||
@@ -792,9 +971,9 @@ end
|
||||
-- @param Arguments A #table or any field.
|
||||
function BASE:F2( Arguments )
|
||||
|
||||
if debug and _TraceOnOff then
|
||||
local DebugInfoCurrent = debug.getinfo( 2, "nl" )
|
||||
local DebugInfoFrom = debug.getinfo( 3, "l" )
|
||||
if BASE.Debug and _TraceOnOff then
|
||||
local DebugInfoCurrent = BASE.Debug.getinfo( 2, "nl" )
|
||||
local DebugInfoFrom = BASE.Debug.getinfo( 3, "l" )
|
||||
|
||||
if _TraceLevel >= 2 then
|
||||
self:_F( Arguments, DebugInfoCurrent, DebugInfoFrom )
|
||||
@@ -807,9 +986,9 @@ end
|
||||
-- @param Arguments A #table or any field.
|
||||
function BASE:F3( Arguments )
|
||||
|
||||
if debug and _TraceOnOff then
|
||||
local DebugInfoCurrent = debug.getinfo( 2, "nl" )
|
||||
local DebugInfoFrom = debug.getinfo( 3, "l" )
|
||||
if BASE.Debug and _TraceOnOff then
|
||||
local DebugInfoCurrent = BASE.Debug.getinfo( 2, "nl" )
|
||||
local DebugInfoFrom = BASE.Debug.getinfo( 3, "l" )
|
||||
|
||||
if _TraceLevel >= 3 then
|
||||
self:_F( Arguments, DebugInfoCurrent, DebugInfoFrom )
|
||||
@@ -822,10 +1001,10 @@ end
|
||||
-- @param Arguments A #table or any field.
|
||||
function BASE:_T( Arguments, DebugInfoCurrentParam, DebugInfoFromParam )
|
||||
|
||||
if debug and ( _TraceAll == true ) or ( _TraceClass[self.ClassName] or _TraceClassMethod[self.ClassName] ) then
|
||||
if BASE.Debug and ( _TraceAll == true ) or ( _TraceClass[self.ClassName] or _TraceClassMethod[self.ClassName] ) then
|
||||
|
||||
local DebugInfoCurrent = DebugInfoCurrentParam and DebugInfoCurrentParam or debug.getinfo( 2, "nl" )
|
||||
local DebugInfoFrom = DebugInfoFromParam and DebugInfoFromParam or debug.getinfo( 3, "l" )
|
||||
local DebugInfoCurrent = DebugInfoCurrentParam and DebugInfoCurrentParam or BASE.Debug.getinfo( 2, "nl" )
|
||||
local DebugInfoFrom = DebugInfoFromParam and DebugInfoFromParam or BASE.Debug.getinfo( 3, "l" )
|
||||
|
||||
local Function = "function"
|
||||
if DebugInfoCurrent.name then
|
||||
@@ -851,9 +1030,9 @@ end
|
||||
-- @param Arguments A #table or any field.
|
||||
function BASE:T( Arguments )
|
||||
|
||||
if debug and _TraceOnOff then
|
||||
local DebugInfoCurrent = debug.getinfo( 2, "nl" )
|
||||
local DebugInfoFrom = debug.getinfo( 3, "l" )
|
||||
if BASE.Debug and _TraceOnOff then
|
||||
local DebugInfoCurrent = BASE.Debug.getinfo( 2, "nl" )
|
||||
local DebugInfoFrom = BASE.Debug.getinfo( 3, "l" )
|
||||
|
||||
if _TraceLevel >= 1 then
|
||||
self:_T( Arguments, DebugInfoCurrent, DebugInfoFrom )
|
||||
@@ -867,9 +1046,9 @@ end
|
||||
-- @param Arguments A #table or any field.
|
||||
function BASE:T2( Arguments )
|
||||
|
||||
if debug and _TraceOnOff then
|
||||
local DebugInfoCurrent = debug.getinfo( 2, "nl" )
|
||||
local DebugInfoFrom = debug.getinfo( 3, "l" )
|
||||
if BASE.Debug and _TraceOnOff then
|
||||
local DebugInfoCurrent = BASE.Debug.getinfo( 2, "nl" )
|
||||
local DebugInfoFrom = BASE.Debug.getinfo( 3, "l" )
|
||||
|
||||
if _TraceLevel >= 2 then
|
||||
self:_T( Arguments, DebugInfoCurrent, DebugInfoFrom )
|
||||
@@ -882,9 +1061,9 @@ end
|
||||
-- @param Arguments A #table or any field.
|
||||
function BASE:T3( Arguments )
|
||||
|
||||
if debug and _TraceOnOff then
|
||||
local DebugInfoCurrent = debug.getinfo( 2, "nl" )
|
||||
local DebugInfoFrom = debug.getinfo( 3, "l" )
|
||||
if BASE.Debug and _TraceOnOff then
|
||||
local DebugInfoCurrent = BASE.Debug.getinfo( 2, "nl" )
|
||||
local DebugInfoFrom = BASE.Debug.getinfo( 3, "l" )
|
||||
|
||||
if _TraceLevel >= 3 then
|
||||
self:_T( Arguments, DebugInfoCurrent, DebugInfoFrom )
|
||||
@@ -897,9 +1076,9 @@ end
|
||||
-- @param Arguments A #table or any field.
|
||||
function BASE:E( Arguments )
|
||||
|
||||
if debug then
|
||||
local DebugInfoCurrent = debug.getinfo( 2, "nl" )
|
||||
local DebugInfoFrom = debug.getinfo( 3, "l" )
|
||||
if BASE.Debug then
|
||||
local DebugInfoCurrent = BASE.Debug.getinfo( 2, "nl" )
|
||||
local DebugInfoFrom = BASE.Debug.getinfo( 3, "l" )
|
||||
|
||||
local Function = "function"
|
||||
if DebugInfoCurrent.name then
|
||||
@@ -913,9 +1092,71 @@ function BASE:E( Arguments )
|
||||
end
|
||||
|
||||
env.info( string.format( "%6d(%6d)/%1s:%20s%05d.%s(%s)" , LineCurrent, LineFrom, "E", self.ClassName, self.ClassID, Function, routines.utils.oneLineSerialize( Arguments ) ) )
|
||||
else
|
||||
env.info( string.format( "%1s:%20s%05d(%s)" , "E", self.ClassName, self.ClassID, routines.utils.oneLineSerialize( Arguments ) ) )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Log an information which will be traced always. Can be anywhere within the function logic.
|
||||
-- @param #BASE self
|
||||
-- @param Arguments A #table or any field.
|
||||
function BASE:I( Arguments )
|
||||
|
||||
if BASE.Debug then
|
||||
local DebugInfoCurrent = BASE.Debug.getinfo( 2, "nl" )
|
||||
local DebugInfoFrom = BASE.Debug.getinfo( 3, "l" )
|
||||
|
||||
local Function = "function"
|
||||
if DebugInfoCurrent.name then
|
||||
Function = DebugInfoCurrent.name
|
||||
end
|
||||
|
||||
local LineCurrent = DebugInfoCurrent.currentline
|
||||
local LineFrom = -1
|
||||
if DebugInfoFrom then
|
||||
LineFrom = DebugInfoFrom.currentline
|
||||
end
|
||||
|
||||
env.info( string.format( "%6d(%6d)/%1s:%20s%05d.%s(%s)" , LineCurrent, LineFrom, "I", self.ClassName, self.ClassID, Function, routines.utils.oneLineSerialize( Arguments ) ) )
|
||||
else
|
||||
env.info( string.format( "%1s:%20s%05d(%s)" , "I", self.ClassName, self.ClassID, routines.utils.oneLineSerialize( Arguments ) ) )
|
||||
end
|
||||
|
||||
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
@@ -1,9 +1,20 @@
|
||||
--- This module contains the DATABASE class, managing the database of mission objects.
|
||||
--- **Core** -- DATABASE manages the database of mission objects.
|
||||
--
|
||||
-- ====
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module Database
|
||||
|
||||
|
||||
--- @type DATABASE
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
--- # DATABASE class, extends @{Base#BASE}
|
||||
--
|
||||
-- 1) @{#DATABASE} class, extends @{Base#BASE}
|
||||
-- ===================================================
|
||||
-- Mission designers can use the DATABASE class to refer to:
|
||||
--
|
||||
-- * STATICS
|
||||
@@ -17,30 +28,10 @@
|
||||
--
|
||||
-- On top, for internal MOOSE administration purposes, the DATBASE administers the Unit and Group TEMPLATES as defined within the Mission Editor.
|
||||
--
|
||||
-- Moose will automatically create one instance of the DATABASE class into the **global** object _DATABASE.
|
||||
-- Moose refers to _DATABASE within the framework extensively, but you can also refer to the _DATABASE object within your missions if required.
|
||||
-- The singleton object **_DATABASE** is automatically created by MOOSE, that administers all objects within the mission.
|
||||
-- Moose refers to **_DATABASE** within the framework extensively, but you can also refer to the _DATABASE object within your missions if required.
|
||||
--
|
||||
-- 1.1) DATABASE iterators
|
||||
-- -----------------------
|
||||
-- You can iterate the database with the available iterator methods.
|
||||
-- The iterator methods will walk the DATABASE set, and call for each element within the set a function that you provide.
|
||||
-- The following iterator methods are currently available within the DATABASE:
|
||||
--
|
||||
-- * @{#DATABASE.ForEachUnit}: Calls a function for each @{UNIT} it finds within the DATABASE.
|
||||
-- * @{#DATABASE.ForEachGroup}: Calls a function for each @{GROUP} it finds within the DATABASE.
|
||||
-- * @{#DATABASE.ForEachPlayer}: Calls a function for each alive player it finds within the DATABASE.
|
||||
-- * @{#DATABASE.ForEachPlayerJoined}: Calls a function for each joined player it finds within the DATABASE.
|
||||
-- * @{#DATABASE.ForEachClient}: Calls a function for each @{CLIENT} it finds within the DATABASE.
|
||||
-- * @{#DATABASE.ForEachClientAlive}: Calls a function for each alive @{CLIENT} it finds within the DATABASE.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module Database
|
||||
-- @author FlightControl
|
||||
|
||||
--- DATABASE class
|
||||
-- @type DATABASE
|
||||
-- @extends Core.Base#BASE
|
||||
-- @field #DATABASE
|
||||
DATABASE = {
|
||||
ClassName = "DATABASE",
|
||||
Templates = {
|
||||
@@ -56,12 +47,18 @@ DATABASE = {
|
||||
GROUPS = {},
|
||||
PLAYERS = {},
|
||||
PLAYERSJOINED = {},
|
||||
PLAYERUNITS = {},
|
||||
CLIENTS = {},
|
||||
CARGOS = {},
|
||||
AIRBASES = {},
|
||||
COUNTRY_ID = {},
|
||||
COUNTRY_NAME = {},
|
||||
NavPoints = {},
|
||||
PLAYERSETTINGS = {},
|
||||
ZONENAMES = {},
|
||||
HITS = {},
|
||||
DESTROYS = {},
|
||||
ZONES = {},
|
||||
}
|
||||
|
||||
local _DATABASECoalition =
|
||||
@@ -96,46 +93,53 @@ function DATABASE:New()
|
||||
self:HandleEvent( EVENTS.Birth, self._EventOnBirth )
|
||||
self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash )
|
||||
self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash )
|
||||
self:HandleEvent( EVENTS.Hit, self.AccountHits )
|
||||
self:HandleEvent( EVENTS.NewCargo )
|
||||
self:HandleEvent( EVENTS.DeleteCargo )
|
||||
|
||||
-- Follow alive players and clients
|
||||
-- self:HandleEvent( EVENTS.PlayerEnterUnit, self._EventOnPlayerEnterUnit )
|
||||
--self:HandleEvent( EVENTS.PlayerEnterUnit, self._EventOnPlayerEnterUnit ) -- This is not working anymore!, handling this through the birth event.
|
||||
self:HandleEvent( EVENTS.PlayerLeaveUnit, self._EventOnPlayerLeaveUnit )
|
||||
|
||||
self:_RegisterTemplates()
|
||||
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 } )
|
||||
|
||||
local UNITS_Count = #self.UNITS_Index
|
||||
if UNITS_Count > 0 then
|
||||
self.UNITS_Position = ( ( self.UNITS_Position <= UNITS_Count ) and self.UNITS_Position + 1 ) or 1
|
||||
local PlayerUnit = self.UNITS[self.UNITS_Index[self.UNITS_Position]]
|
||||
if PlayerUnit then
|
||||
local UnitName = PlayerUnit:GetName()
|
||||
local PlayerName = PlayerUnit:GetPlayerName()
|
||||
--self:E( { UNITS_Count, self.UNITS_Position, UnitName, PlayerName } )
|
||||
if PlayerName and PlayerName ~= "" then
|
||||
if self.PLAYERS[PlayerName] == nil or self.PLAYERS[PlayerName] ~= UnitName then
|
||||
self:E( { "Add player for unit:", UnitName, PlayerName } )
|
||||
self:AddPlayer( UnitName, PlayerName )
|
||||
--_EVENTDISPATCHER:CreateEventPlayerEnterUnit( PlayerUnit )
|
||||
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" )
|
||||
--local PlayerCheckSchedule = SCHEDULER:New( nil, CheckPlayers, { self }, 2, 0.1 )
|
||||
--self:E( "Scheduling" )
|
||||
--PlayerCheckSchedule = SCHEDULER:New( nil, CheckPlayers, { self }, 1, 1 )
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -200,6 +204,16 @@ function DATABASE:FindStatic( StaticName )
|
||||
return StaticFound
|
||||
end
|
||||
|
||||
--- Finds a 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 Airbase based on the Airbase Name in the DATABASE.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string AirbaseName The name of the airbase
|
||||
@@ -229,35 +243,146 @@ function DATABASE:FindAirbase( AirbaseName )
|
||||
return AirbaseFound
|
||||
end
|
||||
|
||||
--- Adds a Cargo based on the Cargo Name in the DATABASE.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string CargoName The name of the airbase
|
||||
function DATABASE:AddCargo( Cargo )
|
||||
|
||||
if not self.CARGOS[Cargo.Name] then
|
||||
self.CARGOS[Cargo.Name] = Cargo
|
||||
do -- Zones
|
||||
|
||||
--- Finds a @{Zone} based on the zone name.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string ZoneName The name of the zone.
|
||||
-- @return Core.Zone#ZONE_BASE The found ZONE.
|
||||
function DATABASE:FindZone( ZoneName )
|
||||
|
||||
local ZoneFound = self.ZONES[ZoneName]
|
||||
return ZoneFound
|
||||
end
|
||||
|
||||
--- Adds a @{Zone} based on the zone name in the DATABASE.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string ZoneName The name of the zone.
|
||||
-- @param Core.Zone#ZONE_BASE Zone The zone.
|
||||
function DATABASE:AddZone( ZoneName, Zone )
|
||||
|
||||
if not self.ZONES[ZoneName] then
|
||||
self.ZONES[ZoneName] = Zone
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Deletes a @{Zone} from the DATABASE based on the zone name.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string ZoneName The name of the zone.
|
||||
function DATABASE:DeleteZone( ZoneName )
|
||||
|
||||
self.ZONES[ZoneName] = nil
|
||||
end
|
||||
|
||||
--- Finds an @{Zone} based on the zone name in the DATABASE.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string ZoneName
|
||||
-- @return Core.Zone#ZONE_BASE The found @{Zone}.
|
||||
function DATABASE:FindZone( ZoneName )
|
||||
|
||||
local ZoneFound = self.ZONES[ZoneName]
|
||||
return ZoneFound
|
||||
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 )
|
||||
do -- cargo
|
||||
|
||||
self.CARGOS[CargoName] = nil
|
||||
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
|
||||
|
||||
--- Checks if the Template name has a ~CARGO tag.
|
||||
-- If yes, the group is a cargo.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string TemplateName
|
||||
-- @return #boolean
|
||||
function DATABASE:IsCargo( TemplateName )
|
||||
|
||||
--- Finds an CARGO based on the CargoName.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string CargoName
|
||||
-- @return Wrapper.Cargo#CARGO The found CARGO.
|
||||
function DATABASE:FindCargo( CargoName )
|
||||
TemplateName = env.getValueDictByKey( TemplateName )
|
||||
|
||||
local Cargo = TemplateName:match( "~(CARGO)" )
|
||||
|
||||
local CargoFound = self.CARGOS[CargoName]
|
||||
return CargoFound
|
||||
end
|
||||
return Cargo and Cargo == "CARGO"
|
||||
end
|
||||
|
||||
--- Private method that registers new Static Templates within the DATABASE Object.
|
||||
-- @param #DATABASE self
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:RegisterCargos()
|
||||
|
||||
|
||||
for CargoGroupName, CargoGroup in pairs( self.GROUPS ) do
|
||||
if self:IsCargo( CargoGroupName ) then
|
||||
local CargoInfo = CargoGroupName:match("~CARGO(.*)")
|
||||
local CargoParam = CargoInfo and CargoInfo:match( "%((.*)%)")
|
||||
local CargoName = CargoGroupName:match("(.*)~CARGO")
|
||||
local Type = CargoParam and CargoParam:match( "T=([%a%d ]+),?")
|
||||
local Name = CargoParam and CargoParam:match( "N=([%a%d]+),?") or CargoName
|
||||
local LoadRadius = CargoParam and tonumber( CargoParam:match( "RR=([%a%d]+),?") )
|
||||
local NearRadius = CargoParam and tonumber( CargoParam:match( "NR=([%a%d]+),?") )
|
||||
|
||||
self:F({"Register CargoGroup:",Type=Type,Name=Name,LoadRadius=LoadRadius,NearRadius=NearRadius})
|
||||
CARGO_GROUP:New( CargoGroup, Type, Name, LoadRadius, NearRadius )
|
||||
end
|
||||
end
|
||||
|
||||
for CargoStaticName, CargoStatic in pairs( self.STATICS ) do
|
||||
if self:IsCargo( CargoStaticName ) then
|
||||
local CargoInfo = CargoStaticName:match("~CARGO(.*)")
|
||||
local CargoParam = CargoInfo and CargoInfo:match( "%((.*)%)")
|
||||
local CargoName = CargoStaticName:match("(.*)~CARGO")
|
||||
local Type = CargoParam and CargoParam:match( "T=([%a%d ]+),?")
|
||||
local Category = CargoParam and CargoParam:match( "C=([%a%d ]+),?")
|
||||
local Name = CargoParam and CargoParam:match( "N=([%a%d]+),?") or CargoName
|
||||
local LoadRadius = CargoParam and tonumber( CargoParam:match( "RR=([%a%d]+),?") )
|
||||
local NearRadius = CargoParam and tonumber( CargoParam:match( "NR=([%a%d]+),?") )
|
||||
|
||||
if Category == "SLING" then
|
||||
self:F({"Register CargoSlingload:",Type=Type,Name=Name,LoadRadius=LoadRadius,NearRadius=NearRadius})
|
||||
CARGO_SLINGLOAD:New( CargoStatic, Type, Name, LoadRadius, NearRadius )
|
||||
else
|
||||
if Category == "CRATE" then
|
||||
self:F({"Register CargoCrate:",Type=Type,Name=Name,LoadRadius=LoadRadius,NearRadius=NearRadius})
|
||||
CARGO_CRATE:New( CargoStatic, Type, Name, LoadRadius, NearRadius )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end -- cargo
|
||||
|
||||
--- Finds a CLIENT based on the ClientName.
|
||||
-- @param #DATABASE self
|
||||
@@ -312,20 +437,60 @@ function DATABASE:AddPlayer( UnitName, PlayerName )
|
||||
if PlayerName then
|
||||
self:E( { "Add player for unit:", UnitName, PlayerName } )
|
||||
self.PLAYERS[PlayerName] = UnitName
|
||||
self.PLAYERUNITS[PlayerName] = self:FindUnit( UnitName )
|
||||
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[PlayerName] = nil
|
||||
end
|
||||
end
|
||||
|
||||
--- Get the player table from the DATABASE.
|
||||
-- The player table contains all unit names with the key the name of the player (PlayerName).
|
||||
-- @param #DATABASE self
|
||||
-- @usage
|
||||
-- local Players = _DATABASE:GetPlayers()
|
||||
-- for PlayerName, UnitName in pairs( Players ) do
|
||||
-- ..
|
||||
-- end
|
||||
function DATABASE:GetPlayers()
|
||||
return self.PLAYERS
|
||||
end
|
||||
|
||||
|
||||
--- Get the player table from the DATABASE, which contains all UNIT objects.
|
||||
-- The player table contains all UNIT objects of the player with the key the name of the player (PlayerName).
|
||||
-- @param #DATABASE self
|
||||
-- @usage
|
||||
-- local PlayerUnits = _DATABASE:GetPlayerUnits()
|
||||
-- for PlayerName, PlayerUnit in pairs( PlayerUnits ) do
|
||||
-- ..
|
||||
-- end
|
||||
function DATABASE:GetPlayerUnits()
|
||||
return self.PLAYERUNITS
|
||||
end
|
||||
|
||||
|
||||
--- Get the player table from the DATABASE which have joined in the mission historically.
|
||||
-- The player table contains all UNIT objects with the key the name of the player (PlayerName).
|
||||
-- @param #DATABASE self
|
||||
-- @usage
|
||||
-- local PlayersJoined = _DATABASE:GetPlayersJoined()
|
||||
-- for PlayerName, PlayerUnit in pairs( PlayersJoined ) do
|
||||
-- ..
|
||||
-- end
|
||||
function DATABASE:GetPlayersJoined()
|
||||
return self.PLAYERSJOINED
|
||||
end
|
||||
|
||||
|
||||
--- Instantiate new Groups within the DCSRTE.
|
||||
-- This method expects EXACTLY the same structure as a structure within the ME, and needs 2 additional fields defined:
|
||||
@@ -359,7 +524,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
|
||||
|
||||
@@ -384,13 +554,14 @@ end
|
||||
--- Private method that registers new Group Templates within the DATABASE Object.
|
||||
-- @param #DATABASE self
|
||||
-- @param #table GroupTemplate
|
||||
-- @param Dcs.DCScoalition#coalition.side CoalitionSide The coalition.side of the object.
|
||||
-- @param Dcs.DCSObject#Object.Category CategoryID The Object.category of the object.
|
||||
-- @param Dcs.DCScountry#country.id CountryID the country.id of the object
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionID, CategoryID, CountryID )
|
||||
function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, CategoryID, CountryID, GroupName )
|
||||
|
||||
local GroupTemplateName = env.getValueDictByKey(GroupTemplate.name)
|
||||
local GroupTemplateName = GroupName or env.getValueDictByKey( GroupTemplate.name )
|
||||
|
||||
local TraceTable = {}
|
||||
|
||||
if not self.Templates.Groups[GroupTemplateName] then
|
||||
self.Templates.Groups[GroupTemplateName] = {}
|
||||
self.Templates.Groups[GroupTemplateName].Status = nil
|
||||
@@ -402,7 +573,7 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionID, CategoryID
|
||||
end
|
||||
|
||||
GroupTemplate.CategoryID = CategoryID
|
||||
GroupTemplate.CoalitionID = CoalitionID
|
||||
GroupTemplate.CoalitionID = CoalitionSide
|
||||
GroupTemplate.CountryID = CountryID
|
||||
|
||||
self.Templates.Groups[GroupTemplateName].GroupName = GroupTemplateName
|
||||
@@ -411,21 +582,10 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionID, CategoryID
|
||||
self.Templates.Groups[GroupTemplateName].UnitCount = #GroupTemplate.units
|
||||
self.Templates.Groups[GroupTemplateName].Units = GroupTemplate.units
|
||||
self.Templates.Groups[GroupTemplateName].CategoryID = CategoryID
|
||||
self.Templates.Groups[GroupTemplateName].CoalitionID = CoalitionID
|
||||
self.Templates.Groups[GroupTemplateName].CoalitionID = CoalitionSide
|
||||
self.Templates.Groups[GroupTemplateName].CountryID = CountryID
|
||||
|
||||
|
||||
TraceTable[#TraceTable+1] = "Group"
|
||||
TraceTable[#TraceTable+1] = self.Templates.Groups[GroupTemplateName].GroupName
|
||||
|
||||
TraceTable[#TraceTable+1] = "Coalition"
|
||||
TraceTable[#TraceTable+1] = self.Templates.Groups[GroupTemplateName].CoalitionID
|
||||
TraceTable[#TraceTable+1] = "Category"
|
||||
TraceTable[#TraceTable+1] = self.Templates.Groups[GroupTemplateName].CategoryID
|
||||
TraceTable[#TraceTable+1] = "Country"
|
||||
TraceTable[#TraceTable+1] = self.Templates.Groups[GroupTemplateName].CountryID
|
||||
|
||||
TraceTable[#TraceTable+1] = "Units"
|
||||
local UnitNames = {}
|
||||
|
||||
for unit_num, UnitTemplate in pairs( GroupTemplate.units ) do
|
||||
|
||||
@@ -438,21 +598,27 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionID, CategoryID
|
||||
self.Templates.Units[UnitTemplate.name].GroupTemplate = GroupTemplate
|
||||
self.Templates.Units[UnitTemplate.name].GroupId = GroupTemplate.groupId
|
||||
self.Templates.Units[UnitTemplate.name].CategoryID = CategoryID
|
||||
self.Templates.Units[UnitTemplate.name].CoalitionID = CoalitionID
|
||||
self.Templates.Units[UnitTemplate.name].CoalitionID = CoalitionSide
|
||||
self.Templates.Units[UnitTemplate.name].CountryID = CountryID
|
||||
|
||||
if UnitTemplate.skill and (UnitTemplate.skill == "Client" or UnitTemplate.skill == "Player") then
|
||||
self.Templates.ClientsByName[UnitTemplate.name] = UnitTemplate
|
||||
self.Templates.ClientsByName[UnitTemplate.name].CategoryID = CategoryID
|
||||
self.Templates.ClientsByName[UnitTemplate.name].CoalitionID = CoalitionID
|
||||
self.Templates.ClientsByName[UnitTemplate.name].CoalitionID = CoalitionSide
|
||||
self.Templates.ClientsByName[UnitTemplate.name].CountryID = CountryID
|
||||
self.Templates.ClientsByID[UnitTemplate.unitId] = UnitTemplate
|
||||
end
|
||||
|
||||
TraceTable[#TraceTable+1] = self.Templates.Units[UnitTemplate.name].UnitName
|
||||
UnitNames[#UnitNames+1] = self.Templates.Units[UnitTemplate.name].UnitName
|
||||
end
|
||||
|
||||
self:E( TraceTable )
|
||||
self:I( { Group = self.Templates.Groups[GroupTemplateName].GroupName,
|
||||
Coalition = self.Templates.Groups[GroupTemplateName].CoalitionID,
|
||||
Category = self.Templates.Groups[GroupTemplateName].CategoryID,
|
||||
Country = self.Templates.Groups[GroupTemplateName].CountryID,
|
||||
Units = UnitNames
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
function DATABASE:GetGroupTemplate( GroupName )
|
||||
@@ -469,8 +635,6 @@ end
|
||||
-- @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 {}
|
||||
@@ -486,28 +650,22 @@ function DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, Category
|
||||
self.Templates.Statics[StaticTemplateName].CoalitionID = CoalitionID
|
||||
self.Templates.Statics[StaticTemplateName].CountryID = CountryID
|
||||
|
||||
self:I( { Static = self.Templates.Statics[StaticTemplateName].StaticName,
|
||||
Coalition = self.Templates.Statics[StaticTemplateName].CoalitionID,
|
||||
Category = self.Templates.Statics[StaticTemplateName].CategoryID,
|
||||
Country = self.Templates.Statics[StaticTemplateName].CountryID
|
||||
}
|
||||
)
|
||||
|
||||
self:AddStatic( StaticTemplateName )
|
||||
|
||||
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
|
||||
return StaticTemplate, self.Templates.Statics[StaticName].CoalitionID, self.Templates.Statics[StaticName].CategoryID, self.Templates.Statics[StaticName].CountryID
|
||||
end
|
||||
|
||||
|
||||
@@ -615,6 +773,7 @@ end
|
||||
function DATABASE:_RegisterStatics()
|
||||
|
||||
local CoalitionsData = { GroupsRed = coalition.getStaticObjects( coalition.side.RED ), GroupsBlue = coalition.getStaticObjects( coalition.side.BLUE ) }
|
||||
self:E( { Statics = CoalitionsData } )
|
||||
for CoalitionId, CoalitionData in pairs( CoalitionsData ) do
|
||||
for DCSStaticId, DCSStatic in pairs( CoalitionData ) do
|
||||
|
||||
@@ -641,7 +800,7 @@ function DATABASE:_RegisterAirbases()
|
||||
|
||||
local DCSAirbaseName = DCSAirbase:getName()
|
||||
|
||||
self:E( { "Register Airbase:", DCSAirbaseName } )
|
||||
self:E( { "Register Airbase:", DCSAirbaseName, DCSAirbase:getID() } )
|
||||
self:AddAirbase( DCSAirbaseName )
|
||||
end
|
||||
end
|
||||
@@ -667,7 +826,21 @@ function DATABASE:_EventOnBirth( Event )
|
||||
self:AddGroup( Event.IniDCSGroupName )
|
||||
end
|
||||
end
|
||||
self:_EventOnPlayerEnterUnit( Event )
|
||||
if Event.IniObjectCategory == 1 then
|
||||
Event.IniUnit = self:FindUnit( Event.IniDCSUnitName )
|
||||
Event.IniGroup = self:FindGroup( Event.IniDCSGroupName )
|
||||
local PlayerName = Event.IniUnit:GetPlayerName()
|
||||
self:E( { "PlayerName:", PlayerName } )
|
||||
if PlayerName then
|
||||
self:E( { "Player Joined:", PlayerName } )
|
||||
if not self.PLAYERS[PlayerName] then
|
||||
self:AddPlayer( Event.IniUnitName, PlayerName )
|
||||
end
|
||||
local Settings = SETTINGS:Set( PlayerName )
|
||||
Settings:SetPlayerMenu( Event.IniUnit )
|
||||
--MENU_INDEX:Refresh( Event.IniGroup )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -691,6 +864,8 @@ function DATABASE:_EventOnDeadOrCrash( Event )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self:AccountDestroys( Event )
|
||||
end
|
||||
|
||||
|
||||
@@ -700,14 +875,17 @@ end
|
||||
function DATABASE:_EventOnPlayerEnterUnit( Event )
|
||||
self:F2( { Event } )
|
||||
|
||||
if Event.IniUnit then
|
||||
if Event.IniDCSUnit then
|
||||
if Event.IniObjectCategory == 1 then
|
||||
self:AddUnit( Event.IniDCSUnitName )
|
||||
Event.IniUnit = self:FindUnit( Event.IniDCSUnitName )
|
||||
self:AddGroup( Event.IniDCSGroupName )
|
||||
local PlayerName = Event.IniUnit:GetPlayerName()
|
||||
local PlayerName = Event.IniDCSUnit:getPlayerName()
|
||||
if not self.PLAYERS[PlayerName] then
|
||||
self:AddPlayer( Event.IniUnitName, PlayerName )
|
||||
self:AddPlayer( Event.IniDCSUnitName, PlayerName )
|
||||
end
|
||||
local Settings = SETTINGS:Set( PlayerName )
|
||||
Settings:SetPlayerMenu( Event.IniUnit )
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -722,8 +900,11 @@ function DATABASE:_EventOnPlayerLeaveUnit( Event )
|
||||
if Event.IniUnit then
|
||||
if Event.IniObjectCategory == 1 then
|
||||
local PlayerName = Event.IniUnit:GetPlayerName()
|
||||
if self.PLAYERS[PlayerName] then
|
||||
self:DeletePlayer( PlayerName )
|
||||
if PlayerName and self.PLAYERS[PlayerName] then
|
||||
self:E( { "Player Left:", PlayerName } )
|
||||
local Settings = SETTINGS:Set( PlayerName )
|
||||
Settings:RemovePlayerMenu( Event.IniUnit )
|
||||
self:DeletePlayer( Event.IniUnit, PlayerName )
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -808,10 +989,10 @@ end
|
||||
-- @param #DATABASE self
|
||||
-- @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
|
||||
@@ -821,10 +1002,10 @@ end
|
||||
-- @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: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
|
||||
@@ -834,14 +1015,27 @@ end
|
||||
-- @param #DATABASE self
|
||||
-- @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 object in the database. The function needs to accept a CLIENT parameter.
|
||||
@@ -891,6 +1085,29 @@ function DATABASE:OnEventDeleteCargo( EventData )
|
||||
end
|
||||
|
||||
|
||||
--- Gets the player settings
|
||||
-- @param #DATABASE self
|
||||
-- @param #string PlayerName
|
||||
-- @return Core.Settings#SETTINGS
|
||||
function DATABASE:GetPlayerSettings( PlayerName )
|
||||
self:F2( { 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:F2( { PlayerName, Settings } )
|
||||
self.PLAYERSETTINGS[PlayerName] = Settings
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
--- @param #DATABASE self
|
||||
function DATABASE:_RegisterTemplates()
|
||||
self:F2()
|
||||
@@ -905,7 +1122,6 @@ function DATABASE:_RegisterTemplates()
|
||||
|
||||
local CoalitionSide = coalition.side[string.upper(CoalitionName)]
|
||||
|
||||
----------------------------------------------
|
||||
-- build nav points DB
|
||||
self.Navpoints[CoalitionName] = {}
|
||||
if coa_data.nav_points then --navpoints
|
||||
@@ -920,8 +1136,9 @@ function DATABASE:_RegisterTemplates()
|
||||
self.Navpoints[CoalitionName][nav_ind]['point']['y'] = 0
|
||||
self.Navpoints[CoalitionName][nav_ind]['point']['z'] = nav_data.y
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-------------------------------------------------
|
||||
if coa_data.country then --there is a country table
|
||||
for cntry_id, cntry_data in pairs(coa_data.country) do
|
||||
@@ -974,9 +1191,111 @@ 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
|
||||
self:AddZone( ZoneName, ZONE:New( ZoneName ) )
|
||||
self:I( "Added ZONE " .. ZoneName )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Account the Hits of the Players.
|
||||
-- @param #DATABASE self
|
||||
-- @param Core.Event#EVENTDATA Event
|
||||
function DATABASE:AccountHits( Event )
|
||||
self:F( { Event } )
|
||||
|
||||
if Event.IniPlayerName ~= nil then -- It is a player that is hitting something
|
||||
self:T( "Hitting Something" )
|
||||
|
||||
-- What is he hitting?
|
||||
if Event.TgtCategory then
|
||||
|
||||
-- A target got hit
|
||||
self.HITS[Event.TgtUnitName] = self.HITS[Event.TgtUnitName] or {}
|
||||
local Hit = self.HITS[Event.TgtUnitName]
|
||||
|
||||
Hit.Players = Hit.Players or {}
|
||||
Hit.Players[Event.IniPlayerName] = true
|
||||
end
|
||||
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:T( "Hitting Scenery" )
|
||||
|
||||
-- What is he hitting?
|
||||
if Event.TgtCategory then
|
||||
|
||||
if Event.IniCoalition then -- A coalition object was hit, probably a static.
|
||||
-- A target got hit
|
||||
self.HITS[Event.TgtUnitName] = self.HITS[Event.TgtUnitName] or {}
|
||||
local Hit = self.HITS[Event.TgtUnitName]
|
||||
|
||||
Hit.Players = Hit.Players or {}
|
||||
Hit.Players[Event.WeaponPlayerName] = true
|
||||
else -- A scenery object was hit.
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Account the destroys.
|
||||
-- @param #DATABASE self
|
||||
-- @param Core.Event#EVENTDATA Event
|
||||
function DATABASE:AccountDestroys( Event )
|
||||
self:F( { Event } )
|
||||
|
||||
local TargetUnit = nil
|
||||
local TargetGroup = nil
|
||||
local TargetUnitName = ""
|
||||
local TargetGroupName = ""
|
||||
local TargetPlayerName = ""
|
||||
local TargetCoalition = nil
|
||||
local TargetCategory = nil
|
||||
local TargetType = nil
|
||||
local TargetUnitCoalition = nil
|
||||
local TargetUnitCategory = nil
|
||||
local TargetUnitType = nil
|
||||
|
||||
if Event.IniDCSUnit then
|
||||
|
||||
TargetUnit = Event.IniUnit
|
||||
TargetUnitName = Event.IniDCSUnitName
|
||||
TargetGroup = Event.IniDCSGroup
|
||||
TargetGroupName = Event.IniDCSGroupName
|
||||
TargetPlayerName = Event.IniPlayerName
|
||||
|
||||
TargetCoalition = Event.IniCoalition
|
||||
--TargetCategory = TargetUnit:getCategory()
|
||||
--TargetCategory = TargetUnit:getDesc().category -- Workaround
|
||||
TargetCategory = Event.IniCategory
|
||||
TargetType = Event.IniTypeName
|
||||
|
||||
TargetUnitType = TargetType
|
||||
|
||||
self:T( { TargetUnitName, TargetGroupName, TargetPlayerName, TargetCoalition, TargetCategory, TargetType } )
|
||||
end
|
||||
|
||||
self:T( "Something got destroyed" )
|
||||
|
||||
local Destroyed = false
|
||||
|
||||
-- What is the player destroying?
|
||||
if self.HITS[Event.IniUnitName] then -- Was there a hit for this unit for this player before registered???
|
||||
|
||||
|
||||
self.DESTROYS[Event.IniUnitName] = self.DESTROYS[Event.IniUnitName] or {}
|
||||
|
||||
self.DESTROYS[Event.IniUnitName] = true
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
--- **Core R2.1** - EVENT models DCS **event dispatching** using a **publish-subscribe** model.
|
||||
--- **Core** -- EVENT models DCS **event dispatching** using a **publish-subscribe** model.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
@@ -157,33 +157,12 @@
|
||||
--
|
||||
-- When a static object is involved in the event, the Group and Player fields won't be populated.
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- # **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:
|
||||
--
|
||||
-- * 2017-03-07: Added the correct event dispatching in case the event is subscribed by a GROUP.
|
||||
--
|
||||
-- * 2017-02-07: Did a complete revision of the Event Handing API and underlying mechanisms.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # **AUTHORS and CONTRIBUTIONS**
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ### Authors:
|
||||
--
|
||||
-- * [**FlightControl**](https://forums.eagle.ru/member.php?u=89536): Design & Programming & documentation.
|
||||
-- ===
|
||||
--
|
||||
-- @module Event
|
||||
|
||||
@@ -195,6 +174,7 @@
|
||||
EVENT = {
|
||||
ClassName = "EVENT",
|
||||
ClassID = 0,
|
||||
MissionEnd = false,
|
||||
}
|
||||
|
||||
world.event.S_EVENT_NEW_CARGO = world.event.S_EVENT_MAX + 1000
|
||||
@@ -471,16 +451,16 @@ end
|
||||
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is.
|
||||
-- @param Dcs.DCSWorld#world.event EventID
|
||||
-- @return #EVENT.Events
|
||||
function EVENT:Remove( EventClass, EventID )
|
||||
function EVENT:RemoveEvent( EventClass, EventID )
|
||||
|
||||
self:E( { "Removing subscription for class: ", EventClass:GetClassNameAndID() } )
|
||||
self:F2( { "Removing subscription for class: ", EventClass:GetClassNameAndID() } )
|
||||
|
||||
local EventPriority = EventClass:GetEventPriority()
|
||||
|
||||
self.EventsDead = self.EventsDead or {}
|
||||
self.EventsDead[EventID] = self.EventsDead[EventID] or {}
|
||||
self.EventsDead[EventID][EventPriority] = self.EventsDead[EventID][EventPriority] or {}
|
||||
self.EventsDead[EventID][EventPriority][EventClass] = self.Events[EventID][EventPriority][EventClass]
|
||||
self.Events = self.Events or {}
|
||||
self.Events[EventID] = self.Events[EventID] or {}
|
||||
self.Events[EventID][EventPriority] = self.Events[EventID][EventPriority] or {}
|
||||
self.Events[EventID][EventPriority][EventClass] = self.Events[EventID][EventPriority][EventClass]
|
||||
|
||||
self.Events[EventID][EventPriority][EventClass] = nil
|
||||
|
||||
@@ -493,7 +473,7 @@ end
|
||||
-- @return #EVENT.Events
|
||||
function EVENT:Reset( EventObject ) --R2.1
|
||||
|
||||
self:E( { "Resetting subscriptions for class: ", EventObject:GetClassNameAndID() } )
|
||||
self:F( { "Resetting subscriptions for class: ", EventObject:GetClassNameAndID() } )
|
||||
|
||||
local EventPriority = EventObject:GetEventPriority()
|
||||
for EventID, EventData in pairs( self.Events ) do
|
||||
@@ -582,12 +562,12 @@ end
|
||||
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is.
|
||||
-- @param EventID
|
||||
-- @return #EVENT
|
||||
function EVENT:OnEventForGroup( GroupName, EventFunction, EventClass, EventID )
|
||||
self:F2( GroupName )
|
||||
function EVENT:OnEventForGroup( GroupName, EventFunction, EventClass, EventID, ... )
|
||||
|
||||
local Event = self:Init( EventID, EventClass )
|
||||
Event.EventGroup = true
|
||||
Event.EventFunction = EventFunction
|
||||
Event.Params = arg
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -754,7 +734,7 @@ function EVENT:onEvent( Event )
|
||||
local ErrorHandler = function( errmsg )
|
||||
|
||||
env.info( "Error in SCHEDULER function:" .. errmsg )
|
||||
if debug ~= nil then
|
||||
if BASE.Debug ~= nil then
|
||||
env.info( debug.traceback() )
|
||||
end
|
||||
|
||||
@@ -764,10 +744,20 @@ function EVENT:onEvent( Event )
|
||||
|
||||
local EventMeta = _EVENTMETA[Event.id]
|
||||
|
||||
if self and self.Events and self.Events[Event.id] then
|
||||
--self:E( { EventMeta.Text, Event } ) -- Activate the see all incoming events ...
|
||||
|
||||
if self and
|
||||
self.Events and
|
||||
self.Events[Event.id] and
|
||||
self.MissionEnd == false and
|
||||
( Event.initiator ~= nil or ( Event.initiator == nil and Event.id ~= EVENTS.PlayerLeaveUnit ) ) then
|
||||
|
||||
if Event.id and Event.id == EVENTS.MissionEnd then
|
||||
self.MissionEnd = true
|
||||
end
|
||||
|
||||
if Event.initiator then
|
||||
|
||||
|
||||
Event.IniObjectCategory = Event.initiator:getCategory()
|
||||
|
||||
if Event.IniObjectCategory == Object.Category.UNIT then
|
||||
@@ -804,13 +794,23 @@ function EVENT:onEvent( Event )
|
||||
Event.IniTypeName = Event.IniDCSUnit:getTypeName()
|
||||
end
|
||||
|
||||
if Event.IniObjectCategory == Object.Category.CARGO then
|
||||
Event.IniDCSUnit = Event.initiator
|
||||
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
||||
Event.IniUnitName = Event.IniDCSUnitName
|
||||
Event.IniUnit = CARGO:FindByName( Event.IniDCSUnitName )
|
||||
Event.IniCoalition = Event.IniDCSUnit:getCoalition()
|
||||
Event.IniCategory = Event.IniDCSUnit:getDesc().category
|
||||
Event.IniTypeName = Event.IniDCSUnit:getTypeName()
|
||||
end
|
||||
|
||||
if Event.IniObjectCategory == Object.Category.SCENERY then
|
||||
Event.IniDCSUnit = Event.initiator
|
||||
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
||||
Event.IniUnitName = Event.IniDCSUnitName
|
||||
Event.IniUnit = SCENERY:Register( Event.IniDCSUnitName, Event.initiator )
|
||||
Event.IniCategory = Event.IniDCSUnit:getDesc().category
|
||||
Event.IniTypeName = Event.IniDCSUnit:getTypeName()
|
||||
Event.IniTypeName = Event.initiator:isExist() and Event.IniDCSUnit:getTypeName() or "SCENERY" -- TODO: Bug fix for 2.1!
|
||||
end
|
||||
end
|
||||
|
||||
@@ -842,7 +842,7 @@ function EVENT:onEvent( Event )
|
||||
Event.TgtDCSUnit = Event.target
|
||||
Event.TgtDCSUnitName = Event.TgtDCSUnit:getName()
|
||||
Event.TgtUnitName = Event.TgtDCSUnitName
|
||||
Event.TgtUnit = STATIC:FindByName( Event.TgtDCSUnitName )
|
||||
Event.TgtUnit = STATIC:FindByName( Event.TgtDCSUnitName, false )
|
||||
Event.TgtCoalition = Event.TgtDCSUnit:getCoalition()
|
||||
Event.TgtCategory = Event.TgtDCSUnit:getDesc().category
|
||||
Event.TgtTypeName = Event.TgtDCSUnit:getTypeName()
|
||||
@@ -888,10 +888,10 @@ function EVENT:onEvent( Event )
|
||||
|
||||
-- Okay, we got the event from DCS. Now loop the SORTED self.EventSorted[] table for the received Event.id, and for each EventData registered, check if a function needs to be called.
|
||||
for EventClass, EventData in pairs( self.Events[Event.id][EventPriority] ) do
|
||||
|
||||
if Event.IniObjectCategory ~= Object.Category.STATIC then
|
||||
--self:E( { "Evaluating: ", EventClass:GetClassNameAndID() } )
|
||||
end
|
||||
|
||||
--if Event.IniObjectCategory ~= Object.Category.STATIC then
|
||||
-- self:E( { "Evaluating: ", EventClass:GetClassNameAndID() } )
|
||||
--end
|
||||
|
||||
Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName )
|
||||
Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName )
|
||||
@@ -901,6 +901,7 @@ function EVENT:onEvent( Event )
|
||||
|
||||
-- So now the EventClass must be a UNIT class!!! We check if it is still "Alive".
|
||||
if EventClass:IsAlive() or
|
||||
Event.id == EVENTS.PlayerEnterUnit or
|
||||
Event.id == EVENTS.Crash or
|
||||
Event.id == EVENTS.Dead then
|
||||
|
||||
@@ -913,7 +914,7 @@ function EVENT:onEvent( Event )
|
||||
if EventData.EventFunction then
|
||||
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:E( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } )
|
||||
self:F( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } )
|
||||
end
|
||||
|
||||
local Result, Value = xpcall(
|
||||
@@ -929,7 +930,7 @@ function EVENT:onEvent( Event )
|
||||
|
||||
-- Now call the default event function.
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:E( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
self:F( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
end
|
||||
|
||||
local Result, Value = xpcall(
|
||||
@@ -941,7 +942,7 @@ function EVENT:onEvent( Event )
|
||||
end
|
||||
else
|
||||
-- The EventClass is not alive anymore, we remove it from the EventHandlers...
|
||||
self:Remove( EventClass, Event.id )
|
||||
self:RemoveEvent( EventClass, Event.id )
|
||||
end
|
||||
else
|
||||
|
||||
@@ -950,6 +951,7 @@ function EVENT:onEvent( Event )
|
||||
|
||||
-- So now the EventClass must be a GROUP class!!! We check if it is still "Alive".
|
||||
if EventClass:IsAlive() or
|
||||
Event.id == EVENTS.PlayerEnterUnit or
|
||||
Event.id == EVENTS.Crash or
|
||||
Event.id == EVENTS.Dead then
|
||||
|
||||
@@ -963,12 +965,12 @@ function EVENT:onEvent( Event )
|
||||
if EventData.EventFunction then
|
||||
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:E( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } )
|
||||
self:F( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } )
|
||||
end
|
||||
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
return EventData.EventFunction( EventClass, Event )
|
||||
return EventData.EventFunction( EventClass, Event, unpack( EventData.Params ) )
|
||||
end, ErrorHandler )
|
||||
|
||||
else
|
||||
@@ -979,19 +981,19 @@ function EVENT:onEvent( Event )
|
||||
|
||||
-- Now call the default event function.
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:E( { "Calling " .. EventMeta.Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
self:F( { "Calling " .. EventMeta.Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
end
|
||||
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
return EventFunction( EventClass, Event )
|
||||
return EventFunction( EventClass, Event, unpack( EventData.Params ) )
|
||||
end, ErrorHandler )
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
-- The EventClass is not alive anymore, we remove it from the EventHandlers...
|
||||
self:Remove( EventClass, Event.id )
|
||||
--self:RemoveEvent( EventClass, Event.id )
|
||||
end
|
||||
else
|
||||
|
||||
@@ -1004,7 +1006,7 @@ function EVENT:onEvent( Event )
|
||||
|
||||
-- There is an EventFunction defined, so call the EventFunction.
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:E( { "Calling EventFunction for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
self:F2( { "Calling EventFunction for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
end
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
@@ -1018,7 +1020,7 @@ function EVENT:onEvent( Event )
|
||||
|
||||
-- Now call the default event function.
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:E( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
self:F2( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
end
|
||||
|
||||
local Result, Value = xpcall(
|
||||
@@ -1035,8 +1037,18 @@ function EVENT:onEvent( Event )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- When cargo was deleted, it may probably be because of an S_EVENT_DEAD.
|
||||
-- However, in the loading logic, an S_EVENT_DEAD is also generated after a Destroy() call.
|
||||
-- And this is a problem because it will remove all entries from the SET_CARGOs.
|
||||
-- To prevent this from happening, the Cargo object has a flag NoDestroy.
|
||||
-- When true, the SET_CARGO won't Remove the Cargo object from the set.
|
||||
-- But we need to switch that flag off after the event handlers have been called.
|
||||
if Event.id == EVENTS.DeleteCargo then
|
||||
Event.Cargo.NoDestroy = nil
|
||||
end
|
||||
else
|
||||
self:E( { EventMeta.Text, Event } )
|
||||
self:T( { EventMeta.Text, Event } )
|
||||
end
|
||||
|
||||
Event = nil
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
--- **Core** - The **FSM** (**F**inite **S**tate **M**achine) class and derived **FSM\_** classes
|
||||
--- **Core** -- The **FSM** (**F**inite **S**tate **M**achine) class and derived **FSM\_** classes
|
||||
-- are design patterns allowing efficient (long-lasting) processes and workflows.
|
||||
--
|
||||
-- 
|
||||
@@ -56,33 +56,13 @@
|
||||
-- * @{#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.
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- # **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: **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
|
||||
|
||||
@@ -357,7 +337,7 @@ do -- FSM
|
||||
--- Creates a new FSM object.
|
||||
-- @param #FSM self
|
||||
-- @return #FSM
|
||||
function FSM:New( FsmT )
|
||||
function FSM:New()
|
||||
|
||||
-- Inherits from BASE
|
||||
self = BASE:Inherit( self, BASE:New() )
|
||||
@@ -416,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 )
|
||||
@@ -438,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
|
||||
@@ -461,6 +441,8 @@ do -- FSM
|
||||
-- @return #table
|
||||
function FSM:GetProcesses()
|
||||
|
||||
self:F( { Processes = self._Processes } )
|
||||
|
||||
return self._Processes or {}
|
||||
end
|
||||
|
||||
@@ -475,6 +457,18 @@ do -- FSM
|
||||
error( "Sub-Process from state " .. From .. " with event " .. Event .. " not found!" )
|
||||
end
|
||||
|
||||
function FSM:SetProcess( From, Event, Fsm )
|
||||
|
||||
for ProcessID, Process in pairs( self:GetProcesses() ) do
|
||||
if Process.From == From and Process.Event == Event then
|
||||
Process.fsm = Fsm
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
error( "Sub-Process from state " .. From .. " with event " .. Event .. " not found!" )
|
||||
end
|
||||
|
||||
--- Adds an End state.
|
||||
function FSM:AddEndState( State )
|
||||
|
||||
@@ -554,14 +548,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 {}
|
||||
|
||||
@@ -577,95 +571,130 @@ do -- FSM
|
||||
end
|
||||
|
||||
|
||||
function FSM:_call_handler( handler, params, EventName )
|
||||
function FSM:_call_handler( step, trigger, params, EventName )
|
||||
|
||||
local handler = step .. trigger
|
||||
local ErrorHandler = function( errmsg )
|
||||
|
||||
env.info( "Error in SCHEDULER function:" .. errmsg )
|
||||
if debug ~= nil then
|
||||
env.info( debug.traceback() )
|
||||
if BASE.Debug ~= nil then
|
||||
env.info( BASE.Debug.traceback() )
|
||||
end
|
||||
|
||||
return errmsg
|
||||
end
|
||||
if self[handler] then
|
||||
self:T( "Calling " .. handler )
|
||||
self:T( "*** FSM *** " .. step .. " *** " .. params[1] .. " --> " .. params[2] .. " --> " .. params[3] )
|
||||
self._EventSchedules[EventName] = nil
|
||||
local Result, Value = xpcall( function() return self[handler]( self, unpack( params ) ) end, ErrorHandler )
|
||||
return Value
|
||||
end
|
||||
end
|
||||
|
||||
--- @param #FSM self
|
||||
function FSM._handler( self, EventName, ... )
|
||||
|
||||
local Can, to = self:can( EventName )
|
||||
local Can, To = self:can( EventName )
|
||||
|
||||
if to == "*" then
|
||||
to = self.current
|
||||
if To == "*" then
|
||||
To = self.current
|
||||
end
|
||||
|
||||
if Can then
|
||||
local from = self.current
|
||||
local params = { from, EventName, to, ... }
|
||||
local From = self.current
|
||||
local Params = { From, EventName, To, ... }
|
||||
|
||||
if self.Controllable then
|
||||
self:T( "FSM Transition for " .. self.Controllable.ControllableName .. " :" .. self.current .. " --> " .. EventName .. " --> " .. to )
|
||||
|
||||
if self["onleave".. From] or
|
||||
self["OnLeave".. From] or
|
||||
self["onbefore".. EventName] or
|
||||
self["OnBefore".. EventName] or
|
||||
self["onafter".. EventName] or
|
||||
self["OnAfter".. EventName] or
|
||||
self["onenter".. To] or
|
||||
self["OnEnter".. To]
|
||||
then
|
||||
if self:_call_handler( "onbefore", EventName, Params, EventName ) == false then
|
||||
self:T( "*** FSM *** Cancel" .. " *** " .. self.current .. " --> " .. EventName .. " --> " .. To .. " *** onbefore" .. EventName )
|
||||
return false
|
||||
else
|
||||
if self:_call_handler( "OnBefore", EventName, Params, EventName ) == false then
|
||||
self:T( "*** FSM *** Cancel" .. " *** " .. self.current .. " --> " .. EventName .. " --> " .. To .. " *** OnBefore" .. EventName )
|
||||
return false
|
||||
else
|
||||
if self:_call_handler( "onleave", From, Params, EventName ) == false then
|
||||
self:T( "*** FSM *** Cancel" .. " *** " .. self.current .. " --> " .. EventName .. " --> " .. To .. " *** onleave" .. From )
|
||||
return false
|
||||
else
|
||||
if self:_call_handler( "OnLeave", From, Params, EventName ) == false then
|
||||
self:T( "*** FSM *** Cancel" .. " *** " .. self.current .. " --> " .. EventName .. " --> " .. To .. " *** OnLeave" .. From )
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
self:T( "FSM Transition:" .. self.current .. " --> " .. EventName .. " --> " .. to )
|
||||
end
|
||||
local ClassName = self:GetClassName()
|
||||
if ClassName == "FSM" then
|
||||
self:T( "*** FSM *** Transit *** " .. self.current .. " --> " .. EventName .. " --> " .. To )
|
||||
end
|
||||
|
||||
if ( self:_call_handler("onbefore" .. EventName, params, EventName ) == false )
|
||||
or ( self:_call_handler("OnBefore" .. EventName, params, EventName ) == false )
|
||||
or ( self:_call_handler("onleave" .. from, params, EventName ) == false )
|
||||
or ( self:_call_handler("OnLeave" .. from, params, EventName ) == false ) then
|
||||
self:T( "Cancel Transition" )
|
||||
return false
|
||||
if ClassName == "FSM_TASK" then
|
||||
self:T( "*** FSM *** Transit *** " .. self.current .. " --> " .. EventName .. " --> " .. To .. " *** Task: " .. self.TaskName )
|
||||
end
|
||||
|
||||
if ClassName == "FSM_CONTROLLABLE" then
|
||||
self:T( "*** FSM *** Transit *** " .. self.current .. " --> " .. EventName .. " --> " .. To .. " *** TaskUnit: " .. self.Controllable.ControllableName .. " *** " )
|
||||
end
|
||||
|
||||
if ClassName == "FSM_PROCESS" then
|
||||
self:T( "*** FSM *** Transit *** " .. self.current .. " --> " .. EventName .. " --> " .. To .. " *** Task: " .. self.Task:GetName() .. ", TaskUnit: " .. self.Controllable.ControllableName .. " *** " )
|
||||
end
|
||||
end
|
||||
|
||||
self.current = to
|
||||
self.current = To
|
||||
|
||||
local execute = true
|
||||
|
||||
local subtable = self:_gosub( from, EventName )
|
||||
local subtable = self:_gosub( From, EventName )
|
||||
for _, sub in pairs( subtable ) do
|
||||
--if sub.nextevent then
|
||||
-- self:F2( "nextevent = " .. sub.nextevent )
|
||||
-- self[sub.nextevent]( self )
|
||||
--end
|
||||
self:T( "calling sub start event: " .. sub.StartEvent )
|
||||
self:T( "*** FSM *** Sub *** " .. sub.StartEvent )
|
||||
sub.fsm.fsmparent = self
|
||||
sub.fsm.ReturnEvents = sub.ReturnEvents
|
||||
sub.fsm[sub.StartEvent]( sub.fsm )
|
||||
execute = false
|
||||
end
|
||||
|
||||
local fsmparent, Event = self:_isendstate( to )
|
||||
local fsmparent, Event = self:_isendstate( To )
|
||||
if fsmparent and Event then
|
||||
self:F2( { "end state: ", fsmparent, Event } )
|
||||
self:_call_handler("onenter" .. to, params, EventName )
|
||||
self:_call_handler("OnEnter" .. to, params, EventName )
|
||||
self:_call_handler("onafter" .. EventName, params, EventName )
|
||||
self:_call_handler("OnAfter" .. EventName, params, EventName )
|
||||
self:_call_handler("onstatechange", params, EventName )
|
||||
self:T( "*** FSM *** End *** " .. Event )
|
||||
self:_call_handler("onenter", To, Params, EventName )
|
||||
self:_call_handler("OnEnter", To, Params, EventName )
|
||||
self:_call_handler("onafter", EventName, Params, EventName )
|
||||
self:_call_handler("OnAfter", EventName, Params, EventName )
|
||||
self:_call_handler("onstate", "change", Params, EventName )
|
||||
fsmparent[Event]( fsmparent )
|
||||
execute = false
|
||||
end
|
||||
|
||||
if execute then
|
||||
self:_call_handler("onafter", EventName, Params, EventName )
|
||||
self:_call_handler("OnAfter", EventName, Params, EventName )
|
||||
|
||||
-- only execute the call if the From state is not equal to the To state! Otherwise this function should never execute!
|
||||
--if from ~= to then
|
||||
self:_call_handler("onenter" .. to, params, EventName )
|
||||
self:_call_handler("OnEnter" .. to, params, EventName )
|
||||
self:_call_handler("onenter", To, Params, EventName )
|
||||
self:_call_handler("OnEnter", To, Params, EventName )
|
||||
--end
|
||||
|
||||
self:_call_handler("onafter" .. EventName, params, EventName )
|
||||
self:_call_handler("OnAfter" .. EventName, params, EventName )
|
||||
|
||||
self:_call_handler("onstatechange", params, EventName )
|
||||
|
||||
self:_call_handler("onstate", "change", Params, EventName )
|
||||
end
|
||||
else
|
||||
self:T( "Cannot execute transition." )
|
||||
self:T( { From = self.current, Event = EventName, To = to, Can = Can } )
|
||||
self:T( "*** FSM *** NO Transition *** " .. self.current .. " --> " .. EventName .. " --> ? " )
|
||||
end
|
||||
|
||||
return nil
|
||||
@@ -711,17 +740,16 @@ do -- FSM
|
||||
function FSM:_isendstate( Current )
|
||||
local FSMParent = self.fsmparent
|
||||
if FSMParent and self.endstates[Current] then
|
||||
self:T( { state = Current, endstates = self.endstates, endstate = self.endstates[Current] } )
|
||||
--self:T( { state = Current, endstates = self.endstates, endstate = self.endstates[Current] } )
|
||||
FSMParent.current = Current
|
||||
local ParentFrom = FSMParent.current
|
||||
self:T( ParentFrom )
|
||||
self:T( self.ReturnEvents )
|
||||
--self:T( { ParentFrom, self.ReturnEvents } )
|
||||
local Event = self.ReturnEvents[Current]
|
||||
self:T( { ParentFrom, Event, self.ReturnEvents } )
|
||||
--self:T( { Event } )
|
||||
if Event then
|
||||
return FSMParent, Event
|
||||
else
|
||||
self:T( { "Could not find parent event name for state ", ParentFrom } )
|
||||
--self:T( { "Could not find parent event name for state ", ParentFrom } )
|
||||
end
|
||||
end
|
||||
|
||||
@@ -744,6 +772,10 @@ do -- FSM
|
||||
return self.current
|
||||
end
|
||||
|
||||
function FSM:GetCurrentState()
|
||||
return self.current
|
||||
end
|
||||
|
||||
|
||||
function FSM:Is( State )
|
||||
return self.current == State
|
||||
@@ -789,10 +821,10 @@ do -- FSM_CONTROLLABLE
|
||||
-- @param #table FSMT Finite State Machine Table
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable (optional) The CONTROLLABLE object that the FSM_CONTROLLABLE governs.
|
||||
-- @return #FSM_CONTROLLABLE
|
||||
function FSM_CONTROLLABLE:New( FSMT, Controllable )
|
||||
function FSM_CONTROLLABLE:New( Controllable )
|
||||
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, FSM:New( FSMT ) ) -- Core.Fsm#FSM_CONTROLLABLE
|
||||
local self = BASE:Inherit( self, FSM:New() ) -- Core.Fsm#FSM_CONTROLLABLE
|
||||
|
||||
if Controllable then
|
||||
self:SetControllable( Controllable )
|
||||
@@ -864,7 +896,7 @@ do -- FSM_CONTROLLABLE
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE FSMControllable
|
||||
-- @return #FSM_CONTROLLABLE
|
||||
function FSM_CONTROLLABLE:SetControllable( FSMControllable )
|
||||
self:F( FSMControllable )
|
||||
--self:F( FSMControllable:GetName() )
|
||||
self.Controllable = FSMControllable
|
||||
end
|
||||
|
||||
@@ -875,20 +907,22 @@ do -- FSM_CONTROLLABLE
|
||||
return self.Controllable
|
||||
end
|
||||
|
||||
function FSM_CONTROLLABLE:_call_handler( handler, params, EventName )
|
||||
function FSM_CONTROLLABLE:_call_handler( step, trigger, params, EventName )
|
||||
|
||||
local handler = step .. trigger
|
||||
|
||||
local ErrorHandler = function( errmsg )
|
||||
|
||||
env.info( "Error in SCHEDULER function:" .. errmsg )
|
||||
if debug ~= nil then
|
||||
env.info( debug.traceback() )
|
||||
if BASE.Debug ~= nil then
|
||||
env.info( BASE.Debug.traceback() )
|
||||
end
|
||||
|
||||
return errmsg
|
||||
end
|
||||
|
||||
if self[handler] then
|
||||
self:F3( "Calling " .. handler )
|
||||
self:T( "*** FSM *** " .. step .. " *** " .. params[1] .. " --> " .. params[2] .. " --> " .. params[3] .. " *** TaskUnit: " .. self.Controllable:GetName() )
|
||||
self._EventSchedules[EventName] = nil
|
||||
local Result, Value = xpcall( function() return self[handler]( self, self.Controllable, unpack( params ) ) end, ErrorHandler )
|
||||
return Value
|
||||
@@ -924,10 +958,10 @@ 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 )
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -935,22 +969,28 @@ do -- FSM_PROCESS
|
||||
self:T( "No Initialisation" )
|
||||
end
|
||||
|
||||
function FSM_PROCESS:_call_handler( handler, params, EventName )
|
||||
function FSM_PROCESS:_call_handler( step, trigger, params, EventName )
|
||||
|
||||
local handler = step .. trigger
|
||||
|
||||
local ErrorHandler = function( errmsg )
|
||||
|
||||
env.info( "Error in FSM_PROCESS call handler:" .. errmsg )
|
||||
if debug ~= nil then
|
||||
env.info( debug.traceback() )
|
||||
if BASE.Debug ~= nil then
|
||||
env.info( BASE.Debug.traceback() )
|
||||
end
|
||||
|
||||
return errmsg
|
||||
end
|
||||
|
||||
if self[handler] then
|
||||
self:F3( "Calling " .. handler )
|
||||
if handler ~= "onstatechange" then
|
||||
self:T( "*** FSM *** " .. step .. " *** " .. params[1] .. " --> " .. params[2] .. " --> " .. params[3] .. " *** Task: " .. self.Task:GetName() .. ", TaskUnit: " .. self.Controllable:GetName() )
|
||||
end
|
||||
self._EventSchedules[EventName] = nil
|
||||
local Result, Value = xpcall( function() return self[handler]( self, self.Controllable, self.Task, unpack( params ) ) end, ErrorHandler )
|
||||
if self.Controllable and self.Controllable:IsAlive() == true then
|
||||
local Result, Value = xpcall( function() return self[handler]( self, self.Controllable, self.Task, unpack( params ) ) end, ErrorHandler )
|
||||
end
|
||||
return Value
|
||||
--return self[handler]( self, self.Controllable, unpack( params ) )
|
||||
end
|
||||
@@ -966,7 +1006,7 @@ do -- FSM_PROCESS
|
||||
local NewFsm = self:New( Controllable, Task ) -- Core.Fsm#FSM_PROCESS
|
||||
|
||||
NewFsm:Assign( Controllable, Task )
|
||||
|
||||
|
||||
-- Polymorphic call to initialize the new FSM_PROCESS based on self FSM_PROCESS
|
||||
NewFsm:Init( self )
|
||||
|
||||
@@ -980,7 +1020,7 @@ do -- FSM_PROCESS
|
||||
|
||||
-- Copy Processes
|
||||
for ProcessID, Process in pairs( self:GetProcesses() ) do
|
||||
self:E( { Process} )
|
||||
--self:E( { Process:GetName() } )
|
||||
local FsmProcess = NewFsm:AddProcess( Process.From, Process.Event, Process.fsm:Copy( Controllable, Task ), Process.ReturnEvents )
|
||||
end
|
||||
|
||||
@@ -1010,7 +1050,6 @@ do -- FSM_PROCESS
|
||||
|
||||
-- Copy Processes
|
||||
for ProcessID, Process in pairs( self:GetProcesses() ) do
|
||||
self:E( { Process} )
|
||||
if Process.fsm then
|
||||
Process.fsm:Remove()
|
||||
Process.fsm = nil
|
||||
@@ -1058,21 +1097,21 @@ do -- FSM_PROCESS
|
||||
-- TODO: Need to check and fix that an FSM_PROCESS is only for a UNIT. Not for a GROUP.
|
||||
|
||||
--- Send a message of the @{Task} to the Group of the Unit.
|
||||
-- @param #FSM_PROCESS self
|
||||
function FSM_PROCESS:Message( Message )
|
||||
self:F( { Message = Message } )
|
||||
|
||||
local CC = self:GetCommandCenter()
|
||||
local TaskGroup = self.Controllable:GetGroup()
|
||||
-- @param #FSM_PROCESS self
|
||||
function FSM_PROCESS:Message( Message )
|
||||
self:F( { Message = Message } )
|
||||
|
||||
local PlayerName = self.Controllable:GetPlayerName() -- Only for a unit
|
||||
PlayerName = PlayerName and " (" .. PlayerName .. ")" or "" -- If PlayerName is nil, then keep it nil, otherwise add brackets.
|
||||
local Callsign = self.Controllable:GetCallsign()
|
||||
local Prefix = Callsign and " @ " .. Callsign .. PlayerName or ""
|
||||
|
||||
Message = Prefix .. ": " .. Message
|
||||
CC:MessageToGroup( Message, TaskGroup )
|
||||
end
|
||||
local CC = self:GetCommandCenter()
|
||||
local TaskGroup = self.Controllable:GetGroup()
|
||||
|
||||
local PlayerName = self.Controllable:GetPlayerName() -- Only for a unit
|
||||
PlayerName = PlayerName and " (" .. PlayerName .. ")" or "" -- If PlayerName is nil, then keep it nil, otherwise add brackets.
|
||||
local Callsign = self.Controllable:GetCallsign()
|
||||
local Prefix = Callsign and " @ " .. Callsign .. PlayerName or ""
|
||||
|
||||
Message = Prefix .. ": " .. Message
|
||||
CC:MessageToGroup( Message, TaskGroup )
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -1083,7 +1122,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 )
|
||||
@@ -1093,23 +1132,20 @@ end
|
||||
return self
|
||||
end
|
||||
|
||||
function FSM_PROCESS:onenterAssigned( ProcessUnit )
|
||||
self:T( "Assign" )
|
||||
-- function FSM_PROCESS:onenterAssigned( ProcessUnit, Task, From, Event, To )
|
||||
--
|
||||
-- if From( "Planned" ) then
|
||||
-- self:T( "*** FSM *** Assign *** " .. Task:GetName() .. "/" .. ProcessUnit:GetName() .. " *** " .. From .. " --> " .. Event .. " --> " .. To )
|
||||
-- self.Task:Assign()
|
||||
-- end
|
||||
-- end
|
||||
|
||||
self.Task:Assign()
|
||||
end
|
||||
|
||||
function FSM_PROCESS:onenterFailed( ProcessUnit )
|
||||
self:T( "Failed" )
|
||||
function FSM_PROCESS:onenterFailed( ProcessUnit, Task, From, Event, To )
|
||||
self:T( "*** FSM *** Failed *** " .. Task:GetName() .. "/" .. ProcessUnit:GetName() .. " *** " .. From .. " --> " .. Event .. " --> " .. To )
|
||||
|
||||
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
|
||||
@@ -1117,14 +1153,17 @@ end
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function FSM_PROCESS:onstatechange( ProcessUnit, Task, From, Event, To, Dummy )
|
||||
self:T( { ProcessUnit, From, Event, To, Dummy, self:IsTrace() } )
|
||||
function FSM_PROCESS:onstatechange( ProcessUnit, Task, From, Event, To )
|
||||
|
||||
if self:IsTrace() then
|
||||
--MESSAGE:New( "@ Process " .. self:GetClassNameAndID() .. " : " .. Event .. " changed to state " .. To, 2 ):ToAll()
|
||||
if From ~= To then
|
||||
self:T( "*** FSM *** Change *** " .. Task:GetName() .. "/" .. ProcessUnit:GetName() .. " *** " .. From .. " --> " .. Event .. " --> " .. To )
|
||||
end
|
||||
|
||||
self:T( { Scores = self._Scores, To = To } )
|
||||
-- if self:IsTrace() then
|
||||
-- MESSAGE:New( "@ Process " .. self:GetClassNameAndID() .. " : " .. Event .. " changed to state " .. To, 2 ):ToAll()
|
||||
-- self:F2( { Scores = self._Scores, To = To } )
|
||||
-- end
|
||||
|
||||
-- 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
|
||||
|
||||
@@ -1159,22 +1198,23 @@ do -- FSM_TASK
|
||||
|
||||
--- Creates a new FSM_TASK object.
|
||||
-- @param #FSM_TASK self
|
||||
-- @param #table FSMT
|
||||
-- @param Tasking.Task#TASK Task
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param #string TaskName The name of the task.
|
||||
-- @return #FSM_TASK
|
||||
function FSM_TASK:New( FSMT )
|
||||
function FSM_TASK:New( TaskName )
|
||||
|
||||
local self = BASE:Inherit( self, FSM_CONTROLLABLE:New( FSMT ) ) -- Core.Fsm#FSM_TASK
|
||||
local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- Core.Fsm#FSM_TASK
|
||||
|
||||
self["onstatechange"] = self.OnStateChange
|
||||
self.TaskName = TaskName
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function FSM_TASK:_call_handler( handler, params, EventName )
|
||||
function FSM_TASK:_call_handler( step, trigger, params, EventName )
|
||||
local handler = step .. trigger
|
||||
|
||||
if self[handler] then
|
||||
self:T( "Calling " .. handler )
|
||||
self:T( "*** FSM *** " .. step .. " *** " .. params[1] .. " --> " .. params[2] .. " --> " .. params[3] .. " *** Task: " .. self.TaskName )
|
||||
self._EventSchedules[EventName] = nil
|
||||
return self[handler]( self, unpack( params ) )
|
||||
end
|
||||
@@ -1236,9 +1276,10 @@ do -- FSM_SET
|
||||
return self.Controllable
|
||||
end
|
||||
|
||||
function FSM_SET:_call_handler( handler, params, EventName )
|
||||
function FSM_SET:_call_handler( step, trigger, params, EventName )
|
||||
local handler = step .. trigger
|
||||
if self[handler] then
|
||||
self:T( "Calling " .. handler )
|
||||
self:T( "*** FSM *** " .. step .. " *** " .. params[1] .. " --> " .. params[2] .. " --> " .. params[3] )
|
||||
self._EventSchedules[EventName] = nil
|
||||
return self[handler]( self, self.Set, unpack( params ) )
|
||||
end
|
||||
|
||||
146
Moose Development/Moose/Core/Goal.lua
Normal file
146
Moose Development/Moose/Core/Goal.lua
Normal file
@@ -0,0 +1,146 @@
|
||||
--- **Core (WIP)** -- Base class to allow the modeling of processes to achieve Goals.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- GOAL models processes that have an objective with a defined achievement. Derived classes implement the ways how the achievements can be realized.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module Goal
|
||||
|
||||
do -- Goal
|
||||
|
||||
--- @type GOAL
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
|
||||
--- # GOAL class, extends @{Fsm#FSM}
|
||||
--
|
||||
-- GOAL models processes that have an objective with a defined achievement. Derived classes implement the ways how the achievements can be realized.
|
||||
--
|
||||
-- ## 1. GOAL constructor
|
||||
--
|
||||
-- * @{#GOAL.New}(): Creates a new GOAL object.
|
||||
--
|
||||
-- ## 2. GOAL is a finite state machine (FSM).
|
||||
--
|
||||
-- ### 2.1 GOAL States
|
||||
--
|
||||
-- * **Pending**: The goal object is in progress.
|
||||
-- * **Achieved**: The goal objective is Achieved.
|
||||
--
|
||||
-- ### 2.2 GOAL Events
|
||||
--
|
||||
-- * **Achieved**: Set the goal objective to Achieved.
|
||||
--
|
||||
-- @field #GOAL
|
||||
GOAL = {
|
||||
ClassName = "GOAL",
|
||||
}
|
||||
|
||||
--- @field #table GOAL.Players
|
||||
GOAL.Players = {}
|
||||
|
||||
--- @field #number GOAL.TotalContributions
|
||||
GOAL.TotalContributions = 0
|
||||
|
||||
--- GOAL Constructor.
|
||||
-- @param #GOAL self
|
||||
-- @return #GOAL
|
||||
function GOAL:New()
|
||||
|
||||
local self = BASE:Inherit( self, FSM:New() ) -- #GOAL
|
||||
self:F( {} )
|
||||
|
||||
--- Achieved State for GOAL
|
||||
-- @field GOAL.Achieved
|
||||
|
||||
--- Achieved State Handler OnLeave for GOAL
|
||||
-- @function [parent=#GOAL] OnLeaveAchieved
|
||||
-- @param #GOAL self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Achieved State Handler OnEnter for GOAL
|
||||
-- @function [parent=#GOAL] OnEnterAchieved
|
||||
-- @param #GOAL self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
|
||||
self:SetStartState( "Pending" )
|
||||
self:AddTransition( "*", "Achieved", "Achieved" )
|
||||
|
||||
--- Achieved Handler OnBefore for GOAL
|
||||
-- @function [parent=#GOAL] OnBeforeAchieved
|
||||
-- @param #GOAL self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Achieved Handler OnAfter for GOAL
|
||||
-- @function [parent=#GOAL] OnAfterAchieved
|
||||
-- @param #GOAL self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
--- Achieved Trigger for GOAL
|
||||
-- @function [parent=#GOAL] Achieved
|
||||
-- @param #GOAL self
|
||||
|
||||
--- Achieved Asynchronous Trigger for GOAL
|
||||
-- @function [parent=#GOAL] __Achieved
|
||||
-- @param #GOAL self
|
||||
-- @param #number Delay
|
||||
|
||||
self:SetEventPriority( 5 )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- @param #GOAL self
|
||||
-- @param #string PlayerName
|
||||
function GOAL:AddPlayerContribution( PlayerName )
|
||||
self.Players[PlayerName] = self.Players[PlayerName] or 0
|
||||
self.Players[PlayerName] = self.Players[PlayerName] + 1
|
||||
self.TotalContributions = self.TotalContributions + 1
|
||||
end
|
||||
|
||||
|
||||
--- @param #GOAL self
|
||||
-- @param #number Player contribution.
|
||||
function GOAL:GetPlayerContribution( PlayerName )
|
||||
return self.Players[PlayerName] or 0
|
||||
end
|
||||
|
||||
|
||||
--- @param #GOAL self
|
||||
function GOAL:GetPlayerContributions()
|
||||
return self.Players or {}
|
||||
end
|
||||
|
||||
|
||||
--- @param #GOAL self
|
||||
function GOAL:GetTotalContributions()
|
||||
return self.TotalContributions or 0
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- @param #GOAL self
|
||||
-- @return #boolean true if the goal is Achieved
|
||||
function GOAL:IsAchieved()
|
||||
return self:Is( "Achieved" )
|
||||
end
|
||||
|
||||
end
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
--- **Core** - MESSAGE class takes are of the **real-time notifications** and **messages to players** during a simulation.
|
||||
--- **Core** -- MESSAGE class takes are of the **real-time notifications** and **messages to players** during a simulation.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
@@ -39,6 +39,13 @@
|
||||
-- * To all players using @{Message#MESSAGE.ToAllIf}().
|
||||
-- * To a coalition using @{Message#MESSAGE.ToCoalitionIf}().
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #MESSAGE
|
||||
MESSAGE = {
|
||||
ClassName = "MESSAGE",
|
||||
@@ -46,6 +53,16 @@ MESSAGE = {
|
||||
MessageID = 0,
|
||||
}
|
||||
|
||||
--- Message Types
|
||||
-- @type MESSAGE.Type
|
||||
MESSAGE.Type = {
|
||||
Update = "Update",
|
||||
Information = "Information",
|
||||
Briefing = "Briefing Report",
|
||||
Overview = "Overview Report",
|
||||
Detailed = "Detailed Report"
|
||||
}
|
||||
|
||||
|
||||
--- Creates a new MESSAGE object. Note that these MESSAGE objects are not yet displayed on the display panel. You must use the functions @{ToClient} or @{ToCoalition} or @{ToAll} to send these Messages to the respective recipients.
|
||||
-- @param self
|
||||
@@ -67,6 +84,9 @@ function MESSAGE:New( MessageText, MessageDuration, MessageCategory )
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
self:F( { MessageText, MessageDuration, MessageCategory } )
|
||||
|
||||
|
||||
self.MessageType = nil
|
||||
|
||||
-- When no MessageCategory is given, we don't show it as a title...
|
||||
if MessageCategory and MessageCategory ~= "" then
|
||||
if MessageCategory:sub(-1) ~= "\n" then
|
||||
@@ -80,7 +100,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
|
||||
@@ -89,6 +109,37 @@ function MESSAGE:New( MessageText, MessageDuration, MessageCategory )
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Creates a new MESSAGE object of a certain type.
|
||||
-- Note that these MESSAGE objects are not yet displayed on the display panel.
|
||||
-- You must use the functions @{ToClient} or @{ToCoalition} or @{ToAll} to send these Messages to the respective recipients.
|
||||
-- The message display times are automatically defined based on the timing settings in the @{Settings} menu.
|
||||
-- @param self
|
||||
-- @param #string MessageText is the text of the Message.
|
||||
-- @param #MESSAGE.Type MessageType The type of the message.
|
||||
-- @return #MESSAGE
|
||||
-- @usage
|
||||
-- MessageAll = MESSAGE:NewType( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", MESSAGE.Type.Information )
|
||||
-- MessageRED = MESSAGE:NewType( "To the RED Players: You receive a penalty because you've killed one of your own units", MESSAGE.Type.Information )
|
||||
-- MessageClient1 = MESSAGE:NewType( "Congratulations, you've just hit a target", MESSAGE.Type.Update )
|
||||
-- MessageClient2 = MESSAGE:NewType( "Congratulations, you've just killed a target", MESSAGE.Type.Update )
|
||||
function MESSAGE:NewType( MessageText, MessageType )
|
||||
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
self:F( { MessageText } )
|
||||
|
||||
self.MessageType = MessageType
|
||||
|
||||
self.MessageTime = timer.getTime()
|
||||
self.MessageText = MessageText:gsub("^\n","",1):gsub("\n$","",1)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
--- Sends a MESSAGE to a Client Group. Note that the Group needs to be defined within the ME with the skillset "Client" or "Player".
|
||||
-- @param #MESSAGE self
|
||||
-- @param Wrapper.Client#CLIENT Client is the Group of the Client.
|
||||
@@ -108,14 +159,22 @@ end
|
||||
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" )
|
||||
-- MessageClient1:ToClient( ClientGroup )
|
||||
-- MessageClient2:ToClient( ClientGroup )
|
||||
function MESSAGE:ToClient( Client )
|
||||
function MESSAGE:ToClient( Client, Settings )
|
||||
self:F( Client )
|
||||
|
||||
if Client and Client:GetClientGroupID() then
|
||||
|
||||
local ClientGroupID = Client:GetClientGroupID()
|
||||
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
|
||||
trigger.action.outTextForGroup( ClientGroupID, self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration )
|
||||
if self.MessageType then
|
||||
local Settings = Settings or ( Client and _DATABASE:GetPlayerSettings( Client:GetPlayerName() ) ) or _SETTINGS -- Core.Settings#SETTINGS
|
||||
self.MessageDuration = Settings:GetMessageTime( self.MessageType )
|
||||
self.MessageCategory = "" -- self.MessageType .. ": "
|
||||
end
|
||||
|
||||
if self.MessageDuration ~= 0 then
|
||||
local ClientGroupID = Client:GetClientGroupID()
|
||||
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
|
||||
trigger.action.outTextForGroup( ClientGroupID, self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration )
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
@@ -125,13 +184,21 @@ end
|
||||
-- @param #MESSAGE self
|
||||
-- @param Wrapper.Group#GROUP Group is the Group.
|
||||
-- @return #MESSAGE
|
||||
function MESSAGE:ToGroup( Group )
|
||||
function MESSAGE:ToGroup( Group, Settings )
|
||||
self:F( Group.GroupName )
|
||||
|
||||
if Group then
|
||||
|
||||
if self.MessageType then
|
||||
local Settings = Settings or ( Group and _DATABASE:GetPlayerSettings( Group:GetPlayerName() ) ) or _SETTINGS -- Core.Settings#SETTINGS
|
||||
self.MessageDuration = Settings:GetMessageTime( self.MessageType )
|
||||
self.MessageCategory = "" -- self.MessageType .. ": "
|
||||
end
|
||||
|
||||
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
|
||||
trigger.action.outTextForGroup( Group:GetID(), self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration )
|
||||
if self.MessageDuration ~= 0 then
|
||||
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
|
||||
trigger.action.outTextForGroup( Group:GetID(), self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration )
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
@@ -186,12 +253,20 @@ end
|
||||
-- or
|
||||
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" )
|
||||
-- MessageRED:ToCoalition( coalition.side.RED )
|
||||
function MESSAGE:ToCoalition( CoalitionSide )
|
||||
function MESSAGE:ToCoalition( CoalitionSide, Settings )
|
||||
self:F( CoalitionSide )
|
||||
|
||||
if self.MessageType then
|
||||
local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS
|
||||
self.MessageDuration = Settings:GetMessageTime( self.MessageType )
|
||||
self.MessageCategory = "" -- self.MessageType .. ": "
|
||||
end
|
||||
|
||||
if CoalitionSide then
|
||||
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
|
||||
trigger.action.outTextForCoalition( CoalitionSide, self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration )
|
||||
if self.MessageDuration ~= 0 then
|
||||
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
|
||||
trigger.action.outTextForCoalition( CoalitionSide, self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration )
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
@@ -225,8 +300,16 @@ end
|
||||
function MESSAGE:ToAll()
|
||||
self:F()
|
||||
|
||||
self:ToCoalition( coalition.side.RED )
|
||||
self:ToCoalition( coalition.side.BLUE )
|
||||
if self.MessageType then
|
||||
local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS
|
||||
self.MessageDuration = Settings:GetMessageTime( self.MessageType )
|
||||
self.MessageCategory = "" -- self.MessageType .. ": "
|
||||
end
|
||||
|
||||
if self.MessageDuration ~= 0 then
|
||||
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
|
||||
trigger.action.outText( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -238,8 +321,7 @@ end
|
||||
function MESSAGE:ToAllIf( Condition )
|
||||
|
||||
if Condition and Condition == true then
|
||||
self:ToCoalition( coalition.side.RED )
|
||||
self:ToCoalition( coalition.side.BLUE )
|
||||
self:ToAll()
|
||||
end
|
||||
|
||||
return self
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
--- **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...
|
||||
--- **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...
|
||||
--
|
||||
-- 
|
||||
--
|
||||
@@ -79,7 +79,7 @@
|
||||
-- @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
|
||||
-- @field #boolean Loop (default true)
|
||||
-- @extends Core.Base#BASE
|
||||
RADIO = {
|
||||
ClassName = "RADIO",
|
||||
@@ -89,7 +89,7 @@ RADIO = {
|
||||
Subtitle = "",
|
||||
SubtitleDuration = 0,
|
||||
Power = 100,
|
||||
Loop = 0,
|
||||
Loop = true,
|
||||
}
|
||||
|
||||
--- Create a new RADIO Object. This doesn't broadcast a transmission, though, use @{#RADIO.Broadcast} to actually broadcast
|
||||
@@ -101,6 +101,7 @@ RADIO = {
|
||||
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
|
||||
@@ -296,6 +297,7 @@ end
|
||||
-- @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")
|
||||
|
||||
86
Moose Development/Moose/Core/Report.lua
Normal file
86
Moose Development/Moose/Core/Report.lua
Normal file
@@ -0,0 +1,86 @@
|
||||
--- The REPORT class
|
||||
-- @type REPORT
|
||||
-- @extends Core.Base#BASE
|
||||
REPORT = {
|
||||
ClassName = "REPORT",
|
||||
Title = "",
|
||||
}
|
||||
|
||||
--- Create a new REPORT.
|
||||
-- @param #REPORT self
|
||||
-- @param #string Title
|
||||
-- @return #REPORT
|
||||
function REPORT:New( Title )
|
||||
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #REPORT
|
||||
|
||||
self.Report = {}
|
||||
|
||||
self:SetTitle( Title or "" )
|
||||
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
|
||||
end
|
||||
|
||||
--- Add a new line to a REPORT.
|
||||
-- @param #REPORT self
|
||||
-- @param #string Text
|
||||
-- @return #REPORT
|
||||
function REPORT:AddIndent( Text, Separator ) --R2.1
|
||||
self.Report[#self.Report+1] = ( ( Separator and Separator .. string.rep( " ", self.Indent - 1 ) ) or 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
|
||||
|
||||
--- Sets the title of the report.
|
||||
-- @param #REPORT self
|
||||
-- @param #string Title The title of the report.
|
||||
-- @return #REPORT
|
||||
function REPORT:SetTitle( Title )
|
||||
self.Title = Title
|
||||
return self
|
||||
end
|
||||
|
||||
--- Gets the amount of report items contained in the report.
|
||||
-- @param #REPORT self
|
||||
-- @return #number Returns the number of report items contained in the report. 0 is returned if no report items are contained in the report. The title is not counted for.
|
||||
function REPORT:GetCount()
|
||||
return #self.Report
|
||||
end
|
||||
@@ -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
|
||||
--
|
||||
@@ -57,6 +55,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
||||
self:F2( { Scheduler, ScheduleFunction, ScheduleArguments, Start, Repeat, Randomize, Stop } )
|
||||
|
||||
self.CallID = self.CallID + 1
|
||||
local CallID = self.CallID .. "#" .. ( Scheduler.MasterObject and Scheduler.MasterObject.GetClassNameAndID and Scheduler.MasterObject:GetClassNameAndID() or "" ) or ""
|
||||
|
||||
-- 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.
|
||||
@@ -64,36 +63,36 @@ 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" } ) -- or {}
|
||||
self.ObjectSchedulers = self.ObjectSchedulers or setmetatable( {}, { __mode = "v" } )
|
||||
|
||||
if Scheduler.MasterObject then
|
||||
self.ObjectSchedulers[self.CallID] = Scheduler
|
||||
self:F3( { CallID = self.CallID, ObjectScheduler = tostring(self.ObjectSchedulers[self.CallID]), MasterObject = tostring(Scheduler.MasterObject) } )
|
||||
self.ObjectSchedulers[CallID] = Scheduler
|
||||
self:F3( { CallID = CallID, ObjectScheduler = tostring(self.ObjectSchedulers[CallID]), MasterObject = tostring(Scheduler.MasterObject) } )
|
||||
else
|
||||
self.PersistentSchedulers[self.CallID] = Scheduler
|
||||
self:F3( { CallID = self.CallID, PersistentScheduler = self.PersistentSchedulers[self.CallID] } )
|
||||
self.PersistentSchedulers[CallID] = Scheduler
|
||||
self:F3( { CallID = CallID, PersistentScheduler = self.PersistentSchedulers[CallID] } )
|
||||
end
|
||||
|
||||
self.Schedule = self.Schedule or setmetatable( {}, { __mode = "k" } )
|
||||
self.Schedule[Scheduler] = self.Schedule[Scheduler] or {}
|
||||
self.Schedule[Scheduler][self.CallID] = {}
|
||||
self.Schedule[Scheduler][self.CallID].Function = ScheduleFunction
|
||||
self.Schedule[Scheduler][self.CallID].Arguments = ScheduleArguments
|
||||
self.Schedule[Scheduler][self.CallID].StartTime = timer.getTime() + ( Start or 0 )
|
||||
self.Schedule[Scheduler][self.CallID].Start = Start + .1
|
||||
self.Schedule[Scheduler][self.CallID].Repeat = Repeat
|
||||
self.Schedule[Scheduler][self.CallID].Randomize = Randomize
|
||||
self.Schedule[Scheduler][self.CallID].Stop = Stop
|
||||
self.Schedule[Scheduler][CallID] = {}
|
||||
self.Schedule[Scheduler][CallID].Function = ScheduleFunction
|
||||
self.Schedule[Scheduler][CallID].Arguments = ScheduleArguments
|
||||
self.Schedule[Scheduler][CallID].StartTime = timer.getTime() + ( Start or 0 )
|
||||
self.Schedule[Scheduler][CallID].Start = Start + .1
|
||||
self.Schedule[Scheduler][CallID].Repeat = Repeat or 0
|
||||
self.Schedule[Scheduler][CallID].Randomize = Randomize or 0
|
||||
self.Schedule[Scheduler][CallID].Stop = Stop
|
||||
|
||||
self:T3( self.Schedule[Scheduler][self.CallID] )
|
||||
self:T3( self.Schedule[Scheduler][CallID] )
|
||||
|
||||
self.Schedule[Scheduler][self.CallID].CallHandler = function( CallID )
|
||||
self:F2( CallID )
|
||||
self.Schedule[Scheduler][CallID].CallHandler = function( CallID )
|
||||
--self:E( CallID )
|
||||
|
||||
local ErrorHandler = function( errmsg )
|
||||
env.info( "Error in timer function: " .. errmsg )
|
||||
if debug ~= nil then
|
||||
env.info( debug.traceback() )
|
||||
if BASE.Debug ~= nil then
|
||||
env.info( BASE.Debug.traceback() )
|
||||
end
|
||||
return errmsg
|
||||
end
|
||||
@@ -102,16 +101,17 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
||||
if not Scheduler then
|
||||
Scheduler = self.PersistentSchedulers[CallID]
|
||||
end
|
||||
|
||||
self:T3( { Scheduler = Scheduler } )
|
||||
|
||||
--self:T3( { Scheduler = Scheduler } )
|
||||
|
||||
if Scheduler then
|
||||
|
||||
local MasterObject = tostring(Scheduler.MasterObject)
|
||||
local Schedule = self.Schedule[Scheduler][CallID]
|
||||
|
||||
self:T3( { Schedule = Schedule } )
|
||||
--self:T3( { Schedule = Schedule } )
|
||||
|
||||
local ScheduleObject = Scheduler.SchedulerObject
|
||||
local SchedulerObject = Scheduler.SchedulerObject
|
||||
--local ScheduleObjectName = Scheduler.SchedulerObject:GetNameAndClassID()
|
||||
local ScheduleFunction = Schedule.Function
|
||||
local ScheduleArguments = Schedule.Arguments
|
||||
@@ -122,9 +122,10 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
||||
local ScheduleID = Schedule.ScheduleID
|
||||
|
||||
local Status, Result
|
||||
if ScheduleObject then
|
||||
--self:E( { SchedulerObject = SchedulerObject } )
|
||||
if SchedulerObject then
|
||||
local function Timer()
|
||||
return ScheduleFunction( ScheduleObject, unpack( ScheduleArguments ) )
|
||||
return ScheduleFunction( SchedulerObject, unpack( ScheduleArguments ) )
|
||||
end
|
||||
Status, Result = xpcall( Timer, ErrorHandler )
|
||||
else
|
||||
@@ -135,10 +136,13 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
||||
end
|
||||
|
||||
local CurrentTime = timer.getTime()
|
||||
local StartTime = CurrentTime + Start
|
||||
local StartTime = Schedule.StartTime
|
||||
|
||||
self:F3( { Master = MasterObject, CurrentTime = CurrentTime, StartTime = StartTime, Start = Start, Repeat = Repeat, Randomize = Randomize, Stop = Stop } )
|
||||
|
||||
|
||||
if Status and (( Result == nil ) or ( Result and Result ~= false ) ) then
|
||||
if Repeat ~= 0 and ( Stop == 0 ) or ( Stop ~= 0 and CurrentTime <= StartTime + Stop ) then
|
||||
if Repeat ~= 0 and ( ( Stop == 0 ) or ( Stop ~= 0 and CurrentTime <= StartTime + Stop ) ) then
|
||||
local ScheduleTime =
|
||||
CurrentTime +
|
||||
Repeat +
|
||||
@@ -147,7 +151,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,15 +160,15 @@ 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
|
||||
end
|
||||
|
||||
self:Start( Scheduler, self.CallID )
|
||||
self:Start( Scheduler, CallID )
|
||||
|
||||
return self.CallID
|
||||
return CallID
|
||||
end
|
||||
|
||||
function SCHEDULEDISPATCHER:RemoveSchedule( Scheduler, CallID )
|
||||
@@ -184,10 +188,11 @@ function SCHEDULEDISPATCHER:Start( Scheduler, CallID )
|
||||
-- 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].StartTime = timer.getTime() -- Set the StartTime field to indicate when the scheduler started.
|
||||
Schedule[CallID].ScheduleID = timer.scheduleFunction(
|
||||
Schedule[CallID].CallHandler,
|
||||
CallID,
|
||||
timer.getTime() + Schedule[CallID].Start
|
||||
timer.getTime() + Schedule[CallID].Start
|
||||
)
|
||||
end
|
||||
else
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
--- **Core** - SCHEDULER prepares and handles the **execution of functions over scheduled time (intervals)**.
|
||||
--- **Core** -- SCHEDULER prepares and handles the **execution of functions over scheduled time (intervals)**.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
@@ -21,13 +21,13 @@
|
||||
--
|
||||
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
|
||||
--
|
||||
-- ====
|
||||
-- ===
|
||||
--
|
||||
-- # YouTube Channel
|
||||
--
|
||||
-- ### [SCHEDULER YouTube Channel (none)]()
|
||||
--
|
||||
-- ====
|
||||
-- ===
|
||||
--
|
||||
-- ### Contributions:
|
||||
--
|
||||
@@ -210,7 +210,8 @@ SCHEDULER = {
|
||||
-- @return #SCHEDULER self.
|
||||
-- @return #number The ScheduleID of the planned schedule.
|
||||
function SCHEDULER:New( SchedulerObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop )
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #SCHEDULER
|
||||
self:F2( { Start, Repeat, RandomizeFactor, Stop } )
|
||||
|
||||
local ScheduleID = nil
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
839
Moose Development/Moose/Core/Settings.lua
Normal file
839
Moose Development/Moose/Core/Settings.lua
Normal file
@@ -0,0 +1,839 @@
|
||||
--- **Core** -- Manages various settings for MOOSE classes.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- The documentation of the SETTINGS class can be found further in this document.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # **AUTHORS and CONTRIBUTIONS**
|
||||
--
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ### Authors:
|
||||
--
|
||||
-- * **FlightControl**: Design & Programming
|
||||
--
|
||||
-- @module Settings
|
||||
|
||||
|
||||
--- @type SETTINGS
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
--- # SETTINGS class, extends @{Base#BASE}
|
||||
-- The SETTINGS class takes care of various settings that influence the behaviour of certain functionalities and classes within the MOOSE framework.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- The SETTINGS class takes care of various settings that influence the behaviour of certain functionalities and classes within the MOOSE framework.
|
||||
-- SETTINGS can work on 2 levels:
|
||||
--
|
||||
-- - **Default settings**: A running mission has **Default settings**.
|
||||
-- - **Player settings**: For each player its own **Player settings** can be defined, overriding the **Default settings**.
|
||||
--
|
||||
-- So, when there isn't any **Player setting** defined for a player for a specific setting, or, the player cannot be identified, the **Default setting** will be used instead.
|
||||
--
|
||||
-- ## 1. \_SETTINGS object
|
||||
--
|
||||
-- MOOSE defines by default a singleton object called **\_SETTINGS**. Use this object to modify all the **Default settings** for a running mission.
|
||||
-- For each player, MOOSE will automatically allocate also a **player settings** object, and will expose a radio menu to allow the player to adapt the settings to his own preferences.
|
||||
--
|
||||
-- ## 2. SETTINGS Menu
|
||||
--
|
||||
-- Settings can be adapted by the Players and by the Mission Administrator through **radio menus, which are automatically available in the mission**.
|
||||
-- These menus can be found **on level F10 under "Settings"**. There are two kinds of menus generated by the system.
|
||||
--
|
||||
-- ### 2.1. Default settings menu
|
||||
--
|
||||
-- A menu is created automatically per Command Center that allows to modify the **Default** settings.
|
||||
-- So, when joining a CC unit, a menu will be available that allows to change the settings parameters **FOR ALL THE PLAYERS**!
|
||||
-- Note that the **Default settings** will only be used when a player has not choosen its own settings.
|
||||
--
|
||||
-- ### 2.2. Player settings menu
|
||||
--
|
||||
-- A menu is created automatically per Player Slot (group) that allows to modify the **Player** settings.
|
||||
-- So, when joining a slot, a menu wil be available that allows to change the settings parameters **FOR THE PLAYER ONLY**!
|
||||
-- Note that when a player has not chosen a specific setting, the **Default settings** will be used.
|
||||
--
|
||||
-- ### 2.3. Show or Hide the Player Setting menus
|
||||
--
|
||||
-- Of course, it may be requried not to show any setting menus. In this case, a method is available on the **\_SETTINGS object**.
|
||||
-- Use @{#SETTINGS.SetPlayerMenuOff}() to hide the player menus, and use @{#SETTINGS.SetPlayerMenuOn}() show the player menus.
|
||||
-- Note that when this method is used, any player already in a slot will not have its menus visibility changed.
|
||||
-- The option will only have effect when a player enters a new slot or changes a slot.
|
||||
--
|
||||
-- Example:
|
||||
--
|
||||
-- _SETTINGS:SetPlayerMenuOff() -- will disable the player menus.
|
||||
-- _SETTINGS:SetPlayerMenuOn() -- will enable the player menus.
|
||||
-- -- But only when a player exits and reenters the slot these settings will have effect!
|
||||
--
|
||||
--
|
||||
-- ## 3. Settings
|
||||
--
|
||||
-- There are different settings that are managed and applied within the MOOSE framework.
|
||||
-- See below a comprehensive description of each.
|
||||
--
|
||||
-- ### 3.1. **A2G coordinates** display formatting
|
||||
--
|
||||
-- #### 3.1.1. A2G coordinates setting **types**
|
||||
--
|
||||
-- Will customize which display format is used to indicate A2G coordinates in text as part of the Command Center communications.
|
||||
--
|
||||
-- - A2G BR: [Bearing Range](https://en.wikipedia.org/wiki/Bearing_(navigation)).
|
||||
-- - A2G MGRS: The [Military Grid Reference System](https://en.wikipedia.org/wiki/Military_Grid_Reference_System). The accuracy can also be adapted.
|
||||
-- - A2G LL DMS: Lattitude Longitude [Degrees Minutes Seconds](https://en.wikipedia.org/wiki/Geographic_coordinate_conversion). The accuracy can also be adapted.
|
||||
-- - A2G LL DDM: Lattitude Longitude [Decimal Degrees Minutes](https://en.wikipedia.org/wiki/Decimal_degrees). The accuracy can also be adapted.
|
||||
--
|
||||
-- #### 3.1.2. A2G coordinates setting **menu**
|
||||
--
|
||||
-- The settings can be changed by using the **Default settings menu** on the Command Center or the **Player settings menu** on the Player Slot.
|
||||
--
|
||||
-- #### 3.1.3. A2G coordinates setting **methods**
|
||||
--
|
||||
-- There are different methods that can be used to change the **System settings** using the \_SETTINGS object.
|
||||
--
|
||||
-- - @{#SETTINGS.SetA2G_BR}(): Enable the BR display formatting by default.
|
||||
-- - @{#SETTINGS.SetA2G_MGRS}(): Enable the MGRS display formatting by default. Use @{SETTINGS.SetMGRS_Accuracy}() to adapt the accuracy of the MGRS formatting.
|
||||
-- - @{#SETTINGS.SetA2G_LL_DMS}(): Enable the LL DMS display formatting by default. Use @{SETTINGS.SetLL_Accuracy}() to adapt the accuracy of the Seconds formatting.
|
||||
-- - @{#SETTINGS.SetA2G_LL_DDM}(): Enable the LL DDM display formatting by default. Use @{SETTINGS.SetLL_Accuracy}() to adapt the accuracy of the Seconds formatting.
|
||||
--
|
||||
-- #### 3.1.4. A2G coordinates setting - additional notes
|
||||
--
|
||||
-- One additional note on BR. In a situation when a BR coordinate should be given,
|
||||
-- but there isn't any player context (no player unit to reference from), the MGRS formatting will be applied!
|
||||
--
|
||||
-- ### 3.2. **A2A coordinates** formatting
|
||||
--
|
||||
-- #### 3.2.1. A2A coordinates setting **types**
|
||||
--
|
||||
-- Will customize which display format is used to indicate A2A coordinates in text as part of the Command Center communications.
|
||||
--
|
||||
-- - A2A BRAA: [Bearing Range Altitude Aspect](https://en.wikipedia.org/wiki/Bearing_(navigation)).
|
||||
-- - A2A MGRS: The [Military Grid Reference System](https://en.wikipedia.org/wiki/Military_Grid_Reference_System). The accuracy can also be adapted.
|
||||
-- - A2A LL DMS: Lattitude Longitude [Degrees Minutes Seconds](https://en.wikipedia.org/wiki/Geographic_coordinate_conversion). The accuracy can also be adapted.
|
||||
-- - A2A LL DDM: Lattitude Longitude [Decimal Degrees and Minutes](https://en.wikipedia.org/wiki/Decimal_degrees). The accuracy can also be adapted.
|
||||
-- - A2A BULLS: [Bullseye](http://falcon4.wikidot.com/concepts:bullseye).
|
||||
--
|
||||
-- #### 3.2.2. A2A coordinates setting **menu**
|
||||
--
|
||||
-- The settings can be changed by using the **Default settings menu** on the Command Center or the **Player settings menu** on the Player Slot.
|
||||
--
|
||||
-- #### 3.2.3. A2A coordinates setting **methods**
|
||||
--
|
||||
-- There are different methods that can be used to change the **System settings** using the \_SETTINGS object.
|
||||
--
|
||||
-- - @{#SETTINGS.SetA2A_BRAA}(): Enable the BR display formatting by default.
|
||||
-- - @{#SETTINGS.SetA2A_MGRS}(): Enable the MGRS display formatting by default. Use @{SETTINGS.SetMGRS_Accuracy}() to adapt the accuracy of the MGRS formatting.
|
||||
-- - @{#SETTINGS.SetA2A_LL_DMS}(): Enable the LL DMS display formatting by default. Use @{SETTINGS.SetLL_Accuracy}() to adapt the accuracy of the Seconds formatting.
|
||||
-- - @{#SETTINGS.SetA2A_LL_DDM}(): Enable the LL DDM display formatting by default. Use @{SETTINGS.SetLL_Accuracy}() to adapt the accuracy of the Seconds formatting.
|
||||
-- - @{#SETTINGS.SetA2A_BULLS}(): Enable the BULLSeye display formatting by default.
|
||||
--
|
||||
-- #### 3.2.4. A2A coordinates settings - additional notes
|
||||
--
|
||||
-- One additional note on BRAA. In a situation when a BRAA coordinate should be given,
|
||||
-- but there isn't any player context (no player unit to reference from), the MGRS formatting will be applied!
|
||||
--
|
||||
-- ### 3.3. **Measurements** formatting
|
||||
--
|
||||
-- #### 3.3.1. Measurements setting **types**
|
||||
--
|
||||
-- Will customize the measurements system being used as part as part of the Command Center communications.
|
||||
--
|
||||
-- - **Metrics** system: Applies the [Metrics system](https://en.wikipedia.org/wiki/Metric_system) ...
|
||||
-- - **Imperial** system: Applies the [Imperial system](https://en.wikipedia.org/wiki/Imperial_units) ...
|
||||
--
|
||||
-- #### 3.3.2. Measurements setting **menu**
|
||||
--
|
||||
-- The settings can be changed by using the **Default settings menu** on the Command Center or the **Player settings menu** on the Player Slot.
|
||||
--
|
||||
-- #### 3.3.3. Measurements setting **methods**
|
||||
--
|
||||
-- There are different methods that can be used to change the **Default settings** using the \_SETTINGS object.
|
||||
--
|
||||
-- - @{#SETTINGS.SetMetric}(): Enable the Metric system.
|
||||
-- - @{#SETTINGS.SetImperial}(): Enable the Imperial system.
|
||||
--
|
||||
-- ### 3.4. **Message** display times
|
||||
--
|
||||
-- #### 3.4.1. Message setting **types**
|
||||
--
|
||||
-- There are various **Message Types** that will influence the duration how long a message will appear as part of the Command Center communications.
|
||||
--
|
||||
-- - **Update** message: A short update message.
|
||||
-- - **Information** message: Provides new information **while** executing a mission.
|
||||
-- - **Briefing** message: Provides a complete briefing **before** executing a mission.
|
||||
-- - **Overview report**: Provides a short report overview, the summary of the report.
|
||||
-- - **Detailed report**: Provides a complete report.
|
||||
--
|
||||
-- #### 3.4.2. Message setting **menu**
|
||||
--
|
||||
-- The settings can be changed by using the **Default settings menu** on the Command Center or the **Player settings menu** on the Player Slot.
|
||||
--
|
||||
-- Each Message Type has specific timings that will be applied when the message is displayed.
|
||||
-- The Settings Menu will provide for each Message Type a selection of proposed durations from which can be choosen.
|
||||
-- So the player can choose its own amount of seconds how long a message should be displayed of a certain type.
|
||||
-- Note that **Update** messages can be chosen not to be displayed at all!
|
||||
--
|
||||
-- #### 3.4.3. Message setting **methods**
|
||||
--
|
||||
-- There are different methods that can be used to change the **System settings** using the \_SETTINGS object.
|
||||
--
|
||||
-- - @{#SETTINGS.SetMessageTime}(): Define for a specific @{Message.MESSAGE.MessageType} the duration to be displayed in seconds.
|
||||
-- - @{#SETTINGS.GetMessageTime}(): Retrieves for a specific @{Message.MESSAGE.MessageType} the duration to be displayed in seconds.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #SETTINGS
|
||||
SETTINGS = {
|
||||
ClassName = "SETTINGS",
|
||||
ShowPlayerMenu = true,
|
||||
}
|
||||
|
||||
|
||||
|
||||
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( 3 ) -- Defaults
|
||||
self:SetMGRS_Accuracy( 5 ) -- Defaults
|
||||
self:SetMessageTime( MESSAGE.Type.Briefing, 180 )
|
||||
self:SetMessageTime( MESSAGE.Type.Detailed, 60 )
|
||||
self:SetMessageTime( MESSAGE.Type.Information, 30 )
|
||||
self:SetMessageTime( MESSAGE.Type.Overview, 60 )
|
||||
self:SetMessageTime( MESSAGE.Type.Update, 15 )
|
||||
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()
|
||||
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()
|
||||
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_DDM_Accuracy()
|
||||
return self.LL_DDM_Accuracy or _SETTINGS:GetLL_DDM_Accuracy()
|
||||
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 the SETTINGS Message Display Timing of a MessageType
|
||||
-- @param #SETTINGS self
|
||||
-- @param Core.Message#MESSAGE MessageType The type of the message.
|
||||
-- @param #number MessageTime The display time duration in seconds of the MessageType.
|
||||
function SETTINGS:SetMessageTime( MessageType, MessageTime )
|
||||
self.MessageTypeTimings = self.MessageTypeTimings or {}
|
||||
self.MessageTypeTimings[MessageType] = MessageTime
|
||||
end
|
||||
|
||||
|
||||
--- Gets the SETTINGS Message Display Timing of a MessageType
|
||||
-- @param #SETTINGS self
|
||||
-- @param Core.Message#MESSAGE MessageType The type of the message.
|
||||
-- @return #number
|
||||
function SETTINGS:GetMessageTime( MessageType )
|
||||
return ( self.MessageTypeTimings and self.MessageTypeTimings[MessageType] ) or _SETTINGS:GetMessageTime( MessageType )
|
||||
end
|
||||
|
||||
--- Sets A2G LL DMS
|
||||
-- @param #SETTINGS self
|
||||
-- @return #SETTINGS
|
||||
function SETTINGS:SetA2G_LL_DMS()
|
||||
self.A2GSystem = "LL DMS"
|
||||
end
|
||||
|
||||
--- Sets A2G LL DDM
|
||||
-- @param #SETTINGS self
|
||||
-- @return #SETTINGS
|
||||
function SETTINGS:SetA2G_LL_DDM()
|
||||
self.A2GSystem = "LL DDM"
|
||||
end
|
||||
|
||||
--- Is LL DMS
|
||||
-- @param #SETTINGS self
|
||||
-- @return #boolean true if LL DMS
|
||||
function SETTINGS:IsA2G_LL_DMS()
|
||||
return ( self.A2GSystem and self.A2GSystem == "LL DMS" ) or ( not self.A2GSystem and _SETTINGS:IsA2G_LL_DMS() )
|
||||
end
|
||||
|
||||
--- Is LL DDM
|
||||
-- @param #SETTINGS self
|
||||
-- @return #boolean true if LL DDM
|
||||
function SETTINGS:IsA2G_LL_DDM()
|
||||
return ( self.A2GSystem and self.A2GSystem == "LL DDM" ) or ( not self.A2GSystem and _SETTINGS:IsA2G_LL_DDM() )
|
||||
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()
|
||||
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
|
||||
|
||||
--- Sets A2A LL DMS
|
||||
-- @param #SETTINGS self
|
||||
-- @return #SETTINGS
|
||||
function SETTINGS:SetA2A_LL_DMS()
|
||||
self.A2ASystem = "LL DMS"
|
||||
end
|
||||
|
||||
--- Sets A2A LL DDM
|
||||
-- @param #SETTINGS self
|
||||
-- @return #SETTINGS
|
||||
function SETTINGS:SetA2A_LL_DDM()
|
||||
self.A2ASystem = "LL DDM"
|
||||
end
|
||||
|
||||
--- Is LL DMS
|
||||
-- @param #SETTINGS self
|
||||
-- @return #boolean true if LL DMS
|
||||
function SETTINGS:IsA2A_LL_DMS()
|
||||
return ( self.A2ASystem and self.A2ASystem == "LL DMS" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_LL_DMS() )
|
||||
end
|
||||
|
||||
--- Is LL DDM
|
||||
-- @param #SETTINGS self
|
||||
-- @return #boolean true if LL DDM
|
||||
function SETTINGS:IsA2A_LL_DDM()
|
||||
return ( self.A2ASystem and self.A2ASystem == "LL DDM" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_LL_DDM() )
|
||||
end
|
||||
|
||||
--- Sets A2A MGRS
|
||||
-- @param #SETTINGS self
|
||||
-- @return #SETTINGS
|
||||
function SETTINGS:SetA2A_MGRS()
|
||||
self.A2ASystem = "MGRS"
|
||||
end
|
||||
|
||||
--- Is MGRS
|
||||
-- @param #SETTINGS self
|
||||
-- @return #boolean true if MGRS
|
||||
function SETTINGS:IsA2A_MGRS()
|
||||
return ( self.A2ASystem and self.A2ASystem == "MGRS" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_MGRS() )
|
||||
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 not self:IsA2G_LL_DMS() then
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "Lat/Lon Degree Min Sec (LL DMS)", A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "LL DMS" ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
if not self:IsA2G_LL_DDM() then
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "Lat/Lon Degree Dec Min (LL DDM)", A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "LL DDM" ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
if self:IsA2G_LL_DDM() then
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "LL DDM Accuracy 1", A2GCoordinateMenu, self.MenuLL_DDM_Accuracy, self, MenuGroup, RootMenu, 1 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "LL DDM Accuracy 2", A2GCoordinateMenu, self.MenuLL_DDM_Accuracy, self, MenuGroup, RootMenu, 2 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "LL DDM Accuracy 3", A2GCoordinateMenu, self.MenuLL_DDM_Accuracy, self, MenuGroup, RootMenu, 3 ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
if not self:IsA2G_BR() then
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "Bearing, Range (BR)", A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "BR" ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
if not self:IsA2G_MGRS() then
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "Military Grid (MGRS)", A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "MGRS" ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
if self:IsA2G_MGRS() then
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 1", A2GCoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 1 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 2", A2GCoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 2 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 3", A2GCoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 3 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 4", A2GCoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 4 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 5", A2GCoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 5 ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
local A2ACoordinateMenu = MENU_GROUP:New( MenuGroup, "A2A Coordinate System", SettingsMenu ):SetTime( MenuTime )
|
||||
|
||||
if not self:IsA2A_LL_DMS() then
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "Lat/Lon Degree Min Sec (LL DMS)", A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "LL DMS" ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
if not self:IsA2A_LL_DDM() then
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "Lat/Lon Degree Dec Min (LL DDM)", A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "LL DDM" ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
if self:IsA2A_LL_DDM() then
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "LL DDM Accuracy 1", A2ACoordinateMenu, self.MenuLL_DDM_Accuracy, self, MenuGroup, RootMenu, 1 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "LL DDM Accuracy 2", A2ACoordinateMenu, self.MenuLL_DDM_Accuracy, self, MenuGroup, RootMenu, 2 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "LL DDM Accuracy 3", A2ACoordinateMenu, self.MenuLL_DDM_Accuracy, self, MenuGroup, RootMenu, 3 ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
if not self:IsA2A_BULLS() then
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "Bullseye (BULLS)", A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "BULLS" ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
if not self:IsA2A_BRAA() then
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "Bearing Range Altitude Aspect (BRAA)", A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "BRAA" ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
if not self:IsA2A_MGRS() then
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "Military Grid (MGRS)", A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "MGRS" ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
if self:IsA2A_MGRS() then
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 1", A2ACoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 1 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 2", A2ACoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 2 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 3", A2ACoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 3 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 4", A2ACoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 4 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 5", A2ACoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 5 ):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
|
||||
|
||||
local MessagesMenu = MENU_GROUP:New( MenuGroup, "Messages and Reports", SettingsMenu ):SetTime( MenuTime )
|
||||
|
||||
local UpdateMessagesMenu = MENU_GROUP:New( MenuGroup, "Update Messages", MessagesMenu ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "Off", UpdateMessagesMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Update, 0 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "5 seconds", UpdateMessagesMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Update, 5 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "10 seconds", UpdateMessagesMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Update, 10 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "15 seconds", UpdateMessagesMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Update, 15 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "30 seconds", UpdateMessagesMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Update, 30 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "1 minute", UpdateMessagesMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Update, 60 ):SetTime( MenuTime )
|
||||
|
||||
local InformationMessagesMenu = MENU_GROUP:New( MenuGroup, "Information Messages", MessagesMenu ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "5 seconds", InformationMessagesMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Information, 5 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "10 seconds", InformationMessagesMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Information, 10 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "15 seconds", InformationMessagesMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Information, 15 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "30 seconds", InformationMessagesMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Information, 30 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "1 minute", InformationMessagesMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Information, 60 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "2 minutes", InformationMessagesMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Information, 120 ):SetTime( MenuTime )
|
||||
|
||||
local BriefingReportsMenu = MENU_GROUP:New( MenuGroup, "Briefing Reports", MessagesMenu ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "15 seconds", BriefingReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Briefing, 15 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "30 seconds", BriefingReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Briefing, 30 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "1 minute", BriefingReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Briefing, 60 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "2 minutes", BriefingReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Briefing, 120 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "3 minutes", BriefingReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Briefing, 180 ):SetTime( MenuTime )
|
||||
|
||||
local OverviewReportsMenu = MENU_GROUP:New( MenuGroup, "Overview Reports", MessagesMenu ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "15 seconds", OverviewReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Overview, 15 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "30 seconds", OverviewReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Overview, 30 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "1 minute", OverviewReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Overview, 60 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "2 minutes", OverviewReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Overview, 120 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "3 minutes", OverviewReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Overview, 180 ):SetTime( MenuTime )
|
||||
|
||||
local DetailedReportsMenu = MENU_GROUP:New( MenuGroup, "Detailed Reports", MessagesMenu ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "15 seconds", DetailedReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.DetailedReportsMenu, 15 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "30 seconds", DetailedReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.DetailedReportsMenu, 30 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "1 minute", DetailedReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.DetailedReportsMenu, 60 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "2 minutes", DetailedReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.DetailedReportsMenu, 120 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "3 minutes", DetailedReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.DetailedReportsMenu, 180 ):SetTime( MenuTime )
|
||||
|
||||
|
||||
SettingsMenu:Remove( MenuTime )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Sets the player menus on, so that the **Player setting menus** show up for the players.
|
||||
-- But only when a player exits and reenters the slot these settings will have effect!
|
||||
-- It is advised to use this method at the start of the mission.
|
||||
-- @param #SETTINGS self
|
||||
-- @return #SETTINGS
|
||||
-- @usage
|
||||
-- _SETTINGS:SetPlayerMenuOn() -- will enable the player menus.
|
||||
function SETTINGS:SetPlayerMenuOn()
|
||||
self.ShowPlayerMenu = true
|
||||
end
|
||||
|
||||
--- Sets the player menus off, so that the **Player setting menus** won't show up for the players.
|
||||
-- But only when a player exits and reenters the slot these settings will have effect!
|
||||
-- It is advised to use this method at the start of the mission.
|
||||
-- @param #SETTINGS self
|
||||
-- @return #SETTINGS self
|
||||
-- @usage
|
||||
-- _SETTINGS:SetPlayerMenuOff() -- will disable the player menus.
|
||||
function SETTINGS:SetPlayerMenuOff()
|
||||
self.ShowPlayerMenu = false
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Updates the menu of the player seated in the PlayerUnit.
|
||||
-- @param #SETTINGS self
|
||||
-- @param Wrapper.Client#CLIENT PlayerUnit
|
||||
-- @return #SETTINGS self
|
||||
function SETTINGS:SetPlayerMenu( PlayerUnit )
|
||||
|
||||
if _SETTINGS.ShowPlayerMenu == true then
|
||||
|
||||
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 not self:IsA2G_LL_DMS() then
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Lat/Lon Degree Min Sec (LL DMS)", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DMS" )
|
||||
end
|
||||
|
||||
if not self:IsA2G_LL_DDM() then
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Lat/Lon Degree Dec Min (LL DDM)", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DDM" )
|
||||
end
|
||||
|
||||
if self:IsA2G_LL_DDM() then
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "LL DDM Accuracy 1", A2GCoordinateMenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 1 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "LL DDM Accuracy 2", A2GCoordinateMenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 2 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "LL DDM Accuracy 3", A2GCoordinateMenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 3 )
|
||||
end
|
||||
|
||||
if not self:IsA2G_BR() then
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Bearing, Range (BR)", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "BR" )
|
||||
end
|
||||
|
||||
if not self:IsA2G_MGRS() then
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Military Grid (MGRS)", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "MGRS" )
|
||||
end
|
||||
|
||||
if self:IsA2G_MGRS() then
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "MGRS Accuracy 1", A2GCoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 1 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "MGRS Accuracy 2", A2GCoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 2 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "MGRS Accuracy 3", A2GCoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 3 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "MGRS Accuracy 4", A2GCoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 4 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "MGRS Accuracy 5", A2GCoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 5 )
|
||||
end
|
||||
|
||||
local A2ACoordinateMenu = MENU_GROUP:New( PlayerGroup, "A2A Coordinate System", PlayerMenu )
|
||||
|
||||
|
||||
if not self:IsA2A_LL_DMS() then
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Lat/Lon Degree Min Sec (LL DMS)", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DMS" )
|
||||
end
|
||||
|
||||
if not self:IsA2A_LL_DDM() then
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Lat/Lon Degree Dec Min (LL DDM)", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DDM" )
|
||||
end
|
||||
|
||||
if self:IsA2A_LL_DDM() then
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "LL DDM Accuracy 1", A2GCoordinateMenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 1 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "LL DDM Accuracy 2", A2GCoordinateMenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 2 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "LL DDM Accuracy 3", A2GCoordinateMenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 3 )
|
||||
end
|
||||
|
||||
if not self:IsA2A_BULLS() then
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Bullseye (BULLS)", A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "BULLS" )
|
||||
end
|
||||
|
||||
if not self:IsA2A_BRAA() then
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Bearing Range Altitude Aspect (BRAA)", A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "BRAA" )
|
||||
end
|
||||
|
||||
if not self:IsA2A_MGRS() then
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Military Grid (MGRS)", A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "MGRS" )
|
||||
end
|
||||
|
||||
if self:IsA2A_MGRS() then
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Military Grid (MGRS) Accuracy 1", A2ACoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 1 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Military Grid (MGRS) Accuracy 2", A2ACoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 2 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Military Grid (MGRS) Accuracy 3", A2ACoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 3 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Military Grid (MGRS) Accuracy 4", A2ACoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 4 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Military Grid (MGRS) Accuracy 5", A2ACoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 5 )
|
||||
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
|
||||
|
||||
|
||||
local MessagesMenu = MENU_GROUP:New( PlayerGroup, "Messages and Reports", PlayerMenu )
|
||||
|
||||
local UpdateMessagesMenu = MENU_GROUP:New( PlayerGroup, "Update Messages", MessagesMenu )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Off", UpdateMessagesMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Update, 0 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "5 seconds", UpdateMessagesMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Update, 5 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "10 seconds", UpdateMessagesMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Update, 10 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "15 seconds", UpdateMessagesMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Update, 15 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "30 seconds", UpdateMessagesMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Update, 30 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "1 minute", UpdateMessagesMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Update, 60 )
|
||||
|
||||
local InformationMessagesMenu = MENU_GROUP:New( PlayerGroup, "Information Messages", MessagesMenu )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "5 seconds", InformationMessagesMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Information, 5 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "10 seconds", InformationMessagesMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Information, 10 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "15 seconds", InformationMessagesMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Information, 15 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "30 seconds", InformationMessagesMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Information, 30 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "1 minute", InformationMessagesMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Information, 60 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "2 minutes", InformationMessagesMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Information, 120 )
|
||||
|
||||
local BriefingReportsMenu = MENU_GROUP:New( PlayerGroup, "Briefing Reports", MessagesMenu )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "15 seconds", BriefingReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Briefing, 15 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "30 seconds", BriefingReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Briefing, 30 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "1 minute", BriefingReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Briefing, 60 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "2 minutes", BriefingReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Briefing, 120 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "3 minutes", BriefingReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Briefing, 180 )
|
||||
|
||||
local OverviewReportsMenu = MENU_GROUP:New( PlayerGroup, "Overview Reports", MessagesMenu )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "15 seconds", OverviewReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Overview, 15 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "30 seconds", OverviewReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Overview, 30 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "1 minute", OverviewReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Overview, 60 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "2 minutes", OverviewReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Overview, 120 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "3 minutes", OverviewReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Overview, 180 )
|
||||
|
||||
local DetailedReportsMenu = MENU_GROUP:New( PlayerGroup, "Detailed Reports", MessagesMenu )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "15 seconds", DetailedReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.DetailedReportsMenu, 15 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "30 seconds", DetailedReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.DetailedReportsMenu, 30 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "1 minute", DetailedReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.DetailedReportsMenu, 60 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "2 minutes", DetailedReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.DetailedReportsMenu, 120 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "3 minutes", DetailedReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.DetailedReportsMenu, 180 )
|
||||
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Removes the player menu from the PlayerUnit.
|
||||
--- @param #SETTINGS self
|
||||
-- @param Wrapper.Client#CLIENT PlayerUnit
|
||||
-- @return #SETTINGS self
|
||||
function SETTINGS:RemovePlayerMenu( PlayerUnit )
|
||||
|
||||
if self.PlayerMenu then
|
||||
self.PlayerMenu:Remove()
|
||||
self.PlayerMenu = nil
|
||||
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_DDM_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: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
|
||||
|
||||
--- @param #SETTINGS self
|
||||
function SETTINGS:MenuMessageTimingsSystem( MenuGroup, RootMenu, MessageType, MessageTime )
|
||||
self:SetMessageTime( MessageType, MessageTime )
|
||||
MESSAGE:New( string.format( "Settings: Default message time set for %s to %d.", MessageType, MessageTime ), 5 ):ToAll()
|
||||
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_DDM_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: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
|
||||
|
||||
--- @param #SETTINGS self
|
||||
function SETTINGS:MenuGroupMessageTimingsSystem( PlayerUnit, PlayerGroup, PlayerName, MessageType, MessageTime )
|
||||
self:SetMessageTime( MessageType, MessageTime )
|
||||
MESSAGE:New( string.format( "Settings: Default message time set for %s to %d.", MessageType, MessageTime ), 5 ):ToGroup( PlayerGroup )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -1,44 +1,29 @@
|
||||
--- **Functional** -- Spawn dynamically new GROUPs in your missions.
|
||||
--- **Core** -- SPAWN class dynamically spawns new groups of units in your missions.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ====
|
||||
-- ===
|
||||
--
|
||||
-- The documentation of the SPAWN class can be found further in this document.
|
||||
--
|
||||
-- ====
|
||||
-- ===
|
||||
--
|
||||
-- # Demo Missions
|
||||
--
|
||||
-- ### [SPAWN Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/SPA%20-%20Spawning)
|
||||
--
|
||||
-- ### [SPAWN Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SPA%20-%20Spawning)
|
||||
--
|
||||
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- # YouTube Channel
|
||||
--
|
||||
-- ### [SPAWN YouTube Channel](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl1jirWIo4t4YxqN-HxjqRkL)
|
||||
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/SPA%20-%20Spawning)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # **AUTHORS and CONTRIBUTIONS**
|
||||
-- ### [YouTube Playlist](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl1jirWIo4t4YxqN-HxjqRkL)
|
||||
--
|
||||
-- ### Contributions:
|
||||
-- ===
|
||||
--
|
||||
-- * **Aaron**: Posed the idea for Group position randomization at SpawnInZone and make the Unit randomization separate from the Group randomization.
|
||||
-- * [**Entropy**](https://forums.eagle.ru/member.php?u=111471), **Afinegan**: Came up with the requirement for AIOnOff().
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions: A lot of people within this community!
|
||||
--
|
||||
-- ### Authors:
|
||||
--
|
||||
-- * **FlightControl**: Design & Programming
|
||||
-- ===
|
||||
--
|
||||
-- @module Spawn
|
||||
|
||||
|
||||
|
||||
--- SPAWN Class
|
||||
-- @type SPAWN
|
||||
-- @field ClassName
|
||||
@@ -54,8 +39,12 @@
|
||||
|
||||
--- # SPAWN class, extends @{Base#BASE}
|
||||
--
|
||||
-- -- 
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- The SPAWN class allows to spawn dynamically new groups.
|
||||
-- Each SPAWN object needs to be have a related **template group** setup in the Mission Editor (ME),
|
||||
-- Each SPAWN object needs to be have related **template groups** setup in the Mission Editor (ME),
|
||||
-- which is a normal group with the **Late Activation** flag set.
|
||||
-- This template group will never be activated in your mission.
|
||||
-- SPAWN uses that **template group** to reference to all the characteristics
|
||||
@@ -199,6 +188,7 @@
|
||||
-- * @{#SPAWN.SpawnFromStatic}(): Spawn a new group from a structure, taking the position of a @{Static}.
|
||||
-- * @{#SPAWN.SpawnFromUnit}(): Spawn a new group taking the position of a @{Unit}.
|
||||
-- * @{#SPAWN.SpawnInZone}(): Spawn a new group in a @{Zone}.
|
||||
-- * @{#SPAWN.SpawnAtAirbase}(): Spawn a new group at an @{Airbase}, which can be an airdrome, ship or helipad.
|
||||
--
|
||||
-- Note that @{#SPAWN.Spawn} and @{#SPAWN.ReSpawn} return a @{GROUP#GROUP.New} object, that contains a reference to the DCSGroup object.
|
||||
-- You can use the @{GROUP} object to do further actions with the DCSGroup.
|
||||
@@ -264,6 +254,18 @@ SPAWN = {
|
||||
}
|
||||
|
||||
|
||||
--- Enumerator for spawns at airbases
|
||||
-- @type SPAWN.Takeoff
|
||||
-- @extends Wrapper.Group#GROUP.Takeoff
|
||||
|
||||
--- @field #SPAWN.Takeoff Takeoff
|
||||
SPAWN.Takeoff = {
|
||||
Air = 1,
|
||||
Runway = 2,
|
||||
Hot = 3,
|
||||
Cold = 4,
|
||||
}
|
||||
|
||||
--- @type SPAWN.SpawnZoneTable
|
||||
-- @list <Core.Zone#ZONE_BASE> SpawnZone
|
||||
|
||||
@@ -280,25 +282,26 @@ function SPAWN:New( SpawnTemplatePrefix )
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #SPAWN
|
||||
self:F( { SpawnTemplatePrefix } )
|
||||
|
||||
local TemplateGroup = Group.getByName( SpawnTemplatePrefix )
|
||||
local TemplateGroup = GROUP:FindByName( SpawnTemplatePrefix )
|
||||
if TemplateGroup then
|
||||
self.SpawnTemplatePrefix = SpawnTemplatePrefix
|
||||
self.SpawnIndex = 0
|
||||
self.SpawnCount = 0 -- The internal counter of the amount of spawning the has happened since SpawnStart.
|
||||
self.AliveUnits = 0 -- Contains the counter how many units are currently alive
|
||||
self.SpawnIsScheduled = false -- Reflects if the spawning for this SpawnTemplatePrefix is going to be scheduled or not.
|
||||
self.SpawnIsScheduled = false -- Reflects if the spawning for this SpawnTemplatePrefix is going to be scheduled or not.
|
||||
self.SpawnTemplate = self._GetTemplate( self, SpawnTemplatePrefix ) -- Contains the template structure for a Group Spawn from the Mission Editor. Note that this group must have lateActivation always on!!!
|
||||
self.Repeat = false -- Don't repeat the group from Take-Off till Landing and back Take-Off by ReSpawning.
|
||||
self.UnControlled = false -- When working in UnControlled mode, all planes are Spawned in UnControlled mode before the scheduler starts.
|
||||
self.SpawnInitLimit = false -- By default, no InitLimit
|
||||
self.SpawnMaxUnitsAlive = 0 -- The maximum amount of groups that can be alive of SpawnTemplatePrefix at the same time.
|
||||
self.SpawnMaxGroups = 0 -- The maximum amount of groups that can be spawned.
|
||||
self.SpawnRandomize = false -- Sets the randomization flag of new Spawned units to false.
|
||||
self.SpawnVisible = false -- Flag that indicates if all the Groups of the SpawnGroup need to be visible when Spawned.
|
||||
self.AIOnOff = true -- The AI is on by default when spawning a group.
|
||||
self.Repeat = false -- Don't repeat the group from Take-Off till Landing and back Take-Off by ReSpawning.
|
||||
self.UnControlled = false -- When working in UnControlled mode, all planes are Spawned in UnControlled mode before the scheduler starts.
|
||||
self.SpawnInitLimit = false -- By default, no InitLimit
|
||||
self.SpawnMaxUnitsAlive = 0 -- The maximum amount of groups that can be alive of SpawnTemplatePrefix at the same time.
|
||||
self.SpawnMaxGroups = 0 -- The maximum amount of groups that can be spawned.
|
||||
self.SpawnRandomize = false -- Sets the randomization flag of new Spawned units to false.
|
||||
self.SpawnVisible = false -- Flag that indicates if all the Groups of the SpawnGroup need to be visible when Spawned.
|
||||
self.AIOnOff = true -- The AI is on by default when spawning a group.
|
||||
self.SpawnUnControlled = false
|
||||
self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name.
|
||||
self.DelayOnOff = false -- No intial delay when spawning the first group.
|
||||
self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name.
|
||||
self.DelayOnOff = false -- No intial delay when spawning the first group.
|
||||
self.Grouping = nil -- No grouping
|
||||
|
||||
self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned.
|
||||
else
|
||||
@@ -306,6 +309,7 @@ function SPAWN:New( SpawnTemplatePrefix )
|
||||
end
|
||||
|
||||
self:SetEventPriority( 5 )
|
||||
self.SpawnHookScheduler = SCHEDULER:New( nil )
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -323,26 +327,27 @@ function SPAWN:NewWithAlias( SpawnTemplatePrefix, SpawnAliasPrefix )
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
self:F( { SpawnTemplatePrefix, SpawnAliasPrefix } )
|
||||
|
||||
local TemplateGroup = Group.getByName( SpawnTemplatePrefix )
|
||||
local TemplateGroup = GROUP:FindByName( SpawnTemplatePrefix )
|
||||
if TemplateGroup then
|
||||
self.SpawnTemplatePrefix = SpawnTemplatePrefix
|
||||
self.SpawnAliasPrefix = SpawnAliasPrefix
|
||||
self.SpawnIndex = 0
|
||||
self.SpawnCount = 0 -- The internal counter of the amount of spawning the has happened since SpawnStart.
|
||||
self.AliveUnits = 0 -- Contains the counter how many units are currently alive
|
||||
self.SpawnIsScheduled = false -- Reflects if the spawning for this SpawnTemplatePrefix is going to be scheduled or not.
|
||||
self.SpawnIsScheduled = false -- Reflects if the spawning for this SpawnTemplatePrefix is going to be scheduled or not.
|
||||
self.SpawnTemplate = self._GetTemplate( self, SpawnTemplatePrefix ) -- Contains the template structure for a Group Spawn from the Mission Editor. Note that this group must have lateActivation always on!!!
|
||||
self.Repeat = false -- Don't repeat the group from Take-Off till Landing and back Take-Off by ReSpawning.
|
||||
self.UnControlled = false -- When working in UnControlled mode, all planes are Spawned in UnControlled mode before the scheduler starts.
|
||||
self.SpawnInitLimit = false -- By default, no InitLimit
|
||||
self.SpawnMaxUnitsAlive = 0 -- The maximum amount of groups that can be alive of SpawnTemplatePrefix at the same time.
|
||||
self.SpawnMaxGroups = 0 -- The maximum amount of groups that can be spawned.
|
||||
self.SpawnRandomize = false -- Sets the randomization flag of new Spawned units to false.
|
||||
self.SpawnVisible = false -- Flag that indicates if all the Groups of the SpawnGroup need to be visible when Spawned.
|
||||
self.AIOnOff = true -- The AI is on by default when spawning a group.
|
||||
self.Repeat = false -- Don't repeat the group from Take-Off till Landing and back Take-Off by ReSpawning.
|
||||
self.UnControlled = false -- When working in UnControlled mode, all planes are Spawned in UnControlled mode before the scheduler starts.
|
||||
self.SpawnInitLimit = false -- By default, no InitLimit
|
||||
self.SpawnMaxUnitsAlive = 0 -- The maximum amount of groups that can be alive of SpawnTemplatePrefix at the same time.
|
||||
self.SpawnMaxGroups = 0 -- The maximum amount of groups that can be spawned.
|
||||
self.SpawnRandomize = false -- Sets the randomization flag of new Spawned units to false.
|
||||
self.SpawnVisible = false -- Flag that indicates if all the Groups of the SpawnGroup need to be visible when Spawned.
|
||||
self.AIOnOff = true -- The AI is on by default when spawning a group.
|
||||
self.SpawnUnControlled = false
|
||||
self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name.
|
||||
self.DelayOnOff = false -- No intial delay when spawning the first group.
|
||||
self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name.
|
||||
self.DelayOnOff = false -- No intial delay when spawning the first group.
|
||||
self.Grouping = nil
|
||||
|
||||
self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned.
|
||||
else
|
||||
@@ -350,11 +355,61 @@ function SPAWN:NewWithAlias( SpawnTemplatePrefix, SpawnAliasPrefix )
|
||||
end
|
||||
|
||||
self:SetEventPriority( 5 )
|
||||
self.SpawnHookScheduler = SCHEDULER:New( nil )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Creates a new SPAWN instance to create new groups based on the provided template.
|
||||
-- @param #SPAWN self
|
||||
-- @param #table SpawnTemplate is the Template of the Group. This must be a valid Group Template structure!
|
||||
-- @param #string SpawnTemplatePrefix is the name of the Group that will be given at each spawn.
|
||||
-- @param #string SpawnAliasPrefix (optional) is the name that will be given to the Group at runtime.
|
||||
-- @return #SPAWN
|
||||
-- @usage
|
||||
-- -- Create a new SPAWN object based on a Group Template defined from scratch.
|
||||
-- Spawn_BE_KA50 = SPAWN:NewWithAlias( 'BE KA-50@RAMP-Ground Defense', 'Helicopter Attacking a City' )
|
||||
-- @usage
|
||||
-- -- Create a new CSAR_Spawn object based on a normal Group Template to spawn a soldier.
|
||||
-- local CSAR_Spawn = SPAWN:NewWithFromTemplate( Template, "CSAR", "Pilot" )
|
||||
function SPAWN:NewFromTemplate( SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPrefix )
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
self:F( { SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPrefix } )
|
||||
|
||||
if SpawnTemplate then
|
||||
self.SpawnTemplate = SpawnTemplate -- Contains the template structure for a Group Spawn from the Mission Editor. Note that this group must have lateActivation always on!!!
|
||||
self.SpawnTemplatePrefix = SpawnTemplatePrefix
|
||||
self.SpawnAliasPrefix = SpawnAliasPrefix
|
||||
self.SpawnIndex = 0
|
||||
self.SpawnCount = 0 -- The internal counter of the amount of spawning the has happened since SpawnStart.
|
||||
self.AliveUnits = 0 -- Contains the counter how many units are currently alive
|
||||
self.SpawnIsScheduled = false -- Reflects if the spawning for this SpawnTemplatePrefix is going to be scheduled or not.
|
||||
self.Repeat = false -- Don't repeat the group from Take-Off till Landing and back Take-Off by ReSpawning.
|
||||
self.UnControlled = false -- When working in UnControlled mode, all planes are Spawned in UnControlled mode before the scheduler starts.
|
||||
self.SpawnInitLimit = false -- By default, no InitLimit.
|
||||
self.SpawnMaxUnitsAlive = 0 -- The maximum amount of groups that can be alive of SpawnTemplatePrefix at the same time.
|
||||
self.SpawnMaxGroups = 0 -- The maximum amount of groups that can be spawned.
|
||||
self.SpawnRandomize = false -- Sets the randomization flag of new Spawned units to false.
|
||||
self.SpawnVisible = false -- Flag that indicates if all the Groups of the SpawnGroup need to be visible when Spawned.
|
||||
self.AIOnOff = true -- The AI is on by default when spawning a group.
|
||||
self.SpawnUnControlled = false
|
||||
self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name.
|
||||
self.DelayOnOff = false -- No intial delay when spawning the first group.
|
||||
self.Grouping = nil
|
||||
|
||||
self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned.
|
||||
else
|
||||
error( "There is no template provided for SpawnTemplatePrefix = '" .. SpawnTemplatePrefix .. "'" )
|
||||
end
|
||||
|
||||
self:SetEventPriority( 5 )
|
||||
self.SpawnHookScheduler = SCHEDULER:New( nil )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Limits the Maximum amount of Units that can be alive at the same time, and the maximum amount of groups that can be spawned.
|
||||
-- Note that this method is exceptionally important to balance the performance of the mission. Depending on the machine etc, a mission can only process a maximum amount of units.
|
||||
-- If the time interval must be short, but there should not be more Units or Groups alive than a maximum amount of units, then this method should be used...
|
||||
@@ -398,6 +453,57 @@ function SPAWN:InitKeepUnitNames()
|
||||
return self
|
||||
end
|
||||
|
||||
--- Defines the Heading for the new spawned units.
|
||||
-- The heading can be given as one fixed degree, or can be randomized between minimum and maximum degrees.
|
||||
-- @param #SPAWN self
|
||||
-- @param #number HeadingMin The minimum or fixed heading in degrees.
|
||||
-- @param #number HeadingMax (optional) The maximum heading in degrees. This there is no maximum heading, then the heading will be fixed for all units using minimum heading.
|
||||
-- @return #SPAWN self
|
||||
-- @usage
|
||||
--
|
||||
-- Spawn = SPAWN:New( ... )
|
||||
--
|
||||
-- -- Spawn the units pointing to 100 degrees.
|
||||
-- Spawn:InitHeading( 100 )
|
||||
--
|
||||
-- -- Spawn the units pointing between 100 and 150 degrees.
|
||||
-- Spawn:InitHeading( 100, 150 )
|
||||
--
|
||||
function SPAWN:InitHeading( HeadingMin, HeadingMax )
|
||||
self:F( )
|
||||
|
||||
self.SpawnInitHeadingMin = HeadingMin
|
||||
self.SpawnInitHeadingMax = HeadingMax
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
function SPAWN:InitCoalition( Coalition )
|
||||
self:F( )
|
||||
|
||||
self.SpawnInitCoalition = Coalition
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
function SPAWN:InitCountry( Country )
|
||||
self:F( )
|
||||
|
||||
self.SpawnInitCountry = Country
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
function SPAWN:InitCategory( Category )
|
||||
self:F( )
|
||||
|
||||
self.SpawnInitCategory = Category
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Randomizes the defined route of the SpawnTemplatePrefix group in the ME. This is very useful to define extra variation of the behaviour of groups.
|
||||
-- @param #SPAWN self
|
||||
@@ -509,6 +615,87 @@ function SPAWN:InitRandomizeTemplate( SpawnTemplatePrefixTable )
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Randomize templates to be used as the unit representatives for the Spawned group, defined using a SET_GROUP object.
|
||||
-- This method becomes useful when you need to spawn groups with random templates of groups defined within the mission editor,
|
||||
-- but they will all follow the same Template route and have the same prefix name.
|
||||
-- In other words, this method randomizes between a defined set of groups the template to be used for each new spawn of a group.
|
||||
-- @param #SPAWN self
|
||||
-- @param Core.Set#SET_GROUP SpawnTemplateSet A SET_GROUP object set, that contains the groups that are possible unit representatives of the group to be spawned.
|
||||
-- @return #SPAWN
|
||||
-- @usage
|
||||
-- -- NATO Tank Platoons invading Gori.
|
||||
--
|
||||
-- -- Choose between different 'US Tank Platoon Template' configurations to be spawned for the
|
||||
-- -- 'US Tank Platoon Left', 'US Tank Platoon Middle' and 'US Tank Platoon Right' SPAWN objects.
|
||||
--
|
||||
-- -- Each new SPAWN will randomize the route, with a defined time interval of 200 seconds with 40% time variation (randomization) and
|
||||
-- -- with a limit set of maximum 12 Units alive simulteneously and 150 Groups to be spawned during the whole mission.
|
||||
--
|
||||
-- Spawn_US_PlatoonSet = SET_GROUP:New():FilterPrefixes( "US Tank Platoon Templates" ):FilterOnce()
|
||||
--
|
||||
-- --- Now use the Spawn_US_PlatoonSet to define the templates using InitRandomizeTemplateSet.
|
||||
-- Spawn_US_Platoon_Left = SPAWN:New( 'US Tank Platoon Left' ):InitLimit( 12, 150 ):Schedule( 200, 0.4 ):InitRandomizeTemplateSet( Spawn_US_PlatoonSet ):InitRandomizeRoute( 3, 3, 2000 )
|
||||
-- Spawn_US_Platoon_Middle = SPAWN:New( 'US Tank Platoon Middle' ):InitLimit( 12, 150 ):Schedule( 200, 0.4 ):InitRandomizeTemplateSet( Spawn_US_PlatoonSet ):InitRandomizeRoute( 3, 3, 2000 )
|
||||
-- Spawn_US_Platoon_Right = SPAWN:New( 'US Tank Platoon Right' ):InitLimit( 12, 150 ):Schedule( 200, 0.4 ):InitRandomizeTemplateSet( Spawn_US_PlatoonSet ):InitRandomizeRoute( 3, 3, 2000 )
|
||||
function SPAWN:InitRandomizeTemplateSet( SpawnTemplateSet ) -- R2.3
|
||||
self:F( { self.SpawnTemplatePrefix } )
|
||||
|
||||
self.SpawnTemplatePrefixTable = SpawnTemplateSet:GetSetNames()
|
||||
self.SpawnRandomizeTemplate = true
|
||||
|
||||
for SpawnGroupID = 1, self.SpawnMaxGroups do
|
||||
self:_RandomizeTemplate( SpawnGroupID )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Randomize templates to be used as the unit representatives for the Spawned group, defined by specifying the prefix names.
|
||||
-- This method becomes useful when you need to spawn groups with random templates of groups defined within the mission editor,
|
||||
-- but they will all follow the same Template route and have the same prefix name.
|
||||
-- In other words, this method randomizes between a defined set of groups the template to be used for each new spawn of a group.
|
||||
-- @param #SPAWN self
|
||||
-- @param #string SpawnTemplatePrefixes A string or a list of string that contains the prefixes of the groups that are possible unit representatives of the group to be spawned.
|
||||
-- @return #SPAWN
|
||||
-- @usage
|
||||
-- -- NATO Tank Platoons invading Gori.
|
||||
--
|
||||
-- -- Choose between different 'US Tank Platoon Templates' configurations to be spawned for the
|
||||
-- -- 'US Tank Platoon Left', 'US Tank Platoon Middle' and 'US Tank Platoon Right' SPAWN objects.
|
||||
--
|
||||
-- -- Each new SPAWN will randomize the route, with a defined time interval of 200 seconds with 40% time variation (randomization) and
|
||||
-- -- with a limit set of maximum 12 Units alive simulteneously and 150 Groups to be spawned during the whole mission.
|
||||
--
|
||||
-- Spawn_US_Platoon_Left = SPAWN:New( 'US Tank Platoon Left' ):InitLimit( 12, 150 ):Schedule( 200, 0.4 ):InitRandomizeTemplatePrefixes( "US Tank Platoon Templates" ):InitRandomizeRoute( 3, 3, 2000 )
|
||||
-- Spawn_US_Platoon_Middle = SPAWN:New( 'US Tank Platoon Middle' ):InitLimit( 12, 150 ):Schedule( 200, 0.4 ):InitRandomizeTemplatePrefixes( "US Tank Platoon Templates" ):InitRandomizeRoute( 3, 3, 2000 )
|
||||
-- Spawn_US_Platoon_Right = SPAWN:New( 'US Tank Platoon Right' ):InitLimit( 12, 150 ):Schedule( 200, 0.4 ):InitRandomizeTemplatePrefixes( "US Tank Platoon Templates" ):InitRandomizeRoute( 3, 3, 2000 )
|
||||
function SPAWN:InitRandomizeTemplatePrefixes( SpawnTemplatePrefixes ) --R2.3
|
||||
self:F( { self.SpawnTemplatePrefix } )
|
||||
|
||||
local SpawnTemplateSet = SET_GROUP:New():FilterPrefixes( SpawnTemplatePrefixes ):FilterOnce()
|
||||
|
||||
self:InitRandomizeTemplateSet( SpawnTemplateSet )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- When spawning a new group, make the grouping of the units according the InitGrouping setting.
|
||||
-- @param #SPAWN self
|
||||
-- @param #number Grouping Indicates the maximum amount of units in the group.
|
||||
-- @return #SPAWN
|
||||
function SPAWN:InitGrouping( Grouping ) -- R2.2
|
||||
self:F( { self.SpawnTemplatePrefix, Grouping } )
|
||||
|
||||
self.SpawnGrouping = Grouping
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
--TODO: Add example.
|
||||
--- This method provides the functionality to randomize the spawning of the Groups at a given list of zones of different types.
|
||||
-- @param #SPAWN self
|
||||
@@ -544,7 +731,13 @@ end
|
||||
-- @usage
|
||||
-- -- RU Su-34 - AI Ship Attack
|
||||
-- -- Re-SPAWN the Group(s) after each landing and Engine Shut-Down automatically.
|
||||
-- SpawnRU_SU34 = SPAWN:New( 'TF1 RU Su-34 Krymsk@AI - Attack Ships' ):Schedule( 2, 3, 1800, 0.4 ):SpawnUncontrolled():InitRandomizeRoute( 1, 1, 3000 ):RepeatOnEngineShutDown()
|
||||
-- SpawnRU_SU34 = SPAWN
|
||||
-- :New( 'Su-34' )
|
||||
-- :Schedule( 2, 3, 1800, 0.4 )
|
||||
-- :SpawnUncontrolled()
|
||||
-- :InitRandomizeRoute( 1, 1, 3000 )
|
||||
-- :InitRepeatOnEngineShutDown()
|
||||
--
|
||||
function SPAWN:InitRepeat()
|
||||
self:F( { self.SpawnTemplatePrefix, self.SpawnIndex } )
|
||||
|
||||
@@ -558,6 +751,16 @@ end
|
||||
--- Respawn group after landing.
|
||||
-- @param #SPAWN self
|
||||
-- @return #SPAWN self
|
||||
-- @usage
|
||||
-- -- RU Su-34 - AI Ship Attack
|
||||
-- -- Re-SPAWN the Group(s) after each landing and Engine Shut-Down automatically.
|
||||
-- SpawnRU_SU34 = SPAWN
|
||||
-- :New( 'Su-34' )
|
||||
-- :Schedule( 2, 3, 1800, 0.4 )
|
||||
-- :SpawnUncontrolled()
|
||||
-- :InitRandomizeRoute( 1, 1, 3000 )
|
||||
-- :InitRepeatOnLanding()
|
||||
--
|
||||
function SPAWN:InitRepeatOnLanding()
|
||||
self:F( { self.SpawnTemplatePrefix } )
|
||||
|
||||
@@ -572,6 +775,16 @@ end
|
||||
--- Respawn after landing when its engines have shut down.
|
||||
-- @param #SPAWN self
|
||||
-- @return #SPAWN self
|
||||
-- @usage
|
||||
-- -- RU Su-34 - AI Ship Attack
|
||||
-- -- Re-SPAWN the Group(s) after each landing and Engine Shut-Down automatically.
|
||||
-- SpawnRU_SU34 = SPAWN
|
||||
-- :New( 'Su-34' )
|
||||
-- :Schedule( 2, 3, 1800, 0.4 )
|
||||
-- :SpawnUncontrolled()
|
||||
-- :InitRandomizeRoute( 1, 1, 3000 )
|
||||
-- :InitRepeatOnEngineShutDown()
|
||||
--
|
||||
function SPAWN:InitRepeatOnEngineShutDown()
|
||||
self:F( { self.SpawnTemplatePrefix } )
|
||||
|
||||
@@ -588,7 +801,8 @@ end
|
||||
-- @param #SPAWN self
|
||||
-- @param #string SpawnCleanUpInterval The interval to check for inactive groups within seconds.
|
||||
-- @return #SPAWN self
|
||||
-- @usage Spawn_Helicopter:CleanUp( 20 ) -- CleanUp the spawning of the helicopters every 20 seconds when they become inactive.
|
||||
-- @usage
|
||||
-- Spawn_Helicopter:InitCleanUp( 20 ) -- CleanUp the spawning of the helicopters every 20 seconds when they become inactive.
|
||||
function SPAWN:InitCleanUp( SpawnCleanUpInterval )
|
||||
self:F( { self.SpawnTemplatePrefix, SpawnCleanUpInterval } )
|
||||
|
||||
@@ -615,7 +829,11 @@ end
|
||||
-- @return #SPAWN self
|
||||
-- @usage
|
||||
-- -- Define an array of Groups.
|
||||
-- Spawn_BE_Ground = SPAWN:New( 'BE Ground' ):InitLimit( 2, 24 ):InitArray( 90, "Diamond", 10, 100, 50 )
|
||||
-- Spawn_BE_Ground = SPAWN
|
||||
-- :New( 'BE Ground' )
|
||||
-- :InitLimit( 2, 24 )
|
||||
-- :InitArray( 90, 10, 100, 50 )
|
||||
--
|
||||
function SPAWN:InitArray( SpawnAngle, SpawnWidth, SpawnDeltaX, SpawnDeltaY )
|
||||
self:F( { self.SpawnTemplatePrefix, SpawnAngle, SpawnWidth, SpawnDeltaX, SpawnDeltaY } )
|
||||
|
||||
@@ -823,6 +1041,18 @@ function SPAWN:SpawnWithIndex( SpawnIndex )
|
||||
end
|
||||
end
|
||||
|
||||
-- If Heading is given, point all the units towards the given Heading.
|
||||
if self.SpawnInitHeadingMin then
|
||||
for UnitID = 1, #SpawnTemplate.units do
|
||||
SpawnTemplate.units[UnitID].heading = self.SpawnInitHeadingMax and math.random( self.SpawnInitHeadingMin, self.SpawnInitHeadingMax ) or self.SpawnInitHeadingMin
|
||||
end
|
||||
end
|
||||
|
||||
SpawnTemplate.CategoryID = self.SpawnInitCategory or SpawnTemplate.CategoryID
|
||||
SpawnTemplate.CountryID = self.SpawnInitCountry or SpawnTemplate.CountryID
|
||||
SpawnTemplate.CoalitionID = self.SpawnInitCoalition or SpawnTemplate.CoalitionID
|
||||
|
||||
|
||||
if SpawnTemplate.CategoryID == Group.Category.HELICOPTER or SpawnTemplate.CategoryID == Group.Category.AIRPLANE then
|
||||
if SpawnTemplate.route.points[1].type == "TakeOffParking" then
|
||||
SpawnTemplate.uncontrolled = self.SpawnUnControlled
|
||||
@@ -855,7 +1085,8 @@ function SPAWN:SpawnWithIndex( SpawnIndex )
|
||||
|
||||
-- If there is a SpawnFunction hook defined, call it.
|
||||
if self.SpawnFunctionHook then
|
||||
self.SpawnFunctionHook( self.SpawnGroups[self.SpawnIndex].Group, unpack( self.SpawnFunctionArguments ) )
|
||||
-- delay calling this for .1 seconds so that it hopefully comes after the BIRTH event of the group.
|
||||
self.SpawnHookScheduler:Schedule( nil, self.SpawnFunctionHook, { self.SpawnGroups[self.SpawnIndex].Group, unpack( self.SpawnFunctionArguments)}, 0.1 )
|
||||
end
|
||||
-- TODO: Need to fix this by putting an "R" in the name of the group when the group repeats.
|
||||
--if self.Repeat then
|
||||
@@ -954,6 +1185,151 @@ function SPAWN:OnSpawnGroup( SpawnCallBackFunction, ... )
|
||||
return self
|
||||
end
|
||||
|
||||
--- Will spawn a group at an @{Airbase}.
|
||||
-- This method is mostly advisable to be used if you want to simulate spawning units at an airbase.
|
||||
-- Note that each point in the route assigned to the spawning group is reset to the point of the spawn.
|
||||
-- You can use the returned group to further define the route to be followed.
|
||||
--
|
||||
-- The @{Airbase#AIRBASE} object must refer to a valid airbase known in the sim.
|
||||
-- You can use the following enumerations to search for the pre-defined airbases on the current known maps of DCS:
|
||||
--
|
||||
-- * @{Airbase#AIRBASE.Caucasus}: The airbases on the Caucasus map.
|
||||
-- * @{Airbase#AIRBASE.Nevada}: The airbases on the Nevada (NTTR) map.
|
||||
-- * @{Airbase#AIRBASE.Normandy}: The airbases on the Normandy map.
|
||||
--
|
||||
-- Use the method @{Airbase#AIRBASE.FindByName}() to retrieve the airbase object.
|
||||
-- The known AIRBASE objects are automatically imported at mission start by MOOSE.
|
||||
-- Therefore, there isn't any New() constructor defined for AIRBASE objects.
|
||||
--
|
||||
-- Ships and Farps are added within the mission, and are therefore not known.
|
||||
-- For these AIRBASE objects, there isn't an @{Airbase#AIRBASE} enumeration defined.
|
||||
-- You need to provide the **exact name** of the airbase as the parameter to the @{Airbase#AIRBASE.FindByName}() method!
|
||||
--
|
||||
-- @param #SPAWN self
|
||||
-- @param Wrapper.Airbase#AIRBASE SpawnAirbase The @{Airbase} where to spawn the group.
|
||||
-- @param #SPAWN.Takeoff Takeoff (optional) The location and takeoff method. Default is Hot.
|
||||
-- @param #number TakeoffAltitude (optional) The altitude above the ground.
|
||||
-- @return Wrapper.Group#GROUP that was spawned.
|
||||
-- @return #nil Nothing was spawned.
|
||||
-- @usage
|
||||
-- Spawn_Plane = SPAWN:New( "Plane" )
|
||||
-- Spawn_Plane:SpawnAtAirbase( AIRBASE:FindByName( AIRBASE.Caucasus.Krymsk ), SPAWN.Takeoff.Cold )
|
||||
-- Spawn_Plane:SpawnAtAirbase( AIRBASE:FindByName( AIRBASE.Caucasus.Krymsk ), SPAWN.Takeoff.Hot )
|
||||
-- Spawn_Plane:SpawnAtAirbase( AIRBASE:FindByName( AIRBASE.Caucasus.Krymsk ), SPAWN.Takeoff.Runway )
|
||||
--
|
||||
-- Spawn_Plane:SpawnAtAirbase( AIRBASE:FindByName( "Carrier" ), SPAWN.Takeoff.Cold )
|
||||
--
|
||||
-- Spawn_Heli = SPAWN:New( "Heli")
|
||||
--
|
||||
-- Spawn_Heli:SpawnAtAirbase( AIRBASE:FindByName( "FARP Cold" ), SPAWN.Takeoff.Cold )
|
||||
-- Spawn_Heli:SpawnAtAirbase( AIRBASE:FindByName( "FARP Hot" ), SPAWN.Takeoff.Hot )
|
||||
-- Spawn_Heli:SpawnAtAirbase( AIRBASE:FindByName( "FARP Runway" ), SPAWN.Takeoff.Runway )
|
||||
-- Spawn_Heli:SpawnAtAirbase( AIRBASE:FindByName( "FARP Air" ), SPAWN.Takeoff.Air )
|
||||
--
|
||||
-- Spawn_Heli:SpawnAtAirbase( AIRBASE:FindByName( "Carrier" ), SPAWN.Takeoff.Cold )
|
||||
--
|
||||
function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude ) -- R2.2
|
||||
self:F( { self.SpawnTemplatePrefix, SpawnAirbase, Takeoff, TakeoffAltitude } )
|
||||
|
||||
local PointVec3 = SpawnAirbase:GetPointVec3()
|
||||
self:T2(PointVec3)
|
||||
|
||||
Takeoff = Takeoff or SPAWN.Takeoff.Hot
|
||||
|
||||
if self:_GetSpawnIndex( self.SpawnIndex + 1 ) then
|
||||
|
||||
local SpawnTemplate = self.SpawnGroups[self.SpawnIndex].SpawnTemplate
|
||||
|
||||
if SpawnTemplate then
|
||||
|
||||
self:T( { "Current point of ", self.SpawnTemplatePrefix, SpawnAirbase } )
|
||||
|
||||
local SpawnPoint = SpawnTemplate.route.points[1]
|
||||
|
||||
-- These are only for ships.
|
||||
SpawnPoint.linkUnit = nil
|
||||
SpawnPoint.helipadId = nil
|
||||
SpawnPoint.airdromeId = nil
|
||||
|
||||
local AirbaseID = SpawnAirbase:GetID()
|
||||
local AirbaseCategory = SpawnAirbase:GetDesc().category
|
||||
self:F( { AirbaseCategory = AirbaseCategory } )
|
||||
|
||||
if AirbaseCategory == Airbase.Category.SHIP then
|
||||
SpawnPoint.linkUnit = AirbaseID
|
||||
SpawnPoint.helipadId = AirbaseID
|
||||
elseif AirbaseCategory == Airbase.Category.HELIPAD then
|
||||
SpawnPoint.linkUnit = AirbaseID
|
||||
SpawnPoint.helipadId = AirbaseID
|
||||
elseif AirbaseCategory == Airbase.Category.AIRDROME then
|
||||
SpawnPoint.airdromeId = AirbaseID
|
||||
end
|
||||
|
||||
SpawnPoint.alt = 0
|
||||
|
||||
SpawnPoint.type = GROUPTEMPLATE.Takeoff[Takeoff][1] -- type
|
||||
SpawnPoint.action = GROUPTEMPLATE.Takeoff[Takeoff][2] -- action
|
||||
|
||||
|
||||
-- Translate the position of the Group Template to the Vec3.
|
||||
for UnitID = 1, #SpawnTemplate.units do
|
||||
self:T( 'Before Translation SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y )
|
||||
|
||||
-- These cause a lot of confusion.
|
||||
local UnitTemplate = SpawnTemplate.units[UnitID]
|
||||
|
||||
UnitTemplate.parking = nil
|
||||
UnitTemplate.parking_id = nil
|
||||
UnitTemplate.alt = 0
|
||||
|
||||
local SX = UnitTemplate.x
|
||||
local SY = UnitTemplate.y
|
||||
local BX = SpawnPoint.x
|
||||
local BY = SpawnPoint.y
|
||||
local TX = PointVec3.x + ( SX - BX )
|
||||
local TY = PointVec3.z + ( SY - BY )
|
||||
|
||||
UnitTemplate.x = TX
|
||||
UnitTemplate.y = TY
|
||||
|
||||
if Takeoff == GROUP.Takeoff.Air then
|
||||
UnitTemplate.alt = PointVec3.y + ( TakeoffAltitude or 200 )
|
||||
--else
|
||||
-- UnitTemplate.alt = PointVec3.y + 10
|
||||
end
|
||||
self:T( 'After Translation SpawnTemplate.units['..UnitID..'].x = ' .. UnitTemplate.x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. UnitTemplate.y )
|
||||
end
|
||||
|
||||
SpawnPoint.x = PointVec3.x
|
||||
SpawnPoint.y = PointVec3.z
|
||||
|
||||
if Takeoff == GROUP.Takeoff.Air then
|
||||
SpawnPoint.alt = PointVec3.y + ( TakeoffAltitude or 200 )
|
||||
--else
|
||||
-- SpawnPoint.alt = PointVec3.y + 10
|
||||
end
|
||||
|
||||
SpawnTemplate.x = PointVec3.x
|
||||
SpawnTemplate.y = PointVec3.z
|
||||
|
||||
local GroupSpawned = self:SpawnWithIndex( self.SpawnIndex )
|
||||
|
||||
-- When spawned in the air, we need to generate a Takeoff Event
|
||||
|
||||
if Takeoff == GROUP.Takeoff.Air then
|
||||
for UnitID, UnitSpawned in pairs( GroupSpawned:GetUnits() ) do
|
||||
SCHEDULER:New( nil, BASE.CreateEventTakeoff, { GroupSpawned, timer.getTime(), UnitSpawned:GetDCSObject() } , 1 )
|
||||
end
|
||||
end
|
||||
|
||||
return GroupSpawned
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Will spawn a group from a Vec3 in 3D space.
|
||||
-- This method is mostly advisable to be used if you want to simulate spawning units in the air, like helicopters or airplanes.
|
||||
@@ -982,29 +1358,40 @@ function SPAWN:SpawnFromVec3( Vec3, SpawnIndex )
|
||||
if SpawnTemplate then
|
||||
|
||||
self:T( { "Current point of ", self.SpawnTemplatePrefix, Vec3 } )
|
||||
|
||||
local TemplateHeight = SpawnTemplate.route and SpawnTemplate.route.points[1].alt or nil
|
||||
|
||||
SpawnTemplate.route = SpawnTemplate.route or {}
|
||||
SpawnTemplate.route.points = SpawnTemplate.route.points or {}
|
||||
SpawnTemplate.route.points[1] = SpawnTemplate.route.points[1] or {}
|
||||
SpawnTemplate.route.points[1].x = SpawnTemplate.route.points[1].x or 0
|
||||
SpawnTemplate.route.points[1].y = SpawnTemplate.route.points[1].y or 0
|
||||
|
||||
-- Translate the position of the Group Template to the Vec3.
|
||||
for UnitID = 1, #SpawnTemplate.units do
|
||||
self:T( 'Before Translation SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y )
|
||||
--self:T( 'Before Translation SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y )
|
||||
local UnitTemplate = SpawnTemplate.units[UnitID]
|
||||
local SX = UnitTemplate.x
|
||||
local SY = UnitTemplate.y
|
||||
local SX = UnitTemplate.x or 0
|
||||
local SY = UnitTemplate.y or 0
|
||||
local BX = SpawnTemplate.route.points[1].x
|
||||
local BY = SpawnTemplate.route.points[1].y
|
||||
local TX = Vec3.x + ( SX - BX )
|
||||
local TY = Vec3.z + ( SY - BY )
|
||||
SpawnTemplate.units[UnitID].x = TX
|
||||
SpawnTemplate.units[UnitID].y = TY
|
||||
SpawnTemplate.units[UnitID].alt = Vec3.y
|
||||
if SpawnTemplate.CategoryID ~= Group.Category.SHIP then
|
||||
SpawnTemplate.units[UnitID].alt = Vec3.y or TemplateHeight
|
||||
end
|
||||
self:T( 'After Translation SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y )
|
||||
end
|
||||
|
||||
SpawnTemplate.route.points[1].x = Vec3.x
|
||||
SpawnTemplate.route.points[1].y = Vec3.z
|
||||
SpawnTemplate.route.points[1].alt = Vec3.y
|
||||
|
||||
if SpawnTemplate.CategoryID ~= Group.Category.SHIP then
|
||||
SpawnTemplate.route.points[1].alt = Vec3.y or TemplateHeight
|
||||
end
|
||||
SpawnTemplate.x = Vec3.x
|
||||
SpawnTemplate.y = Vec3.z
|
||||
SpawnTemplate.alt = Vec3.y or TemplateHeight
|
||||
|
||||
return self:SpawnWithIndex( self.SpawnIndex )
|
||||
end
|
||||
@@ -1013,36 +1400,135 @@ function SPAWN:SpawnFromVec3( Vec3, SpawnIndex )
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Will spawn a group from a Coordinate in 3D space.
|
||||
-- This method is mostly advisable to be used if you want to simulate spawning units in the air, like helicopters or airplanes.
|
||||
-- Note that each point in the route assigned to the spawning group is reset to the point of the spawn.
|
||||
-- You can use the returned group to further define the route to be followed.
|
||||
-- @param #SPAWN self
|
||||
-- @param Core.Point#Coordinate Coordinate The Coordinate coordinates where to spawn the group.
|
||||
-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone.
|
||||
-- @return Wrapper.Group#GROUP that was spawned.
|
||||
-- @return #nil Nothing was spawned.
|
||||
function SPAWN:SpawnFromCoordinate( Coordinate, SpawnIndex )
|
||||
self:F( { self.SpawnTemplatePrefix, SpawnIndex } )
|
||||
|
||||
return self:SpawnFromVec3( Coordinate:GetVec3(), SpawnIndex )
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Will spawn a group from a PointVec3 in 3D space.
|
||||
-- This method is mostly advisable to be used if you want to simulate spawning units in the air, like helicopters or airplanes.
|
||||
-- Note that each point in the route assigned to the spawning group is reset to the point of the spawn.
|
||||
-- You can use the returned group to further define the route to be followed.
|
||||
-- @param #SPAWN self
|
||||
-- @param Core.Point#POINT_VEC3 PointVec3 The PointVec3 coordinates where to spawn the group.
|
||||
-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone.
|
||||
-- @return Wrapper.Group#GROUP that was spawned.
|
||||
-- @return #nil Nothing was spawned.
|
||||
-- @usage
|
||||
--
|
||||
-- local SpawnPointVec3 = ZONE:New( ZoneName ):GetPointVec3( 2000 ) -- Get the center of the ZONE object at 2000 meters from the ground.
|
||||
--
|
||||
-- -- Spawn at the zone center position at 2000 meters from the ground!
|
||||
-- SpawnAirplanes:SpawnFromPointVec3( SpawnPointVec3 )
|
||||
--
|
||||
function SPAWN:SpawnFromPointVec3( PointVec3, SpawnIndex )
|
||||
self:F( { self.SpawnTemplatePrefix, SpawnIndex } )
|
||||
|
||||
return self:SpawnFromVec3( PointVec3:GetVec3(), SpawnIndex )
|
||||
end
|
||||
|
||||
|
||||
--- Will spawn a group from a Vec2 in 3D space.
|
||||
-- This method is mostly advisable to be used if you want to simulate spawning groups on the ground from air units, like vehicles.
|
||||
-- Note that each point in the route assigned to the spawning group is reset to the point of the spawn.
|
||||
-- You can use the returned group to further define the route to be followed.
|
||||
-- @param #SPAWN self
|
||||
-- @param Dcs.DCSTypes#Vec2 Vec2 The Vec2 coordinates where to spawn the group.
|
||||
-- @param #number MinHeight (optional) The minimum height to spawn an airborne group into the zone.
|
||||
-- @param #number MaxHeight (optional) The maximum height to spawn an airborne group into the zone.
|
||||
-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone.
|
||||
-- @return Wrapper.Group#GROUP that was spawned.
|
||||
-- @return #nil Nothing was spawned.
|
||||
function SPAWN:SpawnFromVec2( Vec2, SpawnIndex )
|
||||
self:F( { self.SpawnTemplatePrefix, Vec2, SpawnIndex } )
|
||||
-- @usage
|
||||
--
|
||||
-- local SpawnVec2 = ZONE:New( ZoneName ):GetVec2()
|
||||
--
|
||||
-- -- Spawn at the zone center position at the height specified in the ME of the group template!
|
||||
-- SpawnAirplanes:SpawnFromVec2( SpawnVec2 )
|
||||
--
|
||||
-- -- Spawn from the static position at the height randomized between 2000 and 4000 meters.
|
||||
-- SpawnAirplanes:SpawnFromVec2( SpawnVec2, 2000, 4000 )
|
||||
--
|
||||
function SPAWN:SpawnFromVec2( Vec2, MinHeight, MaxHeight, SpawnIndex )
|
||||
self:F( { self.SpawnTemplatePrefix, self.SpawnIndex, Vec2, MinHeight, MaxHeight, SpawnIndex } )
|
||||
|
||||
local PointVec2 = POINT_VEC2:NewFromVec2( Vec2 )
|
||||
return self:SpawnFromVec3( PointVec2:GetVec3(), SpawnIndex )
|
||||
local Height = nil
|
||||
|
||||
if MinHeight and MaxHeight then
|
||||
Height = math.random( MinHeight, MaxHeight)
|
||||
end
|
||||
|
||||
return self:SpawnFromVec3( { x = Vec2.x, y = Height, z = Vec2.y }, SpawnIndex ) -- y can be nil. In this case, spawn on the ground for vehicles, and in the template altitude for air.
|
||||
end
|
||||
|
||||
|
||||
--- Will spawn a group from a POINT_VEC2 in 3D space.
|
||||
-- This method is mostly advisable to be used if you want to simulate spawning groups on the ground from air units, like vehicles.
|
||||
-- Note that each point in the route assigned to the spawning group is reset to the point of the spawn.
|
||||
-- You can use the returned group to further define the route to be followed.
|
||||
-- @param #SPAWN self
|
||||
-- @param Core.Point#POINT_VEC2 PointVec2 The PointVec2 coordinates where to spawn the group.
|
||||
-- @param #number MinHeight (optional) The minimum height to spawn an airborne group into the zone.
|
||||
-- @param #number MaxHeight (optional) The maximum height to spawn an airborne group into the zone.
|
||||
-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone.
|
||||
-- @return Wrapper.Group#GROUP that was spawned.
|
||||
-- @return #nil Nothing was spawned.
|
||||
-- @usage
|
||||
--
|
||||
-- local SpawnPointVec2 = ZONE:New( ZoneName ):GetPointVec2()
|
||||
--
|
||||
-- -- Spawn at the zone center position at the height specified in the ME of the group template!
|
||||
-- SpawnAirplanes:SpawnFromPointVec2( SpawnPointVec2 )
|
||||
--
|
||||
-- -- Spawn from the static position at the height randomized between 2000 and 4000 meters.
|
||||
-- SpawnAirplanes:SpawnFromPointVec2( SpawnPointVec2, 2000, 4000 )
|
||||
--
|
||||
function SPAWN:SpawnFromPointVec2( PointVec2, MinHeight, MaxHeight, SpawnIndex )
|
||||
self:F( { self.SpawnTemplatePrefix, self.SpawnIndex } )
|
||||
|
||||
return self:SpawnFromVec2( PointVec2:GetVec2(), MinHeight, MaxHeight, SpawnIndex )
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Will spawn a group from a hosting unit. This method is mostly advisable to be used if you want to simulate spawning from air units, like helicopters, which are dropping infantry into a defined Landing Zone.
|
||||
-- Note that each point in the route assigned to the spawning group is reset to the point of the spawn.
|
||||
-- You can use the returned group to further define the route to be followed.
|
||||
-- @param #SPAWN self
|
||||
-- @param Wrapper.Unit#UNIT HostUnit The air or ground unit dropping or unloading the group.
|
||||
-- @param #number MinHeight (optional) The minimum height to spawn an airborne group into the zone.
|
||||
-- @param #number MaxHeight (optional) The maximum height to spawn an airborne group into the zone.
|
||||
-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone.
|
||||
-- @return Wrapper.Group#GROUP that was spawned.
|
||||
-- @return #nil Nothing was spawned.
|
||||
function SPAWN:SpawnFromUnit( HostUnit, SpawnIndex )
|
||||
self:F( { self.SpawnTemplatePrefix, HostUnit, SpawnIndex } )
|
||||
-- @usage
|
||||
--
|
||||
-- local SpawnStatic = STATIC:FindByName( StaticName )
|
||||
--
|
||||
-- -- Spawn from the static position at the height specified in the ME of the group template!
|
||||
-- SpawnAirplanes:SpawnFromUnit( SpawnStatic )
|
||||
--
|
||||
-- -- Spawn from the static position at the height randomized between 2000 and 4000 meters.
|
||||
-- SpawnAirplanes:SpawnFromUnit( SpawnStatic, 2000, 4000 )
|
||||
--
|
||||
function SPAWN:SpawnFromUnit( HostUnit, MinHeight, MaxHeight, SpawnIndex )
|
||||
self:F( { self.SpawnTemplatePrefix, HostUnit, MinHeight, MaxHeight, SpawnIndex } )
|
||||
|
||||
if HostUnit and HostUnit:IsAlive() ~= nil then -- and HostUnit:getUnit(1):inAir() == false then
|
||||
return self:SpawnFromVec3( HostUnit:GetVec3(), SpawnIndex )
|
||||
return self:SpawnFromVec2( HostUnit:GetVec2(), MinHeight, MaxHeight, SpawnIndex )
|
||||
end
|
||||
|
||||
return nil
|
||||
@@ -1052,14 +1538,26 @@ end
|
||||
-- You can use the returned group to further define the route to be followed.
|
||||
-- @param #SPAWN self
|
||||
-- @param Wrapper.Static#STATIC HostStatic The static dropping or unloading the group.
|
||||
-- @param #number MinHeight (optional) The minimum height to spawn an airborne group into the zone.
|
||||
-- @param #number MaxHeight (optional) The maximum height to spawn an airborne group into the zone.
|
||||
-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone.
|
||||
-- @return Wrapper.Group#GROUP that was spawned.
|
||||
-- @return #nil Nothing was spawned.
|
||||
function SPAWN:SpawnFromStatic( HostStatic, SpawnIndex )
|
||||
self:F( { self.SpawnTemplatePrefix, HostStatic, SpawnIndex } )
|
||||
-- @usage
|
||||
--
|
||||
-- local SpawnStatic = STATIC:FindByName( StaticName )
|
||||
--
|
||||
-- -- Spawn from the static position at the height specified in the ME of the group template!
|
||||
-- SpawnAirplanes:SpawnFromStatic( SpawnStatic )
|
||||
--
|
||||
-- -- Spawn from the static position at the height randomized between 2000 and 4000 meters.
|
||||
-- SpawnAirplanes:SpawnFromStatic( SpawnStatic, 2000, 4000 )
|
||||
--
|
||||
function SPAWN:SpawnFromStatic( HostStatic, MinHeight, MaxHeight, SpawnIndex )
|
||||
self:F( { self.SpawnTemplatePrefix, HostStatic, MinHeight, MaxHeight, SpawnIndex } )
|
||||
|
||||
if HostStatic and HostStatic:IsAlive() then
|
||||
return self:SpawnFromVec3( HostStatic:GetVec3(), SpawnIndex )
|
||||
return self:SpawnFromVec2( HostStatic:GetVec2(), MinHeight, MaxHeight, SpawnIndex )
|
||||
end
|
||||
|
||||
return nil
|
||||
@@ -1072,17 +1570,38 @@ end
|
||||
-- @param #SPAWN self
|
||||
-- @param Core.Zone#ZONE Zone The zone where the group is to be spawned.
|
||||
-- @param #boolean RandomizeGroup (optional) Randomization of the @{Group} position in the zone.
|
||||
-- @param #number MinHeight (optional) The minimum height to spawn an airborne group into the zone.
|
||||
-- @param #number MaxHeight (optional) The maximum height to spawn an airborne group into the zone.
|
||||
-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone.
|
||||
-- @return Wrapper.Group#GROUP that was spawned.
|
||||
-- @return #nil when nothing was spawned.
|
||||
function SPAWN:SpawnInZone( Zone, RandomizeGroup, SpawnIndex )
|
||||
self:F( { self.SpawnTemplatePrefix, Zone, RandomizeGroup, SpawnIndex } )
|
||||
-- @usage
|
||||
--
|
||||
-- local SpawnZone = ZONE:New( ZoneName )
|
||||
--
|
||||
-- -- Spawn at the zone center position at the height specified in the ME of the group template!
|
||||
-- SpawnAirplanes:SpawnInZone( SpawnZone )
|
||||
--
|
||||
-- -- Spawn in the zone at a random position at the height specified in the Me of the group template.
|
||||
-- SpawnAirplanes:SpawnInZone( SpawnZone, true )
|
||||
--
|
||||
-- -- Spawn in the zone at a random position at the height randomized between 2000 and 4000 meters.
|
||||
-- SpawnAirplanes:SpawnInZone( SpawnZone, true, 2000, 4000 )
|
||||
--
|
||||
-- -- Spawn at the zone center position at the height randomized between 2000 and 4000 meters.
|
||||
-- SpawnAirplanes:SpawnInZone( SpawnZone, false, 2000, 4000 )
|
||||
--
|
||||
-- -- Spawn at the zone center position at the height randomized between 2000 and 4000 meters.
|
||||
-- SpawnAirplanes:SpawnInZone( SpawnZone, nil, 2000, 4000 )
|
||||
--
|
||||
function SPAWN:SpawnInZone( Zone, RandomizeGroup, MinHeight, MaxHeight, SpawnIndex )
|
||||
self:F( { self.SpawnTemplatePrefix, Zone, RandomizeGroup, MinHeight, MaxHeight, SpawnIndex } )
|
||||
|
||||
if Zone then
|
||||
if RandomizeGroup then
|
||||
return self:SpawnFromVec2( Zone:GetRandomVec2(), SpawnIndex )
|
||||
return self:SpawnFromVec2( Zone:GetRandomVec2(), MinHeight, MaxHeight, SpawnIndex )
|
||||
else
|
||||
return self:SpawnFromVec2( Zone:GetVec2(), SpawnIndex )
|
||||
return self:SpawnFromVec2( Zone:GetVec2(), MinHeight, MaxHeight, SpawnIndex )
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1109,6 +1628,19 @@ function SPAWN:InitUnControlled( UnControlled )
|
||||
end
|
||||
|
||||
|
||||
--- Get the Coordinate of the Group that is Late Activated as the template for the SPAWN object.
|
||||
-- @param #SPAWN self
|
||||
-- @return Core.Point#COORDINATE The Coordinate
|
||||
function SPAWN:GetCoordinate()
|
||||
|
||||
local LateGroup = GROUP:FindByName( self.SpawnTemplatePrefix )
|
||||
if LateGroup then
|
||||
return LateGroup:GetCoordinate()
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Will return the SpawnGroupName either with with a specific count number or without any count.
|
||||
-- @param #SPAWN self
|
||||
@@ -1348,7 +1880,10 @@ function SPAWN:_GetTemplate( SpawnTemplatePrefix )
|
||||
|
||||
local SpawnTemplate = nil
|
||||
|
||||
SpawnTemplate = routines.utils.deepCopy( _DATABASE.Templates.Groups[SpawnTemplatePrefix].Template )
|
||||
local Template = _DATABASE.Templates.Groups[SpawnTemplatePrefix].Template
|
||||
self:F( { Template = Template } )
|
||||
|
||||
SpawnTemplate = UTILS.DeepCopy( _DATABASE.Templates.Groups[SpawnTemplatePrefix].Template )
|
||||
|
||||
if SpawnTemplate == nil then
|
||||
error( 'No Template returned for SpawnTemplatePrefix = ' .. SpawnTemplatePrefix )
|
||||
@@ -1367,10 +1902,15 @@ end
|
||||
-- @param #string SpawnTemplatePrefix
|
||||
-- @param #number SpawnIndex
|
||||
-- @return #SPAWN self
|
||||
function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex )
|
||||
function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) --R2.2
|
||||
self:F( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix } )
|
||||
|
||||
local SpawnTemplate = self:_GetTemplate( SpawnTemplatePrefix )
|
||||
-- if not self.SpawnTemplate then
|
||||
-- self.SpawnTemplate = self:_GetTemplate( SpawnTemplatePrefix )
|
||||
-- end
|
||||
|
||||
local SpawnTemplate = self:_GetTemplate( SpawnTemplatePrefix )
|
||||
--local SpawnTemplate = self.SpawnTemplate
|
||||
SpawnTemplate.name = self:SpawnGroupName( SpawnIndex )
|
||||
|
||||
SpawnTemplate.groupId = nil
|
||||
@@ -1382,6 +1922,23 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex )
|
||||
SpawnTemplate.visible = false
|
||||
end
|
||||
|
||||
if self.SpawnGrouping then
|
||||
local UnitAmount = #SpawnTemplate.units
|
||||
self:F( { UnitAmount = UnitAmount, SpawnGrouping = self.SpawnGrouping } )
|
||||
if UnitAmount > self.SpawnGrouping then
|
||||
for UnitID = self.SpawnGrouping + 1, UnitAmount do
|
||||
SpawnTemplate.units[UnitID] = nil
|
||||
end
|
||||
else
|
||||
if UnitAmount < self.SpawnGrouping then
|
||||
for UnitID = UnitAmount + 1, self.SpawnGrouping do
|
||||
SpawnTemplate.units[UnitID] = UTILS.DeepCopy( SpawnTemplate.units[1] )
|
||||
SpawnTemplate.units[UnitID].unitId = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if self.SpawnInitKeepUnitNames == false then
|
||||
for UnitID = 1, #SpawnTemplate.units do
|
||||
SpawnTemplate.units[UnitID].name = string.format( SpawnTemplate.name .. '-%02d', UnitID )
|
||||
@@ -1446,7 +2003,7 @@ function SPAWN:_RandomizeTemplate( SpawnIndex )
|
||||
if self.SpawnRandomizeTemplate then
|
||||
self.SpawnGroups[SpawnIndex].SpawnTemplatePrefix = self.SpawnTemplatePrefixTable[ math.random( 1, #self.SpawnTemplatePrefixTable ) ]
|
||||
self.SpawnGroups[SpawnIndex].SpawnTemplate = self:_Prepare( self.SpawnGroups[SpawnIndex].SpawnTemplatePrefix, SpawnIndex )
|
||||
self.SpawnGroups[SpawnIndex].SpawnTemplate.route = routines.utils.deepCopy( self.SpawnTemplate.route )
|
||||
self.SpawnGroups[SpawnIndex].SpawnTemplate.route = UTILS.DeepCopy( self.SpawnTemplate.route )
|
||||
self.SpawnGroups[SpawnIndex].SpawnTemplate.x = self.SpawnTemplate.x
|
||||
self.SpawnGroups[SpawnIndex].SpawnTemplate.y = self.SpawnTemplate.y
|
||||
self.SpawnGroups[SpawnIndex].SpawnTemplate.start_time = self.SpawnTemplate.start_time
|
||||
@@ -1565,6 +2122,7 @@ function SPAWN:_GetSpawnIndex( SpawnIndex )
|
||||
|
||||
if ( self.SpawnMaxGroups == 0 ) or ( SpawnIndex <= self.SpawnMaxGroups ) then
|
||||
if ( self.SpawnMaxUnitsAlive == 0 ) or ( self.AliveUnits + #self.SpawnTemplate.units <= self.SpawnMaxUnitsAlive ) or self.UnControlled == true then
|
||||
self:F( { SpawnCount = self.SpawnCount, SpawnIndex = SpawnIndex } )
|
||||
if SpawnIndex and SpawnIndex >= self.SpawnCount + 1 then
|
||||
self.SpawnCount = self.SpawnCount + 1
|
||||
SpawnIndex = self.SpawnCount
|
||||
@@ -1,12 +1,12 @@
|
||||
--- (R2.1) **Core** -- Spawn dynamically new STATICs in your missions.
|
||||
--- **Core** -- Spawn dynamically new STATICs in your missions.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ====
|
||||
-- ===
|
||||
--
|
||||
-- SPAWNSTATIC spawns static structures in your missions dynamically. See below the SPAWNSTATIC class documentation.
|
||||
--
|
||||
-- ====
|
||||
-- ===
|
||||
--
|
||||
-- # Demo Missions
|
||||
--
|
||||
@@ -16,32 +16,18 @@
|
||||
--
|
||||
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
|
||||
--
|
||||
-- ====
|
||||
-- ===
|
||||
--
|
||||
-- # YouTube Channel
|
||||
--
|
||||
-- ### [SPAWNSTATIC YouTube Channel]()
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- # **API CHANGE HISTORY**
|
||||
--
|
||||
-- The underlying change log documents the API changes. Please read this carefully. The following notation is used:
|
||||
--
|
||||
-- * **Added** parts are expressed in bold type face.
|
||||
-- * _Removed_ parts are expressed in italic type face.
|
||||
--
|
||||
-- Hereby the change log:
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # **AUTHORS and CONTRIBUTIONS**
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ### Authors:
|
||||
--
|
||||
-- * **FlightControl**: Design & Programming
|
||||
-- ===
|
||||
--
|
||||
-- @module SpawnStatic
|
||||
|
||||
@@ -95,14 +81,16 @@ SPAWNSTATIC = {
|
||||
-- @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
|
||||
function SPAWNSTATIC:NewFromStatic( SpawnTemplatePrefix, SpawnCountryID ) --R2.1
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC
|
||||
self:F( { SpawnTemplatePrefix } )
|
||||
|
||||
local TemplateStatic = StaticObject.getByName( SpawnTemplatePrefix )
|
||||
local TemplateStatic, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticUnitTemplate( SpawnTemplatePrefix )
|
||||
if TemplateStatic then
|
||||
self.SpawnTemplatePrefix = SpawnTemplatePrefix
|
||||
self.CountryID = CountryID
|
||||
self.CountryID = SpawnCountryID or CountryID
|
||||
self.CategoryID = CategoryID
|
||||
self.CoalitionID = CoalitionID
|
||||
self.SpawnIndex = 0
|
||||
else
|
||||
error( "SPAWNSTATIC:New: There is no group declared in the mission editor with SpawnTemplatePrefix = '" .. SpawnTemplatePrefix .. "'" )
|
||||
@@ -131,6 +119,40 @@ function SPAWNSTATIC:NewFromType( SpawnTypeName, SpawnShapeName, SpawnCategory,
|
||||
end
|
||||
|
||||
|
||||
--- Creates a new @{Static} at the original position.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @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:Spawn( Heading, NewName ) --R2.3
|
||||
self:F( { Heading, NewName } )
|
||||
|
||||
local StaticTemplate = _DATABASE:GetStaticUnitTemplate( self.SpawnTemplatePrefix )
|
||||
|
||||
if StaticTemplate then
|
||||
|
||||
local CountryID = self.CountryID
|
||||
local CountryName = _DATABASE.COUNTRY_NAME[CountryID]
|
||||
|
||||
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( CountryID, StaticTemplate )
|
||||
|
||||
self.SpawnIndex = self.SpawnIndex + 1
|
||||
|
||||
return Static
|
||||
end
|
||||
|
||||
return nil
|
||||
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.
|
||||
@@ -140,27 +162,101 @@ end
|
||||
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 )
|
||||
|
||||
if StaticTemplate then
|
||||
|
||||
local CountryID = self.CountryID
|
||||
local CountryName = _DATABASE.COUNTRY_NAME[CountryID]
|
||||
|
||||
StaticTemplate.x = PointVec2.x
|
||||
StaticTemplate.y = PointVec2.z
|
||||
|
||||
StaticTemplate.units = nil
|
||||
StaticTemplate.route = nil
|
||||
StaticTemplate.groupId = nil
|
||||
|
||||
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( CountryID, StaticTemplate )
|
||||
|
||||
self.SpawnIndex = self.SpawnIndex + 1
|
||||
|
||||
return Static
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Creates the original @{Static} at 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:ReSpawn()
|
||||
|
||||
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
|
||||
if StaticTemplate then
|
||||
|
||||
return Static
|
||||
local CountryID = self.CountryID
|
||||
local CountryName = _DATABASE.COUNTRY_NAME[CountryID]
|
||||
|
||||
StaticTemplate.units = nil
|
||||
StaticTemplate.route = nil
|
||||
StaticTemplate.groupId = nil
|
||||
|
||||
StaticTemplate.CountryID = nil
|
||||
StaticTemplate.CoalitionID = nil
|
||||
StaticTemplate.CategoryID = nil
|
||||
|
||||
local Static = coalition.addStaticObject( CountryID, StaticTemplate )
|
||||
|
||||
return Static
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Creates the original @{Static} at a POINT_VEC2.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param Core.Point#COORDINATE Coordinate 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.
|
||||
-- @return #SPAWNSTATIC
|
||||
function SPAWNSTATIC:ReSpawnAt( Coordinate, Heading )
|
||||
|
||||
local StaticTemplate = _DATABASE:GetStaticUnitTemplate( self.SpawnTemplatePrefix )
|
||||
|
||||
if StaticTemplate then
|
||||
|
||||
local CountryID = self.CountryID
|
||||
|
||||
StaticTemplate.x = Coordinate.x
|
||||
StaticTemplate.y = Coordinate.z
|
||||
|
||||
StaticTemplate.heading = Heading and ( ( Heading / 180 ) * math.pi ) or StaticTemplate.heading
|
||||
|
||||
StaticTemplate.CountryID = nil
|
||||
StaticTemplate.CoalitionID = nil
|
||||
StaticTemplate.CategoryID = nil
|
||||
|
||||
local Static = coalition.addStaticObject( CountryID, StaticTemplate )
|
||||
|
||||
return Static
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Creates a new @{Static} from a @{Zone}.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param Core.Zone#ZONE_BASE Zone The Zone where to spawn the static.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
--- **Core 2.1** -- Management of SPOT logistics, that can be transported from and to transportation carriers.
|
||||
--- **Core** -- Management of SPOT logistics, that can be transported from and to transportation carriers.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
@@ -11,7 +11,7 @@
|
||||
-- * Provide a @{Unit} as a target, instead of a point.
|
||||
-- * Implement a status machine, LaseOn, LaseOff.
|
||||
--
|
||||
-- ====
|
||||
-- ===
|
||||
--
|
||||
-- # Demo Missions
|
||||
--
|
||||
@@ -21,7 +21,7 @@
|
||||
--
|
||||
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
|
||||
--
|
||||
-- ====
|
||||
-- ===
|
||||
--
|
||||
-- # YouTube Channel
|
||||
--
|
||||
@@ -29,18 +29,14 @@
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # **AUTHORS and CONTRIBUTIONS**
|
||||
--
|
||||
-- ### Author: **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
|
||||
--
|
||||
--
|
||||
-- ### Authors:
|
||||
--
|
||||
-- * **FlightControl**: Design & Programming
|
||||
-- ===
|
||||
--
|
||||
-- @module Spot
|
||||
|
||||
@@ -204,7 +200,7 @@ do
|
||||
-- @param #number LaserCode
|
||||
-- @param #number Duration
|
||||
function SPOT:onafterLaseOn( From, Event, To, Target, LaserCode, Duration )
|
||||
self:E( { "LaseOn", Target, LaserCode, Duration } )
|
||||
self:F( { "LaseOn", Target, LaserCode, Duration } )
|
||||
|
||||
local function StopLase( self )
|
||||
self:LaseOff()
|
||||
@@ -226,16 +222,16 @@ do
|
||||
|
||||
self:HandleEvent( EVENTS.Dead )
|
||||
|
||||
self:__Lasing( -0.2 )
|
||||
self:__Lasing( -1 )
|
||||
end
|
||||
|
||||
--- @param #SPOT self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function SPOT:OnEventDead(EventData)
|
||||
self:E( { Dead = EventData.IniDCSUnitName, Target = self.Target } )
|
||||
self:F( { 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:F( {"Target dead ", self.Target:GetName() } )
|
||||
self:Destroyed()
|
||||
self:LaseOff()
|
||||
end
|
||||
@@ -253,7 +249,7 @@ do
|
||||
self.SpotLaser:setPoint( self.Target:GetPointVec3():AddY(1):GetVec3() )
|
||||
self:__Lasing( -0.2 )
|
||||
else
|
||||
self:E( { "Target is not alive", self.Target:IsAlive() } )
|
||||
self:F( { "Target is not alive", self.Target:IsAlive() } )
|
||||
end
|
||||
|
||||
end
|
||||
@@ -265,7 +261,7 @@ do
|
||||
-- @return #SPOT
|
||||
function SPOT:onafterLaseOff( From, Event, To )
|
||||
|
||||
self:E( {"Stopped lasing for ", self.Target:GetName() , SpotIR = self.SportIR, SpotLaser = self.SpotLaser } )
|
||||
self:F( {"Stopped lasing for ", self.Target:GetName() , SpotIR = self.SportIR, SpotLaser = self.SpotLaser } )
|
||||
|
||||
self.Lasing = false
|
||||
|
||||
|
||||
95
Moose Development/Moose/Core/UserFlag.lua
Normal file
95
Moose Development/Moose/Core/UserFlag.lua
Normal file
@@ -0,0 +1,95 @@
|
||||
--- **Core (WIP)** -- Manage user flags.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- Management of DCS User Flags.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module UserFlag
|
||||
|
||||
do -- UserFlag
|
||||
|
||||
--- @type USERFLAG
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
|
||||
--- # USERFLAG class, extends @{Base#BASE}
|
||||
--
|
||||
-- Management of DCS User Flags.
|
||||
--
|
||||
-- ## 1. USERFLAG constructor
|
||||
--
|
||||
-- * @{#USERFLAG.New}(): Creates a new USERFLAG object.
|
||||
--
|
||||
-- @field #USERFLAG
|
||||
USERFLAG = {
|
||||
ClassName = "USERFLAG",
|
||||
}
|
||||
|
||||
--- USERFLAG Constructor.
|
||||
-- @param #USERFLAG self
|
||||
-- @param #string UserFlagName The name of the userflag, which is a free text string.
|
||||
-- @return #USERFLAG
|
||||
function USERFLAG:New( UserFlagName ) --R2.3
|
||||
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #USERFLAG
|
||||
|
||||
self.UserFlagName = UserFlagName
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set the userflag to a given Number.
|
||||
-- @param #USERFLAG self
|
||||
-- @param #number Number The number value to be checked if it is the same as the userflag.
|
||||
-- @return #USERFLAG The userflag instance.
|
||||
-- @usage
|
||||
-- local BlueVictory = USERFLAG:New( "VictoryBlue" )
|
||||
-- BlueVictory:Set( 100 ) -- Set the UserFlag VictoryBlue to 100.
|
||||
--
|
||||
function USERFLAG:Set( Number ) --R2.3
|
||||
|
||||
self:F( { Number = Number } )
|
||||
|
||||
trigger.action.setUserFlag( self.UserFlagName, Number )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Get the userflag Number.
|
||||
-- @param #USERFLAG self
|
||||
-- @return #number Number The number value to be checked if it is the same as the userflag.
|
||||
-- @usage
|
||||
-- local BlueVictory = USERFLAG:New( "VictoryBlue" )
|
||||
-- local BlueVictoryValue = BlueVictory:Get() -- Get the UserFlag VictoryBlue value.
|
||||
--
|
||||
function USERFLAG:Get( Number ) --R2.3
|
||||
|
||||
return trigger.misc.getUserFlag( self.UserFlagName )
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Check if the userflag has a value of Number.
|
||||
-- @param #USERFLAG self
|
||||
-- @param #number Number The number value to be checked if it is the same as the userflag.
|
||||
-- @return #boolean true if the Number is the value of the userflag.
|
||||
-- @usage
|
||||
-- local BlueVictory = USERFLAG:New( "VictoryBlue" )
|
||||
-- if BlueVictory:Is( 1 ) then
|
||||
-- return "Blue has won"
|
||||
-- end
|
||||
function USERFLAG:Is( Number ) --R2.3
|
||||
|
||||
return trigger.misc.getUserFlag( self.UserFlagName ) == Number
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
129
Moose Development/Moose/Core/UserSound.lua
Normal file
129
Moose Development/Moose/Core/UserSound.lua
Normal file
@@ -0,0 +1,129 @@
|
||||
--- **Core (WIP)** -- Manage user sound.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- Management of DCS User Sound.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module UserSound
|
||||
|
||||
do -- UserSound
|
||||
|
||||
--- @type USERSOUND
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
|
||||
--- # USERSOUND class, extends @{Base#BASE}
|
||||
--
|
||||
-- Management of DCS User Sound.
|
||||
--
|
||||
-- ## 1. USERSOUND constructor
|
||||
--
|
||||
-- * @{#USERSOUND.New}(): Creates a new USERSOUND object.
|
||||
--
|
||||
-- @field #USERSOUND
|
||||
USERSOUND = {
|
||||
ClassName = "USERSOUND",
|
||||
}
|
||||
|
||||
--- USERSOUND Constructor.
|
||||
-- @param #USERSOUND self
|
||||
-- @param #string UserSoundFileName The filename of the usersound.
|
||||
-- @return #USERSOUND
|
||||
function USERSOUND:New( UserSoundFileName ) --R2.3
|
||||
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #USERSOUND
|
||||
|
||||
self.UserSoundFileName = UserSoundFileName
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set usersound filename.
|
||||
-- @param #USERSOUND self
|
||||
-- @param #string UserSoundFileName The filename of the usersound.
|
||||
-- @return #USERSOUND The usersound instance.
|
||||
-- @usage
|
||||
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
|
||||
-- BlueVictory:SetFileName( "BlueVictoryLoud.ogg" ) -- Set the BlueVictory to change the file name to play a louder sound.
|
||||
--
|
||||
function USERSOUND:SetFileName( UserSoundFileName ) --R2.3
|
||||
|
||||
self.UserSoundFileName = UserSoundFileName
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
--- Play the usersound to all players.
|
||||
-- @param #USERSOUND self
|
||||
-- @return #USERSOUND The usersound instance.
|
||||
-- @usage
|
||||
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
|
||||
-- BlueVictory:ToAll() -- Play the sound that Blue has won.
|
||||
--
|
||||
function USERSOUND:ToAll() --R2.3
|
||||
|
||||
trigger.action.outSound( self.UserSoundFileName )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Play the usersound to the given coalition.
|
||||
-- @param #USERSOUND self
|
||||
-- @param Dcs.DCScoalition#coalition Coalition The coalition to play the usersound to.
|
||||
-- @return #USERSOUND The usersound instance.
|
||||
-- @usage
|
||||
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
|
||||
-- BlueVictory:ToCoalition( coalition.side.BLUE ) -- Play the sound that Blue has won to the blue coalition.
|
||||
--
|
||||
function USERSOUND:ToCoalition( Coalition ) --R2.3
|
||||
|
||||
trigger.action.outSoundForCoalition(Coalition, self.UserSoundFileName )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Play the usersound to the given country.
|
||||
-- @param #USERSOUND self
|
||||
-- @param Dcs.DCScountry#country Country The country to play the usersound to.
|
||||
-- @return #USERSOUND The usersound instance.
|
||||
-- @usage
|
||||
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
|
||||
-- BlueVictory:ToCountry( country.id.USA ) -- Play the sound that Blue has won to the USA country.
|
||||
--
|
||||
function USERSOUND:ToCountry( Country ) --R2.3
|
||||
|
||||
trigger.action.outSoundForCountry( Country, self.UserSoundFileName )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Play the usersound to the given @{Group}.
|
||||
-- @param #USERSOUND self
|
||||
-- @param Wrapper.Group#GROUP Group The @{Group} to play the usersound to.
|
||||
-- @return #USERSOUND The usersound instance.
|
||||
-- @usage
|
||||
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
|
||||
-- local PlayerGroup = GROUP:FindByName( "PlayerGroup" ) -- Search for the active group named "PlayerGroup", that contains a human player.
|
||||
-- BlueVictory:ToGroup( PlayerGroup ) -- Play the sound that Blue has won to the player group.
|
||||
--
|
||||
function USERSOUND:ToGroup( Group ) --R2.3
|
||||
|
||||
trigger.action.outSoundForGroup( Group:GetID(), self.UserSoundFileName )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
end
|
||||
184
Moose Development/Moose/Core/Velocity.lua
Normal file
184
Moose Development/Moose/Core/Velocity.lua
Normal file
@@ -0,0 +1,184 @@
|
||||
--- **Core** -- VELOCITY models a speed, which can be expressed in various formats according the Settings.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module Velocity
|
||||
|
||||
do -- Velocity
|
||||
|
||||
--- @type VELOCITY
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
|
||||
--- # VELOCITY class, extends @{Base#BASE}
|
||||
--
|
||||
-- VELOCITY models a speed, which can be expressed in various formats according the Settings.
|
||||
--
|
||||
-- ## 1. VELOCITY constructor
|
||||
--
|
||||
-- * @{#VELOCITY.New}(): Creates a new VELOCITY object.
|
||||
--
|
||||
-- @field #VELOCITY
|
||||
VELOCITY = {
|
||||
ClassName = "VELOCITY",
|
||||
}
|
||||
|
||||
--- VELOCITY Constructor.
|
||||
-- @param #VELOCITY self
|
||||
-- @param #number VelocityMps The velocity in meters per second.
|
||||
-- @return #VELOCITY
|
||||
function VELOCITY:New( VelocityMps )
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #VELOCITY
|
||||
self:F( {} )
|
||||
self.Velocity = VelocityMps
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set the velocity in Mps (meters per second).
|
||||
-- @param #VELOCITY self
|
||||
-- @param #number VelocityMps The velocity in meters per second.
|
||||
-- @return #VELOCITY
|
||||
function VELOCITY:Set( VelocityMps )
|
||||
self.Velocity = VelocityMps
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the velocity in Mps (meters per second).
|
||||
-- @param #VELOCITY self
|
||||
-- @return #number The velocity in meters per second.
|
||||
function VELOCITY:Get()
|
||||
return self.Velocity
|
||||
end
|
||||
|
||||
--- Set the velocity in Kmph (kilometers per hour).
|
||||
-- @param #VELOCITY self
|
||||
-- @param #number VelocityKmph The velocity in kilometers per hour.
|
||||
-- @return #VELOCITY
|
||||
function VELOCITY:SetKmph( VelocityKmph )
|
||||
self.Velocity = UTILS.KmphToMps( VelocityKmph )
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the velocity in Kmph (kilometers per hour).
|
||||
-- @param #VELOCITY self
|
||||
-- @return #number The velocity in kilometers per hour.
|
||||
function VELOCITY:GetKmph()
|
||||
|
||||
return UTILS.MpsToKmph( self.Velocity )
|
||||
end
|
||||
|
||||
--- Set the velocity in Miph (miles per hour).
|
||||
-- @param #VELOCITY self
|
||||
-- @param #number VelocityMiph The velocity in miles per hour.
|
||||
-- @return #VELOCITY
|
||||
function VELOCITY:SetMiph( VelocityMiph )
|
||||
self.Velocity = UTILS.MiphToMps( VelocityMiph )
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the velocity in Miph (miles per hour).
|
||||
-- @param #VELOCITY self
|
||||
-- @return #number The velocity in miles per hour.
|
||||
function VELOCITY:GetMiph()
|
||||
return UTILS.MpsToMiph( self.Velocity )
|
||||
end
|
||||
|
||||
|
||||
--- Get the velocity in text, according the player @{Settings}.
|
||||
-- @param #VELOCITY self
|
||||
-- @param Core.Settings#SETTINGS Settings
|
||||
-- @return #string The velocity in text.
|
||||
function VELOCITY:GetText( Settings )
|
||||
local Settings = Settings or _SETTINGS
|
||||
if self.Velocity ~= 0 then
|
||||
if Settings:IsMetric() then
|
||||
return string.format( "%d km/h", UTILS.MpsToKmph( self.Velocity ) )
|
||||
else
|
||||
return string.format( "%d mi/h", UTILS.MpsToMiph( self.Velocity ) )
|
||||
end
|
||||
else
|
||||
return "stationary"
|
||||
end
|
||||
end
|
||||
|
||||
--- Get the velocity in text, according the player or default @{Settings}.
|
||||
-- @param #VELOCITY self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable
|
||||
-- @param Core.Settings#SETTINGS Settings
|
||||
-- @return #string The velocity in text according the player or default @{Settings}
|
||||
function VELOCITY:ToString( VelocityGroup, Settings ) -- R2.3
|
||||
self:F( { Group = VelocityGroup and VelocityGroup:GetName() } )
|
||||
local Settings = Settings or ( VelocityGroup and _DATABASE:GetPlayerSettings( VelocityGroup:GetPlayerName() ) ) or _SETTINGS
|
||||
return self:GetText( Settings )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
do -- VELOCITY_POSITIONABLE
|
||||
|
||||
--- @type VELOCITY_POSITIONABLE
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
|
||||
--- # VELOCITY_POSITIONABLE class, extends @{Base#BASE}
|
||||
--
|
||||
-- VELOCITY_POSITIONABLE monitors the speed of an @{Positionable} in the simulation, which can be expressed in various formats according the Settings.
|
||||
--
|
||||
-- ## 1. VELOCITY_POSITIONABLE constructor
|
||||
--
|
||||
-- * @{#VELOCITY_POSITIONABLE.New}(): Creates a new VELOCITY_POSITIONABLE object.
|
||||
--
|
||||
-- @field #VELOCITY_POSITIONABLE
|
||||
VELOCITY_POSITIONABLE = {
|
||||
ClassName = "VELOCITY_POSITIONABLE",
|
||||
}
|
||||
|
||||
--- VELOCITY_POSITIONABLE Constructor.
|
||||
-- @param #VELOCITY_POSITIONABLE self
|
||||
-- @param Wrapper.Positionable#POSITIONABLE Positionable The Positionable to monitor the speed.
|
||||
-- @return #VELOCITY_POSITIONABLE
|
||||
function VELOCITY_POSITIONABLE:New( Positionable )
|
||||
local self = BASE:Inherit( self, VELOCITY:New() ) -- #VELOCITY_POSITIONABLE
|
||||
self:F( {} )
|
||||
self.Positionable = Positionable
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the velocity in Mps (meters per second).
|
||||
-- @param #VELOCITY_POSITIONABLE self
|
||||
-- @return #number The velocity in meters per second.
|
||||
function VELOCITY_POSITIONABLE:Get()
|
||||
return self.Positionable:GetVelocityMPS() or 0
|
||||
end
|
||||
|
||||
--- Get the velocity in Kmph (kilometers per hour).
|
||||
-- @param #VELOCITY_POSITIONABLE self
|
||||
-- @return #number The velocity in kilometers per hour.
|
||||
function VELOCITY_POSITIONABLE:GetKmph()
|
||||
|
||||
return UTILS.MpsToKmph( self.Positionable:GetVelocityMPS() or 0)
|
||||
end
|
||||
|
||||
--- Get the velocity in Miph (miles per hour).
|
||||
-- @param #VELOCITY_POSITIONABLE self
|
||||
-- @return #number The velocity in miles per hour.
|
||||
function VELOCITY_POSITIONABLE:GetMiph()
|
||||
return UTILS.MpsToMiph( self.Positionable:GetVelocityMPS() or 0 )
|
||||
end
|
||||
|
||||
--- Get the velocity in text, according the player or default @{Settings}.
|
||||
-- @param #VELOCITY_POSITIONABLE self
|
||||
-- @return #string The velocity in text according the player or default @{Settings}
|
||||
function VELOCITY_POSITIONABLE:ToString() -- R2.3
|
||||
self:F( { Group = self.Positionable and self.Positionable:GetName() } )
|
||||
local Settings = Settings or ( self.Positionable and _DATABASE:GetPlayerSettings( self.Positionable:GetPlayerName() ) ) or _SETTINGS
|
||||
self.Velocity = self.Positionable:GetVelocityMPS()
|
||||
return self:GetText( Settings )
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,4 +1,4 @@
|
||||
--- **Core** - ZONE classes define **zones** within your mission of **various forms**, with **various capabilities**.
|
||||
--- **Core** -- ZONE classes define **zones** within your mission of **various forms**, with **various capabilities**.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
@@ -29,37 +29,8 @@
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # **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.
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -79,6 +50,8 @@
|
||||
-- ## Each zone has a name:
|
||||
--
|
||||
-- * @{#ZONE_BASE.GetName}(): Returns the name of the zone.
|
||||
-- * @{#ZONE_BASE.SetName}(): Sets the name of the zone.
|
||||
--
|
||||
--
|
||||
-- ## Each zone implements two polymorphic functions defined in @{Zone#ZONE_BASE}:
|
||||
--
|
||||
@@ -141,6 +114,8 @@ function ZONE_BASE:New( ZoneName )
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Returns the name of the zone.
|
||||
-- @param #ZONE_BASE self
|
||||
-- @return #string The name of the zone.
|
||||
@@ -150,6 +125,17 @@ function ZONE_BASE:GetName()
|
||||
return self.ZoneName
|
||||
end
|
||||
|
||||
|
||||
--- Sets the name of the zone.
|
||||
-- @param #ZONE_BASE self
|
||||
-- @param #string ZoneName The name of the zone.
|
||||
-- @return #ZONE_BASE
|
||||
function ZONE_BASE:SetName( ZoneName )
|
||||
self:F2()
|
||||
|
||||
self.ZoneName = ZoneName
|
||||
end
|
||||
|
||||
--- Returns if a Vec2 is within the zone.
|
||||
-- @param #ZONE_BASE self
|
||||
-- @param Dcs.DCSTypes#Vec2 Vec2 The Vec2 to test.
|
||||
@@ -165,10 +151,16 @@ end
|
||||
-- @param Dcs.DCSTypes#Vec3 Vec3 The point to test.
|
||||
-- @return #boolean true if the Vec3 is within the zone.
|
||||
function ZONE_BASE:IsVec3InZone( Vec3 )
|
||||
self:F2( Vec3 )
|
||||
|
||||
local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } )
|
||||
return InZone
|
||||
end
|
||||
|
||||
--- Returns if a Coordinate is within the zone.
|
||||
-- @param #ZONE_BASE self
|
||||
-- @param Core.Point#COORDINATE Coordinate The coordinate to test.
|
||||
-- @return #boolean true if the coordinate is within the zone.
|
||||
function ZONE_BASE:IsCoordinateInZone( Coordinate )
|
||||
local InZone = self:IsVec2InZone( Coordinate:GetVec2() )
|
||||
return InZone
|
||||
end
|
||||
|
||||
@@ -177,10 +169,7 @@ end
|
||||
-- @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
|
||||
|
||||
@@ -189,10 +178,7 @@ end
|
||||
-- @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
|
||||
|
||||
@@ -201,8 +187,6 @@ end
|
||||
-- @param #ZONE_BASE self
|
||||
-- @return #nil.
|
||||
function ZONE_BASE:GetVec2()
|
||||
self:F2( self.ZoneName )
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -225,7 +209,6 @@ end
|
||||
|
||||
--- Returns a @{Point#COORDINATE} of the zone.
|
||||
-- @param #ZONE_BASE self
|
||||
-- @param Dcs.DCSTypes#Distance Height The height to add to the land height where the center of the zone is located.
|
||||
-- @return Core.Point#COORDINATE The Coordinate of the zone.
|
||||
function ZONE_BASE:GetCoordinate()
|
||||
self:F2( self.ZoneName )
|
||||
@@ -251,7 +234,7 @@ function ZONE_BASE:GetVec3( Height )
|
||||
|
||||
local Vec2 = self:GetVec2()
|
||||
|
||||
local Vec3 = { x = Vec2.x, y = land.getHeight( self:GetVec2() ) + Height, z = Vec2.y }
|
||||
local Vec3 = { x = Vec2.x, y = Height and Height or land.getHeight( self:GetVec2() ), z = Vec2.y }
|
||||
|
||||
self:T2( { Vec3 } )
|
||||
|
||||
@@ -475,12 +458,17 @@ end
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param Utilities.Utils#SMOKECOLOR SmokeColor The smoke color.
|
||||
-- @param #number Points (optional) The amount of points in the circle.
|
||||
-- @param #number AddHeight (optional) The height to be added for the smoke.
|
||||
-- @param #number AddOffSet (optional) The angle to be added for the smoking start position.
|
||||
-- @return #ZONE_RADIUS self
|
||||
function ZONE_RADIUS:SmokeZone( SmokeColor, Points )
|
||||
function ZONE_RADIUS:SmokeZone( SmokeColor, Points, AddHeight, AngleOffset )
|
||||
self:F2( SmokeColor )
|
||||
|
||||
local Point = {}
|
||||
local Vec2 = self:GetVec2()
|
||||
|
||||
AddHeight = AddHeight or 0
|
||||
AngleOffset = AngleOffset or 0
|
||||
|
||||
Points = Points and Points or 360
|
||||
|
||||
@@ -488,10 +476,10 @@ function ZONE_RADIUS:SmokeZone( SmokeColor, Points )
|
||||
local RadialBase = math.pi*2
|
||||
|
||||
for Angle = 0, 360, 360 / Points do
|
||||
local Radial = Angle * RadialBase / 360
|
||||
local Radial = ( Angle + AngleOffset ) * RadialBase / 360
|
||||
Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius()
|
||||
Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius()
|
||||
POINT_VEC2:New( Point.x, Point.y ):Smoke( SmokeColor )
|
||||
POINT_VEC2:New( Point.x, Point.y, AddHeight ):Smoke( SmokeColor )
|
||||
end
|
||||
|
||||
return self
|
||||
@@ -503,13 +491,16 @@ end
|
||||
-- @param Utilities.Utils#FLARECOLOR FlareColor The flare color.
|
||||
-- @param #number Points (optional) The amount of points in the circle.
|
||||
-- @param Dcs.DCSTypes#Azimuth Azimuth (optional) Azimuth The azimuth of the flare.
|
||||
-- @param #number AddHeight (optional) The height to be added for the smoke.
|
||||
-- @return #ZONE_RADIUS self
|
||||
function ZONE_RADIUS:FlareZone( FlareColor, Points, Azimuth )
|
||||
function ZONE_RADIUS:FlareZone( FlareColor, Points, Azimuth, AddHeight )
|
||||
self:F2( { FlareColor, Azimuth } )
|
||||
|
||||
local Point = {}
|
||||
local Vec2 = self:GetVec2()
|
||||
|
||||
AddHeight = AddHeight or 0
|
||||
|
||||
Points = Points and Points or 360
|
||||
|
||||
local Angle
|
||||
@@ -519,7 +510,7 @@ function ZONE_RADIUS:FlareZone( FlareColor, Points, Azimuth )
|
||||
local Radial = Angle * RadialBase / 360
|
||||
Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius()
|
||||
Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius()
|
||||
POINT_VEC2:New( Point.x, Point.y ):Flare( FlareColor, Azimuth )
|
||||
POINT_VEC2:New( Point.x, Point.y, AddHeight ):Flare( FlareColor, Azimuth )
|
||||
end
|
||||
|
||||
return self
|
||||
@@ -592,6 +583,233 @@ function ZONE_RADIUS:GetVec3( Height )
|
||||
end
|
||||
|
||||
|
||||
--- Scan the zone for the presence of units of the given ObjectCategories.
|
||||
-- Note that after a zone has been scanned, the zone can be evaluated by:
|
||||
--
|
||||
-- * @{ZONE_RADIUS.IsAllInZoneOfCoalition}(): Scan the presence of units in the zone of a coalition.
|
||||
-- * @{ZONE_RADIUS.IsAllInZoneOfOtherCoalition}(): Scan the presence of units in the zone of an other coalition.
|
||||
-- * @{ZONE_RADIUS.IsSomeInZoneOfCoalition}(): Scan if there is some presence of units in the zone of the given coalition.
|
||||
-- * @{ZONE_RADIUS.IsNoneInZoneOfCoalition}(): Scan if there isn't any presence of units in the zone of an other coalition than the given one.
|
||||
-- * @{ZONE_RADIUS.IsNoneInZone}(): Scan if the zone is empty.
|
||||
-- @{#ZONE_RADIUS.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param ObjectCategories
|
||||
-- @param Coalition
|
||||
-- @usage
|
||||
-- self.Zone:Scan()
|
||||
-- local IsAttacked = self.Zone:IsSomeInZoneOfCoalition( self.Coalition )
|
||||
function ZONE_RADIUS:Scan( ObjectCategories )
|
||||
|
||||
self.ScanData = {}
|
||||
self.ScanData.Coalitions = {}
|
||||
self.ScanData.Scenery = {}
|
||||
self.ScanData.Units = {}
|
||||
|
||||
local ZoneCoord = self:GetCoordinate()
|
||||
local ZoneRadius = self:GetRadius()
|
||||
|
||||
self:F({ZoneCoord = ZoneCoord, ZoneRadius = ZoneRadius, ZoneCoordLL = ZoneCoord:ToStringLLDMS()})
|
||||
|
||||
local SphereSearch = {
|
||||
id = world.VolumeType.SPHERE,
|
||||
params = {
|
||||
point = ZoneCoord:GetVec3(),
|
||||
radius = ZoneRadius,
|
||||
}
|
||||
}
|
||||
|
||||
local function EvaluateZone( ZoneObject )
|
||||
--if ZoneObject:isExist() then --FF: isExist always returns false for SCENERY objects since DCS 2.2 and still in DCS 2.5
|
||||
if ZoneObject then
|
||||
local ObjectCategory = ZoneObject:getCategory()
|
||||
if ( ObjectCategory == Object.Category.UNIT and ZoneObject:isExist() and ZoneObject:isActive() ) or
|
||||
(ObjectCategory == Object.Category.STATIC and ZoneObject:isExist()) then
|
||||
local CoalitionDCSUnit = ZoneObject:getCoalition()
|
||||
self.ScanData.Coalitions[CoalitionDCSUnit] = true
|
||||
self.ScanData.Units[ZoneObject] = ZoneObject
|
||||
self:F( { Name = ZoneObject:getName(), Coalition = CoalitionDCSUnit } )
|
||||
end
|
||||
if ObjectCategory == Object.Category.SCENERY then
|
||||
local SceneryType = ZoneObject:getTypeName()
|
||||
local SceneryName = ZoneObject:getName()
|
||||
self.ScanData.Scenery[SceneryType] = self.ScanData.Scenery[SceneryType] or {}
|
||||
self.ScanData.Scenery[SceneryType][SceneryName] = SCENERY:Register( SceneryName, ZoneObject )
|
||||
self:F( { SCENERY = self.ScanData.Scenery[SceneryType][SceneryName] } )
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
world.searchObjects( ObjectCategories, SphereSearch, EvaluateZone )
|
||||
|
||||
end
|
||||
|
||||
|
||||
function ZONE_RADIUS:GetScannedUnits()
|
||||
|
||||
return self.ScanData.Units
|
||||
end
|
||||
|
||||
|
||||
function ZONE_RADIUS:CountScannedCoalitions()
|
||||
|
||||
local Count = 0
|
||||
|
||||
for CoalitionID, Coalition in pairs( self.ScanData.Coalitions ) do
|
||||
Count = Count + 1
|
||||
end
|
||||
return Count
|
||||
end
|
||||
|
||||
|
||||
--- Get Coalitions of the units in the Zone, or Check if there are units of the given Coalition in the Zone.
|
||||
-- Returns nil if there are none ot two Coalitions in the zone!
|
||||
-- Returns one Coalition if there are only Units of one Coalition in the Zone.
|
||||
-- Returns the Coalition for the given Coalition if there are units of the Coalition in the Zone
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @return #table
|
||||
function ZONE_RADIUS:GetScannedCoalition( Coalition )
|
||||
|
||||
if Coalition then
|
||||
return self.ScanData.Coalitions[Coalition]
|
||||
else
|
||||
local Count = 0
|
||||
local ReturnCoalition = nil
|
||||
|
||||
for CoalitionID, Coalition in pairs( self.ScanData.Coalitions ) do
|
||||
Count = Count + 1
|
||||
ReturnCoalition = CoalitionID
|
||||
end
|
||||
|
||||
if Count ~= 1 then
|
||||
ReturnCoalition = nil
|
||||
end
|
||||
|
||||
return ReturnCoalition
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function ZONE_RADIUS:GetScannedSceneryType( SceneryType )
|
||||
return self.ScanData.Scenery[SceneryType]
|
||||
end
|
||||
|
||||
|
||||
function ZONE_RADIUS:GetScannedScenery()
|
||||
return self.ScanData.Scenery
|
||||
end
|
||||
|
||||
|
||||
--- Is All in Zone of Coalition?
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param Coalition
|
||||
-- @return #boolean
|
||||
-- @usage
|
||||
-- self.Zone:Scan()
|
||||
-- local IsGuarded = self.Zone:IsAllInZoneOfCoalition( self.Coalition )
|
||||
function ZONE_RADIUS:IsAllInZoneOfCoalition( Coalition )
|
||||
|
||||
--self:E( { Coalitions = self.Coalitions, Count = self:CountScannedCoalitions() } )
|
||||
return self:CountScannedCoalitions() == 1 and self:GetScannedCoalition( Coalition ) == true
|
||||
end
|
||||
|
||||
|
||||
--- Is All in Zone of Other Coalition?
|
||||
-- You first need to use the @{#ZONE_RADIUS.Scan} method to scan the zone before it can be evaluated!
|
||||
-- Note that once a zone has been scanned, multiple evaluations can be done on the scan result set.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param Coalition
|
||||
-- @return #boolean
|
||||
-- @usage
|
||||
-- self.Zone:Scan()
|
||||
-- local IsCaptured = self.Zone:IsAllInZoneOfOtherCoalition( self.Coalition )
|
||||
function ZONE_RADIUS:IsAllInZoneOfOtherCoalition( Coalition )
|
||||
|
||||
--self:E( { Coalitions = self.Coalitions, Count = self:CountScannedCoalitions() } )
|
||||
return self:CountScannedCoalitions() == 1 and self:GetScannedCoalition( Coalition ) == nil
|
||||
end
|
||||
|
||||
|
||||
--- Is Some in Zone of Coalition?
|
||||
-- You first need to use the @{#ZONE_RADIUS.Scan} method to scan the zone before it can be evaluated!
|
||||
-- Note that once a zone has been scanned, multiple evaluations can be done on the scan result set.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param Coalition
|
||||
-- @return #boolean
|
||||
-- @usage
|
||||
-- self.Zone:Scan()
|
||||
-- local IsAttacked = self.Zone:IsSomeInZoneOfCoalition( self.Coalition )
|
||||
function ZONE_RADIUS:IsSomeInZoneOfCoalition( Coalition )
|
||||
|
||||
return self:CountScannedCoalitions() > 1 and self:GetScannedCoalition( Coalition ) == true
|
||||
end
|
||||
|
||||
|
||||
--- Is None in Zone of Coalition?
|
||||
-- You first need to use the @{#ZONE_RADIUS.Scan} method to scan the zone before it can be evaluated!
|
||||
-- Note that once a zone has been scanned, multiple evaluations can be done on the scan result set.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param Coalition
|
||||
-- @return #boolean
|
||||
-- @usage
|
||||
-- self.Zone:Scan()
|
||||
-- local IsOccupied = self.Zone:IsNoneInZoneOfCoalition( self.Coalition )
|
||||
function ZONE_RADIUS:IsNoneInZoneOfCoalition( Coalition )
|
||||
|
||||
return self:GetScannedCoalition( Coalition ) == nil
|
||||
end
|
||||
|
||||
|
||||
--- Is None in Zone?
|
||||
-- You first need to use the @{#ZONE_RADIUS.Scan} method to scan the zone before it can be evaluated!
|
||||
-- Note that once a zone has been scanned, multiple evaluations can be done on the scan result set.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @return #boolean
|
||||
-- @usage
|
||||
-- self.Zone:Scan()
|
||||
-- local IsEmpty = self.Zone:IsNoneInZone()
|
||||
function ZONE_RADIUS:IsNoneInZone()
|
||||
|
||||
return self:CountScannedCoalitions() == 0
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
--- Searches the zone
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param ObjectCategories A list of categories, which are members of Object.Category
|
||||
-- @param EvaluateFunction
|
||||
function ZONE_RADIUS:SearchZone( EvaluateFunction, ObjectCategories )
|
||||
|
||||
local SearchZoneResult = true
|
||||
|
||||
local ZoneCoord = self:GetCoordinate()
|
||||
local ZoneRadius = self:GetRadius()
|
||||
|
||||
self:F({ZoneCoord = ZoneCoord, ZoneRadius = ZoneRadius, ZoneCoordLL = ZoneCoord:ToStringLLDMS()})
|
||||
|
||||
local SphereSearch = {
|
||||
id = world.VolumeType.SPHERE,
|
||||
params = {
|
||||
point = ZoneCoord:GetVec3(),
|
||||
radius = ZoneRadius / 2,
|
||||
}
|
||||
}
|
||||
|
||||
local function EvaluateZone( ZoneDCSUnit )
|
||||
|
||||
env.info( ZoneDCSUnit:getName() )
|
||||
|
||||
local ZoneUnit = UNIT:Find( ZoneDCSUnit )
|
||||
|
||||
return EvaluateFunction( ZoneUnit )
|
||||
end
|
||||
|
||||
world.searchObjects( Object.Category.UNIT, SphereSearch, EvaluateZone )
|
||||
|
||||
end
|
||||
|
||||
--- Returns if a location is within the zone.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param Dcs.DCSTypes#Vec2 Vec2 The location to test.
|
||||
@@ -652,13 +870,29 @@ end
|
||||
function ZONE_RADIUS:GetRandomPointVec2( inner, outer )
|
||||
self:F( self.ZoneName, inner, outer )
|
||||
|
||||
local PointVec2 = POINT_VEC2:NewFromVec2( self:GetRandomVec2() )
|
||||
local PointVec2 = POINT_VEC2:NewFromVec2( self:GetRandomVec2( inner, outer ) )
|
||||
|
||||
self:T3( { PointVec2 } )
|
||||
|
||||
return PointVec2
|
||||
end
|
||||
|
||||
--- Returns Returns a random Vec3 location within the zone.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0.
|
||||
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
|
||||
-- @return Dcs.DCSTypes#Vec3 The random location within the zone.
|
||||
function ZONE_RADIUS:GetRandomVec3( inner, outer )
|
||||
self:F( self.ZoneName, inner, outer )
|
||||
|
||||
local Vec2 = self:GetRandomVec2( inner, outer )
|
||||
|
||||
self:T3( { x = Vec2.x, y = self.y, z = Vec2.y } )
|
||||
|
||||
return { x = Vec2.x, y = self.y, z = Vec2.y }
|
||||
end
|
||||
|
||||
|
||||
--- Returns a @{Point#POINT_VEC3} object reflecting a random 3D location within the zone.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0.
|
||||
@@ -667,7 +901,7 @@ end
|
||||
function ZONE_RADIUS:GetRandomPointVec3( inner, outer )
|
||||
self:F( self.ZoneName, inner, outer )
|
||||
|
||||
local PointVec3 = POINT_VEC3:NewFromVec2( self:GetRandomVec2() )
|
||||
local PointVec3 = POINT_VEC3:NewFromVec2( self:GetRandomVec2( inner, outer ) )
|
||||
|
||||
self:T3( { PointVec3 } )
|
||||
|
||||
@@ -675,6 +909,22 @@ function ZONE_RADIUS:GetRandomPointVec3( inner, outer )
|
||||
end
|
||||
|
||||
|
||||
--- Returns a @{Point#COORDINATE} object reflecting a random 3D location within the zone.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0.
|
||||
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
|
||||
-- @return Core.Point#COORDINATE
|
||||
function ZONE_RADIUS:GetRandomCoordinate( inner, outer )
|
||||
self:F( self.ZoneName, inner, outer )
|
||||
|
||||
local Coordinate = COORDINATE:NewFromVec2( self:GetRandomVec2() )
|
||||
|
||||
self:T3( { Coordinate = Coordinate } )
|
||||
|
||||
return Coordinate
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- @type ZONE
|
||||
-- @extends #ZONE_RADIUS
|
||||
@@ -712,6 +962,17 @@ function ZONE:New( ZoneName )
|
||||
return self
|
||||
end
|
||||
|
||||
--- Find a zone in the _DATABASE using the name of the zone.
|
||||
-- @param #ZONE_BASE self
|
||||
-- @param #string ZoneName The name of the zone.
|
||||
-- @return #ZONE_BASE self
|
||||
function ZONE:FindByName( ZoneName )
|
||||
|
||||
local ZoneFound = _DATABASE:FindZone( ZoneName )
|
||||
return ZoneFound
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- @type ZONE_UNIT
|
||||
-- @field Wrapper.Unit#UNIT ZoneUNIT
|
||||
@@ -864,6 +1125,20 @@ function ZONE_GROUP:GetRandomVec2()
|
||||
return Point
|
||||
end
|
||||
|
||||
--- Returns a @{Point#POINT_VEC2} object reflecting a random 2D location within the zone.
|
||||
-- @param #ZONE_GROUP self
|
||||
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0.
|
||||
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
|
||||
-- @return Core.Point#POINT_VEC2 The @{Point#POINT_VEC2} object reflecting the random 3D location within the zone.
|
||||
function ZONE_GROUP:GetRandomPointVec2( inner, outer )
|
||||
self:F( self.ZoneName, inner, outer )
|
||||
|
||||
local PointVec2 = POINT_VEC2:NewFromVec2( self:GetRandomVec2() )
|
||||
|
||||
self:T3( { PointVec2 } )
|
||||
|
||||
return PointVec2
|
||||
end
|
||||
|
||||
|
||||
--- @type ZONE_POLYGON_BASE
|
||||
@@ -1107,6 +1382,20 @@ function ZONE_POLYGON_BASE:GetRandomPointVec3()
|
||||
end
|
||||
|
||||
|
||||
--- Return a @{Point#COORDINATE} object representing a random 3D point at landheight within the zone.
|
||||
-- @param #ZONE_POLYGON_BASE self
|
||||
-- @return Core.Point#COORDINATE
|
||||
function ZONE_POLYGON_BASE:GetRandomCoordinate()
|
||||
self:F2()
|
||||
|
||||
local Coordinate = COORDINATE:NewFromVec2( self:GetRandomVec2() )
|
||||
|
||||
self:T2( Coordinate )
|
||||
|
||||
return Coordinate
|
||||
end
|
||||
|
||||
|
||||
--- Get the bounding square the zone.
|
||||
-- @param #ZONE_POLYGON_BASE self
|
||||
-- @return #ZONE_POLYGON_BASE.BoundingSquare The bounding square.
|
||||
@@ -1144,7 +1433,7 @@ ZONE_POLYGON = {
|
||||
ClassName="ZONE_POLYGON",
|
||||
}
|
||||
|
||||
--- Constructor to create a ZONE_POLYGON instance, taking the zone name and the name of the @{Group#GROUP} defined within the Mission Editor.
|
||||
--- Constructor to create a ZONE_POLYGON instance, taking the zone name and the @{Group#GROUP} defined within the Mission Editor.
|
||||
-- The @{Group#GROUP} waypoints define the polygon corners. The first and the last point are automatically connected by ZONE_POLYGON.
|
||||
-- @param #ZONE_POLYGON self
|
||||
-- @param #string ZoneName Name of the zone.
|
||||
@@ -1160,3 +1449,22 @@ function ZONE_POLYGON:New( ZoneName, ZoneGroup )
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Constructor to create a ZONE_POLYGON instance, taking the zone name and the **name** of the @{Group#GROUP} defined within the Mission Editor.
|
||||
-- The @{Group#GROUP} waypoints define the polygon corners. The first and the last point are automatically connected by ZONE_POLYGON.
|
||||
-- @param #ZONE_POLYGON self
|
||||
-- @param #string ZoneName Name of the zone.
|
||||
-- @param #string GroupName The group name of the GROUP defining the waypoints within the Mission Editor to define the polygon shape.
|
||||
-- @return #ZONE_POLYGON self
|
||||
function ZONE_POLYGON:NewFromGroupName( GroupName )
|
||||
|
||||
local ZoneGroup = GROUP:FindByName( GroupName )
|
||||
|
||||
local GroupPoints = ZoneGroup:GetTaskRoute()
|
||||
|
||||
local self = BASE:Inherit( self, ZONE_POLYGON_BASE:New( GroupName, GroupPoints ) )
|
||||
self:F( { GroupName, ZoneGroup, self._.Polygon } )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
Submodule Moose Development/Moose/Dcs deleted from 53f6e5cd52
54
Moose Development/Moose/Dcs/DCSAirbase.lua
Normal file
54
Moose Development/Moose/Dcs/DCSAirbase.lua
Normal file
@@ -0,0 +1,54 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- @module DCSAirbase
|
||||
|
||||
|
||||
--- Represents airbases: airdromes, helipads and ships with flying decks or landing pads.
|
||||
-- @type Airbase
|
||||
-- @extends Dcs.DCSCoalitionWrapper.Object#CoalitionObject
|
||||
-- @field #Airbase.ID ID Identifier of an airbase. It assigned to an airbase by the Mission Editor automatically. This identifier is used in AI tasks to refer an airbase that exists (spawned and not dead) or not.
|
||||
-- @field #Airbase.Category Category enum contains identifiers of airbase categories.
|
||||
-- @field #Airbase.Desc Desc Airbase descriptor. Airdromes are unique and their types are unique, but helipads and ships are not always unique and may have the same type.
|
||||
|
||||
--- Enum contains identifiers of airbase categories.
|
||||
-- @type Airbase.Category
|
||||
-- @field AIRDROME
|
||||
-- @field HELIPAD
|
||||
-- @field SHIP
|
||||
|
||||
--- Airbase descriptor. Airdromes are unique and their types are unique, but helipads and ships are not always unique and may have the same type.
|
||||
-- @type Airbase.Desc
|
||||
-- @extends #Desc
|
||||
-- @field #Airbase.Category category Category of the airbase type.
|
||||
|
||||
--- Returns airbase by its name. If no airbase found the function will return nil.
|
||||
-- @function [parent=#Airbase] getByName
|
||||
-- @param #string name
|
||||
-- @return #Airbase
|
||||
|
||||
--- Returns airbase descriptor by type name. If no descriptor is found the function will return nil.
|
||||
-- @function [parent=#Airbase] getDescByName
|
||||
-- @param #TypeName typeName Airbase type name.
|
||||
-- @return #Airbase.Desc
|
||||
|
||||
--- Returns Unit that is corresponded to the airbase. Works only for ships.
|
||||
-- @function [parent=#Airbase] getUnit
|
||||
-- @param self
|
||||
-- @return Wrapper.Unit#Unit
|
||||
|
||||
--- Returns identifier of the airbase.
|
||||
-- @function [parent=#Airbase] getID
|
||||
-- @param self
|
||||
-- @return #Airbase.ID
|
||||
|
||||
--- Returns the airbase's callsign - the localized string.
|
||||
-- @function [parent=#Airbase] getCallsign
|
||||
-- @param self
|
||||
-- @return #string
|
||||
|
||||
--- Returns descriptor of the airbase.
|
||||
-- @function [parent=#Airbase] getDesc
|
||||
-- @param self
|
||||
-- @return #Airbase.Desc
|
||||
|
||||
|
||||
Airbase = {} --#Airbase
|
||||
20
Moose Development/Moose/Dcs/DCSCoalitionObject.lua
Normal file
20
Moose Development/Moose/Dcs/DCSCoalitionObject.lua
Normal file
@@ -0,0 +1,20 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- @module DCSCoalitionObject
|
||||
|
||||
--- @type CoalitionObject
|
||||
-- @extends Dcs.DCSWrapper.Object#Object
|
||||
|
||||
|
||||
|
||||
--- Returns coalition of the object.
|
||||
-- @function [parent=#CoalitionObject] getCoalition
|
||||
-- @param #CoalitionObject self
|
||||
-- @return Dcs.DCSTypes#coalition.side
|
||||
|
||||
--- Returns object country.
|
||||
-- @function [parent=#CoalitionObject] getCountry
|
||||
-- @param #CoalitionObject self
|
||||
-- @return #country.id
|
||||
|
||||
|
||||
CoalitionObject = {} --#CoalitionObject
|
||||
10
Moose Development/Moose/Dcs/DCSCommand.lua
Normal file
10
Moose Development/Moose/Dcs/DCSCommand.lua
Normal file
@@ -0,0 +1,10 @@
|
||||
--- @module DCSCommand
|
||||
|
||||
|
||||
--- @type Command
|
||||
-- @field #string id
|
||||
-- @field #Command.params params
|
||||
|
||||
--- @type Command.params
|
||||
|
||||
env.info( "Command defined" )
|
||||
115
Moose Development/Moose/Dcs/DCSController.lua
Normal file
115
Moose Development/Moose/Dcs/DCSController.lua
Normal file
@@ -0,0 +1,115 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- @module DCSController
|
||||
|
||||
--- Controller is an object that performs A.I.-routines. Other words controller is an instance of A.I.. Controller stores current main task, active enroute tasks and behavior options. Controller performs commands. Please, read DCS A-10C GUI Manual EN.pdf chapter "Task Planning for Unit Groups", page 91 to understand A.I. system of DCS:A-10C.
|
||||
--
|
||||
-- This class has 2 types of functions:
|
||||
--
|
||||
-- * Tasks
|
||||
-- * Commands: Commands are instant actions those required zero time to perform. Commands may be used both for control unit/group behavior and control game mechanics.
|
||||
-- @type Controller
|
||||
-- @field #Controller.Detection Detection Enum contains identifiers of surface types.
|
||||
|
||||
--- Enables and disables the controller.
|
||||
-- Note: Now it works only for ground / naval groups!
|
||||
-- @function [parent=#Controller] setOnOff
|
||||
-- @param self
|
||||
-- @param #boolean value Enable / Disable.
|
||||
|
||||
-- Tasks
|
||||
|
||||
--- Resets current task and then sets the task to the controller. Task is a table that contains task identifier and task parameters.
|
||||
-- @function [parent=#Controller] setTask
|
||||
-- @param self
|
||||
-- @param #Task task
|
||||
|
||||
--- Resets current task of the controller.
|
||||
-- @function [parent=#Controller] resetTask
|
||||
-- @param self
|
||||
|
||||
--- Pushes the task to the front of the queue and makes the task active. Further call of function Controller.setTask() function will stop current task, clear the queue and set the new task active. If the task queue is empty the function will work like function Controller.setTask() function.
|
||||
-- @function [parent=#Controller] pushTask
|
||||
-- @param self
|
||||
-- @param #Task task
|
||||
|
||||
--- Pops current (front) task from the queue and makes active next task in the queue (if exists). If no more tasks in the queue the function works like function Controller.resetTask() function. Does nothing if the queue is empty.
|
||||
-- @function [parent=#Controller] popTask
|
||||
-- @param self
|
||||
|
||||
--- Returns true if the controller has a task.
|
||||
-- @function [parent=#Controller] hasTask
|
||||
-- @param self
|
||||
-- @return #boolean
|
||||
|
||||
-- Commands
|
||||
|
||||
--TODO: describe #Command structure
|
||||
--- Sets the command to perform by controller.
|
||||
-- @function [parent=#Controller] setCommand
|
||||
-- @param self
|
||||
-- @param #Command command Table that contains command identifier and command parameters.
|
||||
|
||||
|
||||
-- Behaviours
|
||||
|
||||
--- Sets the option to the controller.
|
||||
-- Option is a pair of identifier and value. Behavior options are global parameters those affect controller behavior in all tasks it performs.
|
||||
-- Option identifiers and values are stored in table AI.Option in subtables Air, Ground and Naval.
|
||||
--
|
||||
-- OptionId = @{#AI.Option.Air.id} or @{#AI.Option.Ground.id} or @{#AI.Option.Naval.id}
|
||||
-- OptionValue = AI.Option.Air.val[optionName] or AI.Option.Ground.val[optionName] or AI.Option.Naval.val[optionName]
|
||||
--
|
||||
-- @function [parent=#Controller] setOption
|
||||
-- @param self
|
||||
-- @param #OptionId optionId Option identifier.
|
||||
-- @param #OptionValue optionValue Value of the option.
|
||||
|
||||
|
||||
-- Detection
|
||||
|
||||
--- Enum contains identifiers of surface types.
|
||||
-- @type Controller.Detection
|
||||
-- @field VISUAL
|
||||
-- @field OPTIC
|
||||
-- @field RADAR
|
||||
-- @field IRST
|
||||
-- @field RWR
|
||||
-- @field DLINK
|
||||
|
||||
--- Detected target.
|
||||
-- @type DetectedTarget
|
||||
-- @field Wrapper.Object#Object object The target
|
||||
-- @field #boolean visible The target is visible
|
||||
-- @field #boolean type The target type is known
|
||||
-- @field #boolean distance Distance to the target is known
|
||||
|
||||
|
||||
--- Checks if the target is detected or not. If one or more detection method is specified the function will return true if the target is detected by at least one of these methods. If no detection methods are specified the function will return true if the target is detected by any method.
|
||||
-- @function [parent=#Controller] isTargetDetected
|
||||
-- @param self
|
||||
-- @param Wrapper.Object#Object target Target to check
|
||||
-- @param #Controller.Detection detection Controller.Detection detection1, Controller.Detection detection2, ... Controller.Detection detectionN
|
||||
-- @return #boolean detected True if the target is detected.
|
||||
-- @return #boolean visible Has effect only if detected is true. True if the target is visible now.
|
||||
-- @return #ModelTime lastTime Has effect only if visible is false. Last time when target was seen.
|
||||
-- @return #boolean type Has effect only if detected is true. True if the target type is known.
|
||||
-- @return #boolean distance Has effect only if detected is true. True if the distance to the target is known.
|
||||
-- @return #Vec3 lastPos Has effect only if visible is false. Last position of the target when it was seen.
|
||||
-- @return #Vec3 lastVel Has effect only if visible is false. Last velocity of the target when it was seen.
|
||||
|
||||
|
||||
--- Returns list of detected targets. If one or more detection method is specified the function will return targets which were detected by at least one of these methods. If no detection methods are specified the function will return targets which were detected by any method.
|
||||
-- @function [parent=#Controller] getDetectedTargets
|
||||
-- @param self
|
||||
-- @param #Controller.Detection detection Controller.Detection detection1, Controller.Detection detection2, ... Controller.Detection detectionN
|
||||
-- @return #list<#DetectedTarget> array of DetectedTarget
|
||||
|
||||
--- Know a target.
|
||||
-- @function [parent=#Controller] knowTarget
|
||||
-- @param self
|
||||
-- @param Wrapper.Object#Object object The target.
|
||||
-- @param #boolean type Target type is known.
|
||||
-- @param #boolean distance Distance to target is known.
|
||||
|
||||
|
||||
Controller = {} --#Controller
|
||||
83
Moose Development/Moose/Dcs/DCSGroup.lua
Normal file
83
Moose Development/Moose/Dcs/DCSGroup.lua
Normal file
@@ -0,0 +1,83 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- @module DCSGroup
|
||||
|
||||
--- Represents group of Units.
|
||||
-- @type Group
|
||||
-- @field #ID ID Identifier of a group. It is assigned to a group by Mission Editor automatically.
|
||||
-- @field #Group.Category Category Enum contains identifiers of group types.
|
||||
|
||||
--- Enum contains identifiers of group types.
|
||||
-- @type Group.Category
|
||||
-- @field AIRPLANE
|
||||
-- @field HELICOPTER
|
||||
-- @field GROUND
|
||||
-- @field SHIP
|
||||
|
||||
-- Static Functions
|
||||
|
||||
--- Returns group by the name assigned to the group in Mission Editor.
|
||||
-- @function [parent=#Group] getByName
|
||||
-- @param #string name
|
||||
-- @return #Group
|
||||
|
||||
-- Member Functions
|
||||
|
||||
--- returns true if the group exist or false otherwise.
|
||||
-- @function [parent=#Group] isExist
|
||||
-- @param #Group self
|
||||
-- @return #boolean
|
||||
|
||||
--- Destroys the group and all of its units.
|
||||
-- @function [parent=#Group] destroy
|
||||
-- @param #Group self
|
||||
|
||||
--- Returns category of the group.
|
||||
-- @function [parent=#Group] getCategory
|
||||
-- @param #Group self
|
||||
-- @return #Group.Category
|
||||
|
||||
--TODO check coalition.side
|
||||
|
||||
--- Returns the coalition of the group.
|
||||
-- @function [parent=#Group] getCoalition
|
||||
-- @param #Group self
|
||||
-- @return Dcs.DCSCoalitionWrapper.Object#coalition.side
|
||||
|
||||
--- Returns the group's name. This is the same name assigned to the group in Mission Editor.
|
||||
-- @function [parent=#Group] getName
|
||||
-- @param #Group self
|
||||
-- @return #string
|
||||
|
||||
--- Returns the group identifier.
|
||||
-- @function [parent=#Group] getID
|
||||
-- @param #Group self
|
||||
-- @return #ID
|
||||
|
||||
--- Returns the unit with number unitNumber. If the unit is not exists the function will return nil.
|
||||
-- @function [parent=#Group] getUnit
|
||||
-- @param #Group self
|
||||
-- @param #number unitNumber
|
||||
-- @return Dcs.DCSWrapper.Unit#Unit
|
||||
|
||||
--- Returns current size of the group. If some of the units will be destroyed, As units are destroyed the size of the group will be changed.
|
||||
-- @function [parent=#Group] getSize
|
||||
-- @param #Group self
|
||||
-- @return #number
|
||||
|
||||
--- Returns initial size of the group. If some of the units will be destroyed, initial size of the group will not be changed. Initial size limits the unitNumber parameter for Group.getUnit() function.
|
||||
-- @function [parent=#Group] getInitialSize
|
||||
-- @param #Group self
|
||||
-- @return #number
|
||||
|
||||
--- Returns array of the units present in the group now. Destroyed units will not be enlisted at all.
|
||||
-- @function [parent=#Group] getUnits
|
||||
-- @param #Group self
|
||||
-- @return #list<Dcs.DCSWrapper.Unit#Unit> array of Units
|
||||
|
||||
--- Returns controller of the group.
|
||||
-- @function [parent=#Group] getController
|
||||
-- @param #Group self
|
||||
-- @return Controller#Controller
|
||||
|
||||
Group = {} --#Group
|
||||
|
||||
73
Moose Development/Moose/Dcs/DCSObject.lua
Normal file
73
Moose Development/Moose/Dcs/DCSObject.lua
Normal file
@@ -0,0 +1,73 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- @module DCSObject
|
||||
|
||||
--- @type Object
|
||||
-- @field #Object.Category Category
|
||||
-- @field #Object.Desc Desc
|
||||
|
||||
--- @type Object.Category
|
||||
-- @field UNIT
|
||||
-- @field WEAPON
|
||||
-- @field STATIC
|
||||
-- @field SCENERY
|
||||
-- @field BASE
|
||||
|
||||
--- @type Object.Desc
|
||||
-- @extends #Desc
|
||||
-- @field #number life initial life level
|
||||
-- @field #Box3 box bounding box of collision geometry
|
||||
|
||||
--- @function [parent=#Object] isExist
|
||||
-- @param #Object self
|
||||
-- @return #boolean
|
||||
|
||||
--- @function [parent=#Object] destroy
|
||||
-- @param #Object self
|
||||
|
||||
--- @function [parent=#Object] getCategory
|
||||
-- @param #Object self
|
||||
-- @return #Object.Category
|
||||
|
||||
--- Returns type name of the Object.
|
||||
-- @function [parent=#Object] getTypeName
|
||||
-- @param #Object self
|
||||
-- @return #string
|
||||
|
||||
--- Returns object descriptor.
|
||||
-- @function [parent=#Object] getDesc
|
||||
-- @param #Object self
|
||||
-- @return #Object.Desc
|
||||
|
||||
--- Returns true if the object belongs to the category.
|
||||
-- @function [parent=#Object] hasAttribute
|
||||
-- @param #Object self
|
||||
-- @param #AttributeName attributeName Attribute name to check.
|
||||
-- @return #boolean
|
||||
|
||||
--- Returns name of the object. This is the name that is assigned to the object in the Mission Editor.
|
||||
-- @function [parent=#Object] getName
|
||||
-- @param #Object self
|
||||
-- @return #string
|
||||
|
||||
--- Returns object coordinates for current time.
|
||||
-- @function [parent=#Object] getPoint
|
||||
-- @param #Object self
|
||||
-- @return #Vec3
|
||||
|
||||
--- Returns object position for current time.
|
||||
-- @function [parent=#Object] getPosition
|
||||
-- @param #Object self
|
||||
-- @return #Position3
|
||||
|
||||
--- Returns the unit's velocity vector.
|
||||
-- @function [parent=#Object] getVelocity
|
||||
-- @param #Object self
|
||||
-- @return #Vec3
|
||||
|
||||
--- Returns true if the unit is in air.
|
||||
-- @function [parent=#Object] inAir
|
||||
-- @param #Object self
|
||||
-- @return #boolean
|
||||
|
||||
Object = {} --#Object
|
||||
|
||||
34
Moose Development/Moose/Dcs/DCSStaticObject.lua
Normal file
34
Moose Development/Moose/Dcs/DCSStaticObject.lua
Normal file
@@ -0,0 +1,34 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- @module DCSStaticObject
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- @module StaticObject
|
||||
-- @extends CoalitionWrapper.Object#CoalitionObject
|
||||
|
||||
--- Represents static object added in the Mission Editor.
|
||||
-- @type StaticObject
|
||||
-- @field #StaticObject.ID ID Identifier of a StaticObject. It assigned to an StaticObject by the Mission Editor automatically.
|
||||
-- @field #StaticObject.Desc Desc Descriptor of StaticObject and Unit are equal. StaticObject is just a passive variant of Unit.
|
||||
|
||||
--- StaticObject descriptor. Airdromes are unique and their types are unique, but helipads and ships are not always unique and may have the same type.
|
||||
-- @type StaticObject.Desc
|
||||
-- @extends Wrapper.Unit#Unit.Desc
|
||||
|
||||
--- Returns static object by its name. If no static object found nil will be returned.
|
||||
-- @function [parent=#StaticObject] getByName
|
||||
-- @param #string name Name of static object to find.
|
||||
-- @return #StaticObject
|
||||
|
||||
--- returns identifier of the static object.
|
||||
-- @function [parent=#StaticObject] getID
|
||||
-- @param #StaticObject self
|
||||
-- @return #StaticObject.ID
|
||||
|
||||
--- Returns descriptor of the StaticObject.
|
||||
-- @function [parent=#StaticObject] getDesc
|
||||
-- @param #StaticObject self
|
||||
-- @return #StaticObject.Desc
|
||||
|
||||
|
||||
StaticObject = {} --#StaticObject
|
||||
15
Moose Development/Moose/Dcs/DCSTask.lua
Normal file
15
Moose Development/Moose/Dcs/DCSTask.lua
Normal file
@@ -0,0 +1,15 @@
|
||||
--- @module DCSTask
|
||||
|
||||
|
||||
--- A task descriptor (internal structure for DCS World)
|
||||
-- @type Task
|
||||
-- @field #string id
|
||||
-- @field #Task.param param
|
||||
|
||||
--- @type Task.param
|
||||
|
||||
--- List of @{#Task}
|
||||
-- @type TaskArray
|
||||
-- @list <#Task>
|
||||
|
||||
env.info( "Task defined" )
|
||||
8
Moose Development/Moose/Dcs/DCSTime.lua
Normal file
8
Moose Development/Moose/Dcs/DCSTime.lua
Normal file
@@ -0,0 +1,8 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- @module DCSTime
|
||||
|
||||
--- @type ModelTime
|
||||
-- @extends #number
|
||||
|
||||
--- @type Time
|
||||
-- @extends #number
|
||||
246
Moose Development/Moose/Dcs/DCSTypes.lua
Normal file
246
Moose Development/Moose/Dcs/DCSTypes.lua
Normal file
@@ -0,0 +1,246 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- @module DCSTypes
|
||||
|
||||
|
||||
|
||||
--- Time is given in seconds.
|
||||
-- @type Time
|
||||
-- @extends #number
|
||||
|
||||
--- Model time is the time that drives the simulation. Model time may be stopped, accelerated and decelerated relative real time.
|
||||
-- @type ModelTime
|
||||
-- @extends #number
|
||||
|
||||
--- Mission time is a model time plus time of the mission start.
|
||||
-- @type MissionTime
|
||||
-- @extends #number
|
||||
|
||||
|
||||
--- Distance is given in meters.
|
||||
-- @type Distance
|
||||
-- @extends #number
|
||||
|
||||
--- Angle is given in radians.
|
||||
-- @type Angle
|
||||
-- @extends #number
|
||||
|
||||
--- Azimuth is an angle of rotation around world axis y counter-clockwise.
|
||||
-- @type Azimuth
|
||||
-- @extends #number
|
||||
|
||||
--- Mass is given in kilograms.
|
||||
-- @type Mass
|
||||
-- @extends #number
|
||||
|
||||
--- Vec3 type is a 3D-vector.
|
||||
-- DCS world has 3-dimensional coordinate system. DCS ground is an infinite plain.
|
||||
-- @type Vec3
|
||||
-- @field #Distance x is directed to the north
|
||||
-- @field #Distance z is directed to the east
|
||||
-- @field #Distance y is directed up
|
||||
|
||||
--- Vec2 is a 2D-vector for the ground plane as a reference plane.
|
||||
-- @type Vec2
|
||||
-- @field #Distance x Vec2.x = Vec3.x
|
||||
-- @field #Distance y Vec2.y = Vec3.z
|
||||
|
||||
--- Position is a composite structure. It consists of both coordinate vector and orientation matrix. Position3 (also known as "Pos3" for short) is a table that has following format:
|
||||
-- @type Position3
|
||||
-- @field #Vec3 p
|
||||
-- @field #Vec3 x
|
||||
-- @field #Vec3 y
|
||||
-- @field #Vec3 z
|
||||
|
||||
--- 3-dimensional box.
|
||||
-- @type Box3
|
||||
-- @field #Vec3 min
|
||||
-- @field #Vec3 max
|
||||
|
||||
--- Each object belongs to a type. Object type is a named couple of properties those independent of mission and common for all units of the same type. Name of unit type is a string. Samples of unit type: "Su-27", "KAMAZ" and "M2 Bradley".
|
||||
-- @type TypeName
|
||||
-- @extends #string
|
||||
|
||||
--- AttributeName = string
|
||||
-- Each object type may have attributes.
|
||||
-- Attributes are enlisted in ./Scripts/Database/db_attributes.Lua.
|
||||
-- To know what attributes the object type has, look for the unit type script in sub-directories planes/, helicopter/s, vehicles, navy/ of ./Scripts/Database/ directory.
|
||||
-- @type AttributeName
|
||||
-- @extends #string
|
||||
|
||||
--- List of @{#AttributeName}
|
||||
-- @type AttributeNameArray
|
||||
-- @list <#AttributeName>
|
||||
|
||||
--- @type AI
|
||||
-- @field #AI.Skill Skill
|
||||
-- @field #AI.Task Task
|
||||
-- @field #AI.Option Option
|
||||
|
||||
--- @type AI.Skill
|
||||
-- @field AVERAGE
|
||||
-- @field GOOD
|
||||
-- @field HIGH
|
||||
-- @field EXCELLENT
|
||||
-- @field PLAYER
|
||||
-- @field CLIENT
|
||||
|
||||
--- @type AI.Task
|
||||
-- @field #AI.Task.WeaponExpend WeaponExpend
|
||||
-- @field #AI.Task.OrbitPattern OrbitPattern
|
||||
-- @field #AI.Task.Designation Designation
|
||||
-- @field #AI.Task.WaypointType WaypointType
|
||||
-- @field #AI.Task.TurnMethod TurnMethod
|
||||
-- @field #AI.Task.AltitudeType AltitudeType
|
||||
-- @field #AI.Task.VehicleFormation VehicleFormation
|
||||
|
||||
--- @type AI.Task.WeaponExpend
|
||||
-- @field ONE
|
||||
-- @field TWO
|
||||
-- @field FOUR
|
||||
-- @field QUARTER
|
||||
-- @field HALF
|
||||
-- @field ALL
|
||||
|
||||
--- @type AI.Task.OrbitPattern
|
||||
-- @field CIRCLE
|
||||
-- @field RACE_TRACK
|
||||
|
||||
--- @type AI.Task.Designation
|
||||
-- @field NO
|
||||
-- @field AUTO
|
||||
-- @field WP
|
||||
-- @field IR_POINTER
|
||||
-- @field LASER
|
||||
|
||||
--- @type AI.Task.WaypointType
|
||||
-- @field TAKEOFF
|
||||
-- @field TAKEOFF_PARKING
|
||||
-- @field TURNING_POINT
|
||||
-- @field LAND
|
||||
|
||||
--- @type AI.Task.TurnMethod
|
||||
-- @field FLY_OVER_POINT
|
||||
-- @field FIN_POINT
|
||||
|
||||
--- @type AI.Task.AltitudeType
|
||||
-- @field BARO
|
||||
-- @field RADIO
|
||||
|
||||
--- @type AI.Task.VehicleFormation
|
||||
-- @field OFF_ROAD
|
||||
-- @field ON_ROAD
|
||||
-- @field RANK
|
||||
-- @field CONE
|
||||
-- @field DIAMOND
|
||||
-- @field VEE
|
||||
-- @field ECHELON_LEFT
|
||||
-- @field ECHELON_RIGHT
|
||||
|
||||
--- @type AI.Option
|
||||
-- @field #AI.Option.Air Air
|
||||
-- @field #AI.Option.Ground Ground
|
||||
-- @field #AI.Option.Naval Naval
|
||||
|
||||
--- @type AI.Option.Air
|
||||
-- @field #AI.Option.Air.id id
|
||||
-- @field #AI.Option.Air.val val
|
||||
|
||||
--- @type AI.Option.Ground
|
||||
-- @field #AI.Option.Ground.id id
|
||||
-- @field #AI.Option.Ground.val val
|
||||
|
||||
--- @type AI.Option.Naval
|
||||
-- @field #AI.Option.Naval.id id
|
||||
-- @field #AI.Option.Naval.val val
|
||||
|
||||
--TODO: work on formation
|
||||
--- @type AI.Option.Air.id
|
||||
-- @field NO_OPTION
|
||||
-- @field ROE
|
||||
-- @field REACTION_ON_THREAT
|
||||
-- @field RADAR_USING
|
||||
-- @field FLARE_USING
|
||||
-- @field FORMATION
|
||||
-- @field RTB_ON_BINGO
|
||||
-- @field SILENCE
|
||||
|
||||
--- @type AI.Option.Air.val
|
||||
-- @field #AI.Option.Air.val.ROE ROE
|
||||
-- @field #AI.Option.Air.val.REACTION_ON_THREAT REACTION_ON_THREAT
|
||||
-- @field #AI.Option.Air.val.RADAR_USING RADAR_USING
|
||||
-- @field #AI.Option.Air.val.FLARE_USING FLARE_USING
|
||||
|
||||
--- @type AI.Option.Air.val.ROE
|
||||
-- @field WEAPON_FREE
|
||||
-- @field OPEN_FIRE_WEAPON_FREE
|
||||
-- @field OPEN_FIRE
|
||||
-- @field RETURN_FIRE
|
||||
-- @field WEAPON_HOLD
|
||||
|
||||
--- @type AI.Option.Air.val.REACTION_ON_THREAT
|
||||
-- @field NO_REACTION
|
||||
-- @field PASSIVE_DEFENCE
|
||||
-- @field EVADE_FIRE
|
||||
-- @field BYPASS_AND_ESCAPE
|
||||
-- @field ALLOW_ABORT_MISSION
|
||||
|
||||
--- @type AI.Option.Air.val.RADAR_USING
|
||||
-- @field NEVER
|
||||
-- @field FOR_ATTACK_ONLY
|
||||
-- @field FOR_SEARCH_IF_REQUIRED
|
||||
-- @field FOR_CONTINUOUS_SEARCH
|
||||
|
||||
--- @type AI.Option.Air.val.FLARE_USING
|
||||
-- @field NEVER
|
||||
-- @field AGAINST_FIRED_MISSILE
|
||||
-- @field WHEN_FLYING_IN_SAM_WEZ
|
||||
-- @field WHEN_FLYING_NEAR_ENEMIES
|
||||
|
||||
--- @type AI.Option.Ground.id
|
||||
-- @field NO_OPTION
|
||||
-- @field ROE @{#AI.Option.Ground.val.ROE}
|
||||
-- @field DISPERSE_ON_ATTACK true or false
|
||||
-- @field ALARM_STATE @{#AI.Option.Ground.val.ALARM_STATE}
|
||||
|
||||
--- @type AI.Option.Ground.val
|
||||
-- @field #AI.Option.Ground.val.ROE ROE
|
||||
-- @field #AI.Option.Ground.val.ALARM_STATE ALARM_STATE
|
||||
|
||||
--- @type AI.Option.Ground.val.ROE
|
||||
-- @field OPEN_FIRE
|
||||
-- @field RETURN_FIRE
|
||||
-- @field WEAPON_HOLD
|
||||
|
||||
--- @type AI.Option.Ground.val.ALARM_STATE
|
||||
-- @field AUTO
|
||||
-- @field GREEN
|
||||
-- @field RED
|
||||
|
||||
--- @type AI.Option.Naval.id
|
||||
-- @field NO_OPTION
|
||||
-- @field ROE
|
||||
|
||||
--- @type AI.Option.Naval.val
|
||||
-- @field #AI.Option.Naval.val.ROE ROE
|
||||
|
||||
--- @type AI.Option.Naval.val.ROE
|
||||
-- @field OPEN_FIRE
|
||||
-- @field RETURN_FIRE
|
||||
-- @field WEAPON_HOLD
|
||||
|
||||
AI = {} --#AI
|
||||
|
||||
|
||||
--- @type Desc
|
||||
-- @field #TypeName typeName type name
|
||||
-- @field #string displayName localized display name
|
||||
-- @field #table attributes object type attributes
|
||||
|
||||
--- A distance type
|
||||
-- @type Distance
|
||||
|
||||
--- An angle type
|
||||
-- @type Angle
|
||||
|
||||
env.info( 'AI types created' )
|
||||
|
||||
241
Moose Development/Moose/Dcs/DCSUnit.lua
Normal file
241
Moose Development/Moose/Dcs/DCSUnit.lua
Normal file
@@ -0,0 +1,241 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- @module DCSUnit
|
||||
|
||||
--- @type Unit
|
||||
-- @extends Dcs.DCSCoalitionWrapper.Object#CoalitionObject
|
||||
-- @field ID Identifier of an unit. It assigned to an unit by the Mission Editor automatically.
|
||||
-- @field #Unit.Category Category
|
||||
-- @field #Unit.RefuelingSystem RefuelingSystem
|
||||
-- @field #Unit.SensorType SensorType
|
||||
-- @field #Unit.OpticType OpticType
|
||||
-- @field #Unit.RadarType RadarType
|
||||
-- @field #Unit.Desc Desc
|
||||
-- @field #Unit.DescAircraft DescAircraft
|
||||
-- @field #Unit.DescAirplane DescAirplane
|
||||
-- @field #Unit.DescHelicopter DescHelicopter
|
||||
-- @field #Unit.DescVehicle DescVehicle
|
||||
-- @field #Unit.DescShip DescShip
|
||||
-- @field #Unit.AmmoItem AmmoItem
|
||||
-- @field #list<#Unit.AmmoItem> Ammo
|
||||
-- @field #Unit.Sensor Sensor
|
||||
-- @field #Unit.Optic Optic
|
||||
-- @field #Unit.Radar Radar
|
||||
-- @field #Unit.IRST IRST
|
||||
|
||||
|
||||
--- Enum that stores unit categories.
|
||||
-- @type Unit.Category
|
||||
-- @field AIRPLANE
|
||||
-- @field HELICOPTER
|
||||
-- @field GROUND_UNIT
|
||||
-- @field SHIP
|
||||
-- @field STRUCTURE
|
||||
|
||||
--- Enum that stores aircraft refueling system types.
|
||||
-- @type Unit.RefuelingSystem
|
||||
-- @field BOOM_AND_RECEPTACLE
|
||||
-- @field PROBE_AND_DROGUE
|
||||
|
||||
--- Enum that stores sensor types.
|
||||
-- @type Unit.SensorType
|
||||
-- @field OPTIC
|
||||
-- @field RADAR
|
||||
-- @field IRST
|
||||
-- @field RWR
|
||||
|
||||
--- Enum that stores types of optic sensors.
|
||||
-- @type Unit.OpticType
|
||||
-- @field TV TV-sensor
|
||||
-- @field LLTV Low-level TV-sensor
|
||||
-- @field IR Infra-Red optic sensor
|
||||
|
||||
--- Enum that stores radar types.
|
||||
-- @type Unit.RadarType
|
||||
-- @field AS air search radar
|
||||
-- @field SS surface/land search radar
|
||||
|
||||
|
||||
--- A unit descriptor.
|
||||
-- @type Unit.Desc
|
||||
-- @extends Wrapper.Object#Object.Desc
|
||||
-- @field #Unit.Category category Unit Category
|
||||
-- @field #Mass massEmpty mass of empty unit
|
||||
-- @field #number speedMax istance / Time, --maximal velocity
|
||||
|
||||
--- An aircraft descriptor.
|
||||
-- @type Unit.DescAircraft
|
||||
-- @extends Wrapper.Unit#Unit.Desc
|
||||
-- @field #Mass fuelMassMax maximal inner fuel mass
|
||||
-- @field #Distance range Operational range
|
||||
-- @field #Distance Hmax Ceiling
|
||||
-- @field #number VyMax #Distance / #Time, --maximal climb rate
|
||||
-- @field #number NyMin minimal safe acceleration
|
||||
-- @field #number NyMax maximal safe acceleration
|
||||
-- @field #Unit.RefuelingSystem tankerType refueling system type
|
||||
|
||||
--- An airplane descriptor.
|
||||
-- @type Unit.DescAirplane
|
||||
-- @extends Wrapper.Unit#Unit.DescAircraft
|
||||
-- @field #number speedMax0 Distance / Time maximal TAS at ground level
|
||||
-- @field #number speedMax10K Distance / Time maximal TAS at altitude of 10 km
|
||||
|
||||
--- A helicopter descriptor.
|
||||
-- @type Unit.DescHelicopter
|
||||
-- @extends Wrapper.Unit#Unit.DescAircraft
|
||||
-- @field #Distance HmaxStat static ceiling
|
||||
|
||||
--- A vehicle descriptor.
|
||||
-- @type Unit.DescVehicle
|
||||
-- @extends Wrapper.Unit#Unit.Desc
|
||||
-- @field #Angle maxSlopeAngle maximal slope angle
|
||||
-- @field #boolean riverCrossing can the vehicle cross a rivers
|
||||
|
||||
--- A ship descriptor.
|
||||
-- @type Unit.DescShip
|
||||
-- @extends #Unit.Desc
|
||||
|
||||
--- ammunition item: "type-count" pair.
|
||||
-- @type Unit.AmmoItem
|
||||
-- @field #Weapon.Desc desc ammunition descriptor
|
||||
-- @field #number count ammunition count
|
||||
|
||||
--- A unit sensor.
|
||||
-- @type Unit.Sensor
|
||||
-- @field #TypeName typeName
|
||||
-- @field #Unit.SensorType type
|
||||
|
||||
--- An optic sensor.
|
||||
-- @type Unit.Optic
|
||||
-- @extends Wrapper.Unit#Unit.Sensor
|
||||
-- @field #Unit.OpticType opticType
|
||||
|
||||
--- A radar.
|
||||
-- @type Unit.Radar
|
||||
-- @extends Wrapper.Unit#Unit.Sensor
|
||||
-- @field #Distance detectionDistanceRBM detection distance for RCS=1m^2 in real-beam mapping mode, nil if radar doesn't support surface/land search
|
||||
-- @field #Distance detectionDistanceHRM detection distance for RCS=1m^2 in high-resolution mapping mode, nil if radar has no HRM
|
||||
-- @field #Unit.Radar.detectionDistanceAir detectionDistanceAir detection distance for RCS=1m^2 airborne target, nil if radar doesn't support air search
|
||||
|
||||
--- @type Unit.Radar.detectionDistanceAir
|
||||
-- @field #Unit.Radar.detectionDistanceAir.upperHemisphere upperHemisphere
|
||||
-- @field #Unit.Radar.detectionDistanceAir.lowerHemisphere lowerHemisphere
|
||||
|
||||
--- @type Unit.Radar.detectionDistanceAir.upperHemisphere
|
||||
-- @field #Distance headOn
|
||||
-- @field #Distance tailOn
|
||||
|
||||
--- @type Unit.Radar.detectionDistanceAir.lowerHemisphere
|
||||
-- @field #Distance headOn
|
||||
-- @field #Distance tailOn
|
||||
|
||||
--- An IRST.
|
||||
-- @type Wrapper.Unit#Unit.IRST
|
||||
-- @extends Unit.Sensor
|
||||
-- @field #Distance detectionDistanceIdle detection of tail-on target with heat signature = 1 in upper hemisphere, engines are in idle
|
||||
-- @field #Distance detectionDistanceMaximal ..., engines are in maximal mode
|
||||
-- @field #Distance detectionDistanceAfterburner ..., engines are in afterburner mode
|
||||
|
||||
--- An RWR.
|
||||
-- @type Unit.RWR
|
||||
-- @extends Wrapper.Unit#Unit.Sensor
|
||||
|
||||
--- table that stores all unit sensors.
|
||||
-- TODO @type Sensors
|
||||
--
|
||||
|
||||
|
||||
--- Returns unit object by the name assigned to the unit in Mission Editor. If there is unit with such name or the unit is destroyed the function will return nil. The function provides access to non-activated units too.
|
||||
-- @function [parent=#Unit] getByName
|
||||
-- @param #string name
|
||||
-- @return #Unit
|
||||
|
||||
--- Returns if the unit is activated.
|
||||
-- @function [parent=#Unit] isActive
|
||||
-- @param #Unit self
|
||||
-- @return #boolean
|
||||
|
||||
--- Returns name of the player that control the unit or nil if the unit is controlled by A.I.
|
||||
-- @function [parent=#Unit] getPlayerName
|
||||
-- @param #Unit self
|
||||
-- @return #string
|
||||
|
||||
--- returns the unit's unique identifier.
|
||||
-- @function [parent=#Unit] getID
|
||||
-- @param #Unit self
|
||||
-- @return #Unit.ID
|
||||
|
||||
|
||||
--- Returns the unit's number in the group. The number is the same number the unit has in ME. It may not be changed during the mission. If any unit in the group is destroyed, the numbers of another units will not be changed.
|
||||
-- @function [parent=#Unit] getNumber
|
||||
-- @param #Unit self
|
||||
-- @return #number
|
||||
|
||||
--- Returns controller of the unit if it exist and nil otherwise
|
||||
-- @function [parent=#Unit] getController
|
||||
-- @param #Unit self
|
||||
-- @return #Controller
|
||||
|
||||
--- Returns the unit's group if it exist and nil otherwise
|
||||
-- @function [parent=#Unit] getGroup
|
||||
-- @param #Unit self
|
||||
-- @return Dcs.DCSWrapper.Group#Group
|
||||
|
||||
--- Returns the unit's callsign - the localized string.
|
||||
-- @function [parent=#Unit] getCallsign
|
||||
-- @param #Unit self
|
||||
-- @return #string
|
||||
|
||||
--- Returns the unit's health. Dead units has health <= 1.0
|
||||
-- @function [parent=#Unit] getLife
|
||||
-- @param #Unit self
|
||||
-- @return #number
|
||||
|
||||
--- returns the unit's initial health.
|
||||
-- @function [parent=#Unit] getLife0
|
||||
-- @param #Unit self
|
||||
-- @return #number
|
||||
|
||||
--- Returns relative amount of fuel (from 0.0 to 1.0) the unit has in its internal tanks. If there are additional fuel tanks the value may be greater than 1.0.
|
||||
-- @function [parent=#Unit] getFuel
|
||||
-- @param #Unit self
|
||||
-- @return #number
|
||||
|
||||
--- Returns the unit ammunition.
|
||||
-- @function [parent=#Unit] getAmmo
|
||||
-- @param #Unit self
|
||||
-- @return #Unit.Ammo
|
||||
|
||||
--- Returns the unit sensors.
|
||||
-- @function [parent=#Unit] getSensors
|
||||
-- @param #Unit self
|
||||
-- @return #Unit.Sensors
|
||||
|
||||
--- Returns true if the unit has specified types of sensors. This function is more preferable than Unit.getSensors() if you don't want to get information about all the unit's sensors, and just want to check if the unit has specified types of sensors.
|
||||
-- @function [parent=#Unit] hasSensors
|
||||
-- @param #Unit self
|
||||
-- @param #Unit.SensorType sensorType (= nil) Sensor type.
|
||||
-- @param ... Additional parameters.
|
||||
-- @return #boolean
|
||||
-- @usage
|
||||
-- If sensorType is Unit.SensorType.OPTIC, additional parameters are optic sensor types. Following example checks if the unit has LLTV or IR optics:
|
||||
-- unit:hasSensors(Unit.SensorType.OPTIC, Unit.OpticType.LLTV, Unit.OpticType.IR)
|
||||
-- If sensorType is Unit.SensorType.RADAR, additional parameters are radar types. Following example checks if the unit has air search radars:
|
||||
-- unit:hasSensors(Unit.SensorType.RADAR, Unit.RadarType.AS)
|
||||
-- If no additional parameters are specified the function returns true if the unit has at least one sensor of specified type.
|
||||
-- If sensor type is not specified the function returns true if the unit has at least one sensor of any type.
|
||||
--
|
||||
|
||||
--- returns two values:
|
||||
-- First value indicates if at least one of the unit's radar(s) is on.
|
||||
-- Second value is the object of the radar's interest. Not nil only if at least one radar of the unit is tracking a target.
|
||||
-- @function [parent=#Unit] getRadar
|
||||
-- @param #Unit self
|
||||
-- @return #boolean, Wrapper.Object#Object
|
||||
|
||||
--- Returns unit descriptor. Descriptor type depends on unit category.
|
||||
-- @function [parent=#Unit] getDesc
|
||||
-- @param #Unit self
|
||||
-- @return #Unit.Desc
|
||||
|
||||
|
||||
Unit = {} --#Unit
|
||||
11
Moose Development/Moose/Dcs/DCSVec3.lua
Normal file
11
Moose Development/Moose/Dcs/DCSVec3.lua
Normal file
@@ -0,0 +1,11 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- @module DCSVec3
|
||||
|
||||
|
||||
|
||||
---
|
||||
-- @type Vec3
|
||||
-- @field #number x
|
||||
-- @field #number y
|
||||
-- @field #number z
|
||||
Vec3 = {}
|
||||
10
Moose Development/Moose/Dcs/DCSZone.lua
Normal file
10
Moose Development/Moose/Dcs/DCSZone.lua
Normal file
@@ -0,0 +1,10 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- @module DCSZone
|
||||
|
||||
|
||||
|
||||
---
|
||||
-- @type Zone
|
||||
-- @field DCSVec3#Vec3 point
|
||||
-- @field #number radius
|
||||
Zone = {}
|
||||
16
Moose Development/Moose/Dcs/DCScoalition.lua
Normal file
16
Moose Development/Moose/Dcs/DCScoalition.lua
Normal file
@@ -0,0 +1,16 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- @module DCScoalition
|
||||
|
||||
--- @type coalition
|
||||
-- @field #coalition.side side
|
||||
|
||||
--- @type coalition.side
|
||||
-- @field NEUTRAL
|
||||
-- @field RED
|
||||
-- @field BLUE
|
||||
|
||||
--- @function [parent=#coalition] getCountryCoalition
|
||||
-- @param #number countryId
|
||||
-- @return #number coalitionId
|
||||
|
||||
coalition = coalition -- #coalition
|
||||
27
Moose Development/Moose/Dcs/DCScountry.lua
Normal file
27
Moose Development/Moose/Dcs/DCScountry.lua
Normal file
@@ -0,0 +1,27 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- @module DCScountry
|
||||
|
||||
--- @type country
|
||||
-- @field #country.id id
|
||||
country = country -- #country
|
||||
|
||||
--- @type country.id
|
||||
-- @field RUSSIA
|
||||
-- @field UKRAINE
|
||||
-- @field USA
|
||||
-- @field TURKEY
|
||||
-- @field UK
|
||||
-- @field FRANCE
|
||||
-- @field GERMANY
|
||||
-- @field CANADA
|
||||
-- @field SPAIN
|
||||
-- @field THE_NETHERLANDS
|
||||
-- @field BELGIUM
|
||||
-- @field NORWAY
|
||||
-- @field DENMARK
|
||||
-- @field ISRAEL
|
||||
-- @field GEORGIA
|
||||
-- @field INSURGENTS
|
||||
-- @field ABKHAZIA
|
||||
-- @field SOUTH_OSETIA
|
||||
-- @field ITALY
|
||||
27
Moose Development/Moose/Dcs/DCSenv.lua
Normal file
27
Moose Development/Moose/Dcs/DCSenv.lua
Normal file
@@ -0,0 +1,27 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- @module env
|
||||
|
||||
--- @type env
|
||||
|
||||
--- Add message to simulator log with caption "INFO". Message box is optional.
|
||||
-- @function [parent=#env] info
|
||||
-- @field #string message message string to add to log.
|
||||
-- @field #boolean showMessageBox If the parameter is true Message Box will appear. Optional.
|
||||
|
||||
--- Add message to simulator log with caption "WARNING". Message box is optional.
|
||||
-- @function [parent=#env] warning
|
||||
-- @field #string message message string to add to log.
|
||||
-- @field #boolean showMessageBox If the parameter is true Message Box will appear. Optional.
|
||||
|
||||
--- Add message to simulator log with caption "ERROR". Message box is optional.
|
||||
-- @function [parent=#env] error
|
||||
-- @field #string message message string to add to log.
|
||||
-- @field #boolean showMessageBox If the parameter is true Message Box will appear. Optional.
|
||||
|
||||
--- Enables/disables appearance of message box each time lua error occurs.
|
||||
-- @function [parent=#env] setErrorMessageBoxEnabled
|
||||
-- @field #boolean on if true message box appearance is enabled.
|
||||
|
||||
|
||||
|
||||
env = {} --#env
|
||||
26
Moose Development/Moose/Dcs/DCSland.lua
Normal file
26
Moose Development/Moose/Dcs/DCSland.lua
Normal file
@@ -0,0 +1,26 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- @module land
|
||||
|
||||
--- @type land
|
||||
-- @field #land.SurfaceType SurfaceType
|
||||
|
||||
|
||||
--- @type land.SurfaceType
|
||||
-- @field LAND
|
||||
-- @field SHALLOW_WATER
|
||||
-- @field WATER
|
||||
-- @field ROAD
|
||||
-- @field RUNWAY
|
||||
|
||||
--- Returns altitude MSL of the point.
|
||||
-- @function [parent=#land] getHeight
|
||||
-- @param #Vec2 point point on the ground.
|
||||
-- @return Dcs.DCSTypes#Distance
|
||||
|
||||
--- returns surface type at the given point.
|
||||
-- @function [parent=#land] getSurfaceType
|
||||
-- @param #Vec2 point Point on the land.
|
||||
-- @return #land.SurfaceType
|
||||
|
||||
|
||||
land = {} --#land
|
||||
45
Moose Development/Moose/Dcs/DCStimer.lua
Normal file
45
Moose Development/Moose/Dcs/DCStimer.lua
Normal file
@@ -0,0 +1,45 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- @module DCStimer
|
||||
|
||||
--- @type timer
|
||||
|
||||
|
||||
--- Returns model time in seconds.
|
||||
-- @function [parent=#timer] getTime
|
||||
-- @return #Time
|
||||
|
||||
--- Returns mission time in seconds.
|
||||
-- @function [parent=#timer] getAbsTime
|
||||
-- @return #Time
|
||||
|
||||
--- Returns mission start time in seconds.
|
||||
-- @function [parent=#timer] getTime0
|
||||
-- @return #Time
|
||||
|
||||
--- Schedules function to call at desired model time.
|
||||
-- Time function FunctionToCall(any argument, Time time)
|
||||
--
|
||||
-- ...
|
||||
--
|
||||
-- return ...
|
||||
--
|
||||
-- end
|
||||
--
|
||||
-- Must return model time of next call or nil. Note that the DCS scheduler calls the function in protected mode and any Lua errors in the called function will be trapped and not reported. If the function triggers a Lua error then it will be terminated and not scheduled to run again.
|
||||
-- @function [parent=#timer] scheduleFunction
|
||||
-- @param #FunctionToCall functionToCall Lua-function to call. Must have prototype of FunctionToCall.
|
||||
-- @param functionArgument Function argument of any type to pass to functionToCall.
|
||||
-- @param #Time time Model time of the function call.
|
||||
-- @return functionId
|
||||
|
||||
--- Re-schedules function to call at another model time.
|
||||
-- @function [parent=#timer] setFunctionTime
|
||||
-- @param functionId Lua-function to call. Must have prototype of FunctionToCall.
|
||||
-- @param #Time time Model time of the function call.
|
||||
|
||||
|
||||
--- Removes the function from schedule.
|
||||
-- @function [parent=#timer] removeFunction
|
||||
-- @param functionId Function identifier to remove from schedule
|
||||
|
||||
timer = {} --#timer
|
||||
8
Moose Development/Moose/Dcs/DCStrigger.lua
Normal file
8
Moose Development/Moose/Dcs/DCStrigger.lua
Normal file
@@ -0,0 +1,8 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- @module DCStrigger
|
||||
|
||||
|
||||
trigger = {} --#timer
|
||||
|
||||
|
||||
|
||||
35
Moose Development/Moose/Dcs/DCSworld.lua
Normal file
35
Moose Development/Moose/Dcs/DCSworld.lua
Normal file
@@ -0,0 +1,35 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- @module DCSWorld
|
||||
|
||||
--- @type world
|
||||
-- @field #world.event event
|
||||
|
||||
|
||||
--- @type world.event
|
||||
-- @field S_EVENT_INVALID
|
||||
-- @field S_EVENT_SHOT
|
||||
-- @field S_EVENT_HIT
|
||||
-- @field S_EVENT_TAKEOFF
|
||||
-- @field S_EVENT_LAND
|
||||
-- @field S_EVENT_CRASH
|
||||
-- @field S_EVENT_EJECTION
|
||||
-- @field S_EVENT_REFUELING
|
||||
-- @field S_EVENT_DEAD
|
||||
-- @field S_EVENT_PILOT_DEAD
|
||||
-- @field S_EVENT_BASE_CAPTURED
|
||||
-- @field S_EVENT_MISSION_START
|
||||
-- @field S_EVENT_MISSION_END
|
||||
-- @field S_EVENT_TOOK_CONTROL
|
||||
-- @field S_EVENT_REFUELING_STOP
|
||||
-- @field S_EVENT_BIRTH
|
||||
-- @field S_EVENT_HUMAN_FAILURE
|
||||
-- @field S_EVENT_ENGINE_STARTUP
|
||||
-- @field S_EVENT_ENGINE_SHUTDOWN
|
||||
-- @field S_EVENT_PLAYER_ENTER_UNIT
|
||||
-- @field S_EVENT_PLAYER_LEAVE_UNIT
|
||||
-- @field S_EVENT_PLAYER_COMMENT
|
||||
-- @field S_EVENT_SHOOTING_START
|
||||
-- @field S_EVENT_SHOOTING_END
|
||||
-- @field S_EVENT_MAX
|
||||
|
||||
world = {} --#world
|
||||
2311
Moose Development/Moose/Functional/ATC_Ground.lua
Normal file
2311
Moose Development/Moose/Functional/ATC_Ground.lua
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
2466
Moose Development/Moose/Functional/Artillery.lua
Normal file
2466
Moose Development/Moose/Functional/Artillery.lua
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,135 +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: **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}
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- 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 )
|
||||
-- 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
|
||||
self:F( { ZoneNames, TimeInterval } )
|
||||
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
|
||||
|
||||
self:HandleEvent( EVENTS.Birth )
|
||||
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
|
||||
|
||||
--- @param #CLEANUP self
|
||||
--- @param #CLEANUP_AIRBASE self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function CLEANUP:_OnEventBirth( EventData )
|
||||
function CLEANUP_AIRBASE.__:OnEventBirth( EventData )
|
||||
self:F( { EventData } )
|
||||
|
||||
self.CleanUpList[EventData.IniDCSUnitName] = {}
|
||||
self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnit = EventData.IniDCSUnit
|
||||
self.CleanUpList[EventData.IniDCSUnitName].CleanUpGroup = EventData.IniDCSGroup
|
||||
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
|
||||
|
||||
EventData.IniUnit:HandleEvent( EVENTS.EngineShutdown , self._EventAddForCleanUp )
|
||||
EventData.IniUnit:HandleEvent( EVENTS.EngineStartup, self._EventAddForCleanUp )
|
||||
EventData.IniUnit:HandleEvent( EVENTS.Hit, self._EventAddForCleanUp )
|
||||
EventData.IniUnit:HandleEvent( EVENTS.PilotDead, self._EventCrash )
|
||||
EventData.IniUnit:HandleEvent( EVENTS.Dead, self._EventCrash )
|
||||
EventData.IniUnit:HandleEvent( EVENTS.Crash, self._EventCrash )
|
||||
EventData.IniUnit:HandleEvent( EVENTS.Shot, self._EventShot )
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- 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.
|
||||
@@ -140,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)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,9 @@
|
||||
--- Taking the lead of AI escorting your flight.
|
||||
--- **Functional** -- Taking the lead of AI escorting your flight.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @{#ESCORT} class
|
||||
-- ================
|
||||
-- ===
|
||||
-- The @{#ESCORT} class allows you to interact with escorting AI on your flight and take the lead.
|
||||
-- Each escorting group can be commanded with a whole set of radio commands (radio menu in your flight, and then F10).
|
||||
--
|
||||
@@ -9,7 +11,7 @@
|
||||
-- Ships and Ground troops will have a more limited set, but they can provide support through the bombing of targets designated by the other escorts.
|
||||
--
|
||||
-- RADIO MENUs that can be created:
|
||||
-- ================================
|
||||
-- ===
|
||||
-- Find a summary below of the current available commands:
|
||||
--
|
||||
-- Navigation ...:
|
||||
@@ -77,13 +79,13 @@
|
||||
-- Note that this is really fantastic, as you now have the dynamic of taking control of the escort groups, and allowing them to resume their path or mission.
|
||||
--
|
||||
-- ESCORT construction methods.
|
||||
-- ============================
|
||||
-- ===
|
||||
-- Create a new SPAWN object with the @{#ESCORT.New} method:
|
||||
--
|
||||
-- * @{#ESCORT.New}: Creates a new ESCORT object from a @{Group#GROUP} for a @{Client#CLIENT}, with an optional briefing text.
|
||||
--
|
||||
-- ESCORT initialization methods.
|
||||
-- ==============================
|
||||
-- ===
|
||||
-- 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.
|
||||
@@ -127,8 +129,7 @@
|
||||
-- @field #boolean ReportTargets If true, nearby targets are reported.
|
||||
-- @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
|
||||
-- @field FunctionalMENU_GROUPDETECTION_BASE Detection
|
||||
ESCORT = {
|
||||
ClassName = "ESCORT",
|
||||
EscortName = nil, -- The Escort Name
|
||||
@@ -205,7 +206,7 @@ function ESCORT:New( EscortClient, EscortGroup, EscortName, EscortBriefing )
|
||||
self.EscortClient._EscortGroups[EscortGroup:GetName()].Detection = self.EscortGroup.Detection
|
||||
end
|
||||
|
||||
self.EscortMenu = MENU_CLIENT:New( self.EscortClient, self.EscortName )
|
||||
self.EscortMenu = MENU_GROUP:New( self.EscortClient:GetGroup(), self.EscortName )
|
||||
|
||||
self.EscortGroup:WayPointInitialize(1)
|
||||
|
||||
@@ -301,14 +302,14 @@ function ESCORT:MenuFollowAt( Distance )
|
||||
|
||||
if self.EscortGroup:IsAir() then
|
||||
if not self.EscortMenuReportNavigation then
|
||||
self.EscortMenuReportNavigation = MENU_CLIENT:New( self.EscortClient, "Navigation", self.EscortMenu )
|
||||
self.EscortMenuReportNavigation = MENU_GROUP:New( self.EscortClient:GetGroup(), "Navigation", self.EscortMenu )
|
||||
end
|
||||
|
||||
if not self.EscortMenuJoinUpAndFollow then
|
||||
self.EscortMenuJoinUpAndFollow = {}
|
||||
end
|
||||
|
||||
self.EscortMenuJoinUpAndFollow[#self.EscortMenuJoinUpAndFollow+1] = MENU_CLIENT_COMMAND:New( self.EscortClient, "Join-Up and Follow at " .. Distance, self.EscortMenuReportNavigation, ESCORT._JoinUpAndFollow, self, Distance )
|
||||
self.EscortMenuJoinUpAndFollow[#self.EscortMenuJoinUpAndFollow+1] = MENU_GROUP_COMMAND:New( self.EscortClient:GetGroup(), "Join-Up and Follow at " .. Distance, self.EscortMenuReportNavigation, ESCORT._JoinUpAndFollow, self, Distance )
|
||||
|
||||
self.EscortMode = ESCORT.MODE.FOLLOW
|
||||
end
|
||||
@@ -330,7 +331,7 @@ function ESCORT:MenuHoldAtEscortPosition( Height, Seconds, MenuTextFormat )
|
||||
if self.EscortGroup:IsAir() then
|
||||
|
||||
if not self.EscortMenuHold then
|
||||
self.EscortMenuHold = MENU_CLIENT:New( self.EscortClient, "Hold position", self.EscortMenu )
|
||||
self.EscortMenuHold = MENU_GROUP:New( self.EscortClient:GetGroup(), "Hold position", self.EscortMenu )
|
||||
end
|
||||
|
||||
if not Height then
|
||||
@@ -360,9 +361,9 @@ function ESCORT:MenuHoldAtEscortPosition( Height, Seconds, MenuTextFormat )
|
||||
self.EscortMenuHoldPosition = {}
|
||||
end
|
||||
|
||||
self.EscortMenuHoldPosition[#self.EscortMenuHoldPosition+1] = MENU_CLIENT_COMMAND
|
||||
self.EscortMenuHoldPosition[#self.EscortMenuHoldPosition+1] = MENU_GROUP_COMMAND
|
||||
:New(
|
||||
self.EscortClient,
|
||||
self.EscortClient:GetGroup(),
|
||||
MenuText,
|
||||
self.EscortMenuHold,
|
||||
ESCORT._HoldPosition,
|
||||
@@ -391,7 +392,7 @@ function ESCORT:MenuHoldAtLeaderPosition( Height, Seconds, MenuTextFormat )
|
||||
if self.EscortGroup:IsAir() then
|
||||
|
||||
if not self.EscortMenuHold then
|
||||
self.EscortMenuHold = MENU_CLIENT:New( self.EscortClient, "Hold position", self.EscortMenu )
|
||||
self.EscortMenuHold = MENU_GROUP:New( self.EscortClient:GetGroup(), "Hold position", self.EscortMenu )
|
||||
end
|
||||
|
||||
if not Height then
|
||||
@@ -421,9 +422,9 @@ function ESCORT:MenuHoldAtLeaderPosition( Height, Seconds, MenuTextFormat )
|
||||
self.EscortMenuHoldAtLeaderPosition = {}
|
||||
end
|
||||
|
||||
self.EscortMenuHoldAtLeaderPosition[#self.EscortMenuHoldAtLeaderPosition+1] = MENU_CLIENT_COMMAND
|
||||
self.EscortMenuHoldAtLeaderPosition[#self.EscortMenuHoldAtLeaderPosition+1] = MENU_GROUP_COMMAND
|
||||
:New(
|
||||
self.EscortClient,
|
||||
self.EscortClient:GetGroup(),
|
||||
MenuText,
|
||||
self.EscortMenuHold,
|
||||
ESCORT._HoldPosition,
|
||||
@@ -450,7 +451,7 @@ function ESCORT:MenuScanForTargets( Height, Seconds, MenuTextFormat )
|
||||
|
||||
if self.EscortGroup:IsAir() then
|
||||
if not self.EscortMenuScan then
|
||||
self.EscortMenuScan = MENU_CLIENT:New( self.EscortClient, "Scan for targets", self.EscortMenu )
|
||||
self.EscortMenuScan = MENU_GROUP:New( self.EscortClient:GetGroup(), "Scan for targets", self.EscortMenu )
|
||||
end
|
||||
|
||||
if not Height then
|
||||
@@ -480,9 +481,9 @@ function ESCORT:MenuScanForTargets( Height, Seconds, MenuTextFormat )
|
||||
self.EscortMenuScanForTargets = {}
|
||||
end
|
||||
|
||||
self.EscortMenuScanForTargets[#self.EscortMenuScanForTargets+1] = MENU_CLIENT_COMMAND
|
||||
self.EscortMenuScanForTargets[#self.EscortMenuScanForTargets+1] = MENU_GROUP_COMMAND
|
||||
:New(
|
||||
self.EscortClient,
|
||||
self.EscortClient:GetGroup(),
|
||||
MenuText,
|
||||
self.EscortMenuScan,
|
||||
ESCORT._ScanTargets,
|
||||
@@ -506,7 +507,7 @@ function ESCORT:MenuFlare( MenuTextFormat )
|
||||
self:F()
|
||||
|
||||
if not self.EscortMenuReportNavigation then
|
||||
self.EscortMenuReportNavigation = MENU_CLIENT:New( self.EscortClient, "Navigation", self.EscortMenu )
|
||||
self.EscortMenuReportNavigation = MENU_GROUP:New( self.EscortClient:GetGroup(), "Navigation", self.EscortMenu )
|
||||
end
|
||||
|
||||
local MenuText = ""
|
||||
@@ -517,11 +518,11 @@ function ESCORT:MenuFlare( MenuTextFormat )
|
||||
end
|
||||
|
||||
if not self.EscortMenuFlare then
|
||||
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!" )
|
||||
self.EscortMenuFlare = MENU_GROUP:New( self.EscortClient:GetGroup(), MenuText, self.EscortMenuReportNavigation, ESCORT._Flare, self )
|
||||
self.EscortMenuFlareGreen = MENU_GROUP_COMMAND:New( self.EscortClient:GetGroup(), "Release green flare", self.EscortMenuFlare, ESCORT._Flare, self, FLARECOLOR.Green, "Released a green flare!" )
|
||||
self.EscortMenuFlareRed = MENU_GROUP_COMMAND:New( self.EscortClient:GetGroup(), "Release red flare", self.EscortMenuFlare, ESCORT._Flare, self, FLARECOLOR.Red, "Released a red flare!" )
|
||||
self.EscortMenuFlareWhite = MENU_GROUP_COMMAND:New( self.EscortClient:GetGroup(), "Release white flare", self.EscortMenuFlare, ESCORT._Flare, self, FLARECOLOR.White, "Released a white flare!" )
|
||||
self.EscortMenuFlareYellow = MENU_GROUP_COMMAND:New( self.EscortClient:GetGroup(), "Release yellow flare", self.EscortMenuFlare, ESCORT._Flare, self, FLARECOLOR.Yellow, "Released a yellow flare!" )
|
||||
end
|
||||
|
||||
return self
|
||||
@@ -539,7 +540,7 @@ function ESCORT:MenuSmoke( MenuTextFormat )
|
||||
|
||||
if not self.EscortGroup:IsAir() then
|
||||
if not self.EscortMenuReportNavigation then
|
||||
self.EscortMenuReportNavigation = MENU_CLIENT:New( self.EscortClient, "Navigation", self.EscortMenu )
|
||||
self.EscortMenuReportNavigation = MENU_GROUP:New( self.EscortClient:GetGroup(), "Navigation", self.EscortMenu )
|
||||
end
|
||||
|
||||
local MenuText = ""
|
||||
@@ -550,12 +551,12 @@ function ESCORT:MenuSmoke( MenuTextFormat )
|
||||
end
|
||||
|
||||
if not self.EscortMenuSmoke then
|
||||
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!" )
|
||||
self.EscortMenuSmoke = MENU_GROUP:New( self.EscortClient:GetGroup(), "Smoke", self.EscortMenuReportNavigation, ESCORT._Smoke, self )
|
||||
self.EscortMenuSmokeGreen = MENU_GROUP_COMMAND:New( self.EscortClient:GetGroup(), "Release green smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.Green, "Releasing green smoke!" )
|
||||
self.EscortMenuSmokeRed = MENU_GROUP_COMMAND:New( self.EscortClient:GetGroup(), "Release red smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.Red, "Releasing red smoke!" )
|
||||
self.EscortMenuSmokeWhite = MENU_GROUP_COMMAND:New( self.EscortClient:GetGroup(), "Release white smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.White, "Releasing white smoke!" )
|
||||
self.EscortMenuSmokeOrange = MENU_GROUP_COMMAND:New( self.EscortClient:GetGroup(), "Release orange smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.Orange, "Releasing orange smoke!" )
|
||||
self.EscortMenuSmokeBlue = MENU_GROUP_COMMAND:New( self.EscortClient:GetGroup(), "Release blue smoke", self.EscortMenuSmoke, ESCORT._Smoke, self, SMOKECOLOR.Blue, "Releasing blue smoke!" )
|
||||
end
|
||||
end
|
||||
|
||||
@@ -572,7 +573,7 @@ function ESCORT:MenuReportTargets( Seconds )
|
||||
self:F( { Seconds } )
|
||||
|
||||
if not self.EscortMenuReportNearbyTargets then
|
||||
self.EscortMenuReportNearbyTargets = MENU_CLIENT:New( self.EscortClient, "Report targets", self.EscortMenu )
|
||||
self.EscortMenuReportNearbyTargets = MENU_GROUP:New( self.EscortClient:GetGroup(), "Report targets", self.EscortMenu )
|
||||
end
|
||||
|
||||
if not Seconds then
|
||||
@@ -580,12 +581,12 @@ function ESCORT:MenuReportTargets( Seconds )
|
||||
end
|
||||
|
||||
-- Report Targets
|
||||
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 )
|
||||
self.EscortMenuReportNearbyTargetsNow = MENU_GROUP_COMMAND:New( self.EscortClient:GetGroup(), "Report targets now!", self.EscortMenuReportNearbyTargets, ESCORT._ReportNearbyTargetsNow, self )
|
||||
self.EscortMenuReportNearbyTargetsOn = MENU_GROUP_COMMAND:New( self.EscortClient:GetGroup(), "Report targets on", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, self, true )
|
||||
self.EscortMenuReportNearbyTargetsOff = MENU_GROUP_COMMAND:New( self.EscortClient:GetGroup(), "Report targets off", self.EscortMenuReportNearbyTargets, ESCORT._SwitchReportNearbyTargets, self, false )
|
||||
|
||||
-- Attack Targets
|
||||
self.EscortMenuAttackNearbyTargets = MENU_CLIENT:New( self.EscortClient, "Attack targets", self.EscortMenu )
|
||||
self.EscortMenuAttackNearbyTargets = MENU_GROUP:New( self.EscortClient:GetGroup(), "Attack targets", self.EscortMenu )
|
||||
|
||||
|
||||
self.ReportTargetsScheduler = SCHEDULER:New( self, self._ReportTargetsScheduler, {}, 1, Seconds )
|
||||
@@ -603,7 +604,7 @@ function ESCORT:MenuAssistedAttack()
|
||||
|
||||
-- Request assistance from other escorts.
|
||||
-- This is very useful to let f.e. an escorting ship attack a target detected by an escorting plane...
|
||||
self.EscortMenuTargetAssistance = MENU_CLIENT:New( self.EscortClient, "Request assistance from", self.EscortMenu )
|
||||
self.EscortMenuTargetAssistance = MENU_GROUP:New( self.EscortClient:GetGroup(), "Request assistance from", self.EscortMenu )
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -617,18 +618,18 @@ function ESCORT:MenuROE( MenuTextFormat )
|
||||
|
||||
if not self.EscortMenuROE then
|
||||
-- Rules of Engagement
|
||||
self.EscortMenuROE = MENU_CLIENT:New( self.EscortClient, "ROE", self.EscortMenu )
|
||||
self.EscortMenuROE = MENU_GROUP:New( self.EscortClient:GetGroup(), "ROE", self.EscortMenu )
|
||||
if self.EscortGroup:OptionROEHoldFirePossible() then
|
||||
self.EscortMenuROEHoldFire = MENU_CLIENT_COMMAND:New( self.EscortClient, "Hold Fire", self.EscortMenuROE, ESCORT._ROE, self, self.EscortGroup:OptionROEHoldFire(), "Holding weapons!" )
|
||||
self.EscortMenuROEHoldFire = MENU_GROUP_COMMAND:New( self.EscortClient:GetGroup(), "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, self, self.EscortGroup:OptionROEReturnFire(), "Returning fire!" )
|
||||
self.EscortMenuROEReturnFire = MENU_GROUP_COMMAND:New( self.EscortClient:GetGroup(), "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, self, self.EscortGroup:OptionROEOpenFire(), "Opening fire on designated targets!!" )
|
||||
self.EscortMenuROEOpenFire = MENU_GROUP_COMMAND:New( self.EscortClient:GetGroup(), "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, self, self.EscortGroup:OptionROEWeaponFree(), "Opening fire on targets of opportunity!" )
|
||||
self.EscortMenuROEWeaponFree = MENU_GROUP_COMMAND:New( self.EscortClient:GetGroup(), "Weapon Free", self.EscortMenuROE, ESCORT._ROE, self, self.EscortGroup:OptionROEWeaponFree(), "Opening fire on targets of opportunity!" )
|
||||
end
|
||||
end
|
||||
|
||||
@@ -646,18 +647,18 @@ function ESCORT:MenuEvasion( MenuTextFormat )
|
||||
if self.EscortGroup:IsAir() then
|
||||
if not self.EscortMenuEvasion then
|
||||
-- Reaction to Threats
|
||||
self.EscortMenuEvasion = MENU_CLIENT:New( self.EscortClient, "Evasion", self.EscortMenu )
|
||||
self.EscortMenuEvasion = MENU_GROUP:New( self.EscortClient:GetGroup(), "Evasion", self.EscortMenu )
|
||||
if self.EscortGroup:OptionROTNoReactionPossible() then
|
||||
self.EscortMenuEvasionNoReaction = MENU_CLIENT_COMMAND:New( self.EscortClient, "Fight until death", self.EscortMenuEvasion, ESCORT._ROT, self, self.EscortGroup:OptionROTNoReaction(), "Fighting until death!" )
|
||||
self.EscortMenuEvasionNoReaction = MENU_GROUP_COMMAND:New( self.EscortClient:GetGroup(), "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, self, self.EscortGroup:OptionROTPassiveDefense(), "Defending using jammers, chaff and flares!" )
|
||||
self.EscortMenuEvasionPassiveDefense = MENU_GROUP_COMMAND:New( self.EscortClient:GetGroup(), "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, self, self.EscortGroup:OptionROTEvadeFire(), "Evading on enemy fire!" )
|
||||
self.EscortMenuEvasionEvadeFire = MENU_GROUP_COMMAND:New( self.EscortClient:GetGroup(), "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, self, self.EscortGroup:OptionROTVertical(), "Evading on enemy fire with vertical manoeuvres!" )
|
||||
self.EscortMenuOptionEvasionVertical = MENU_GROUP_COMMAND:New( self.EscortClient:GetGroup(), "Go below radar and evade fire", self.EscortMenuEvasion, ESCORT._ROT, self, self.EscortGroup:OptionROTVertical(), "Evading on enemy fire with vertical manoeuvres!" )
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -674,7 +675,7 @@ function ESCORT:MenuResumeMission()
|
||||
|
||||
if not self.EscortMenuResumeMission then
|
||||
-- Mission Resume Menu Root
|
||||
self.EscortMenuResumeMission = MENU_CLIENT:New( self.EscortClient, "Resume mission from", self.EscortMenu )
|
||||
self.EscortMenuResumeMission = MENU_GROUP:New( self.EscortClient:GetGroup(), "Resume mission from", self.EscortMenu )
|
||||
end
|
||||
|
||||
return self
|
||||
@@ -845,11 +846,11 @@ function _Resume( EscortGroup )
|
||||
end
|
||||
|
||||
--- @param #ESCORT self
|
||||
-- @param #number DetectedItemID
|
||||
function ESCORT:_AttackTarget( DetectedItemID )
|
||||
-- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem
|
||||
function ESCORT:_AttackTarget( DetectedItem )
|
||||
|
||||
local EscortGroup = self.EscortGroup -- Wrapper.Group#GROUP
|
||||
self:E( EscortGroup )
|
||||
self:F( EscortGroup )
|
||||
|
||||
local EscortClient = self.EscortClient
|
||||
|
||||
@@ -860,7 +861,7 @@ function ESCORT:_AttackTarget( DetectedItemID )
|
||||
EscortGroup:OptionROTPassiveDefense()
|
||||
EscortGroup:SetState( EscortGroup, "Escort", self )
|
||||
|
||||
local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID )
|
||||
local DetectedSet = self.Detection:GetDetectedSet( DetectedItem )
|
||||
|
||||
local Tasks = {}
|
||||
|
||||
@@ -873,7 +874,7 @@ function ESCORT:_AttackTarget( DetectedItemID )
|
||||
end, Tasks
|
||||
)
|
||||
|
||||
Tasks[#Tasks+1] = EscortGroup:TaskFunction( 1, 2, "_Resume", { "''" } )
|
||||
Tasks[#Tasks+1] = EscortGroup:TaskFunction( "_Resume", { "''" } )
|
||||
|
||||
EscortGroup:SetTask(
|
||||
EscortGroup:TaskCombo(
|
||||
@@ -883,7 +884,7 @@ function ESCORT:_AttackTarget( DetectedItemID )
|
||||
|
||||
else
|
||||
|
||||
local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID )
|
||||
local DetectedSet = self.Detection:GetDetectedSet( DetectedItem )
|
||||
|
||||
local Tasks = {}
|
||||
|
||||
@@ -909,8 +910,9 @@ function ESCORT:_AttackTarget( DetectedItemID )
|
||||
end
|
||||
|
||||
---
|
||||
-- @param #number DetectedItemID
|
||||
function ESCORT:_AssistTarget( EscortGroupAttack, DetectedItemID )
|
||||
--- @param #ESCORT self
|
||||
-- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem
|
||||
function ESCORT:_AssistTarget( EscortGroupAttack, DetectedItem )
|
||||
|
||||
local EscortGroup = self.EscortGroup
|
||||
local EscortClient = self.EscortClient
|
||||
@@ -921,7 +923,7 @@ function ESCORT:_AssistTarget( EscortGroupAttack, DetectedItemID )
|
||||
EscortGroupAttack:OptionROEOpenFire()
|
||||
EscortGroupAttack:OptionROTVertical()
|
||||
|
||||
local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID )
|
||||
local DetectedSet = self.Detection:GetDetectedSet( DetectedItem )
|
||||
|
||||
local Tasks = {}
|
||||
|
||||
@@ -943,7 +945,7 @@ function ESCORT:_AssistTarget( EscortGroupAttack, DetectedItemID )
|
||||
)
|
||||
|
||||
else
|
||||
local DetectedSet = self.Detection:GetDetectedSet( DetectedItemID )
|
||||
local DetectedSet = self.Detection:GetDetectedSet( DetectedItem )
|
||||
|
||||
local Tasks = {}
|
||||
|
||||
@@ -1150,7 +1152,7 @@ function ESCORT:_ReportTargetsScheduler()
|
||||
end
|
||||
|
||||
local DetectedItems = self.Detection:GetDetectedItems()
|
||||
self:E( DetectedItems )
|
||||
self:F( DetectedItems )
|
||||
|
||||
local DetectedTargets = false
|
||||
|
||||
@@ -1159,36 +1161,42 @@ function ESCORT:_ReportTargetsScheduler()
|
||||
for ClientEscortGroupName, EscortGroupData in pairs( self.EscortClient._EscortGroups ) do
|
||||
|
||||
local ClientEscortTargets = EscortGroupData.Detection
|
||||
--local EscortUnit = EscortGroupData:GetUnit( 1 )
|
||||
|
||||
for DetectedItemID, DetectedItem in ipairs( DetectedItems ) do
|
||||
self:E( { DetectedItemID, DetectedItem } )
|
||||
for DetectedItemIndex, DetectedItem in pairs( DetectedItems ) do
|
||||
self:F( { DetectedItemIndex, DetectedItem } )
|
||||
-- Remove the sub menus of the Attack menu of the Escort for the EscortGroup.
|
||||
|
||||
local DetectedItemReportSummary = self.Detection:DetectedItemReportSummary( DetectedItemID )
|
||||
local DetectedItemReportSummary = self.Detection:DetectedItemReportSummary( DetectedItem, EscortGroupData.EscortGroup, _DATABASE:GetPlayerSettings( self.EscortClient:GetPlayerName() ) )
|
||||
|
||||
if ClientEscortGroupName == EscortGroupName then
|
||||
|
||||
DetectedMsgs[#DetectedMsgs+1] = DetectedItemReportSummary
|
||||
local DetectedMsg = DetectedItemReportSummary:Text("\n")
|
||||
DetectedMsgs[#DetectedMsgs+1] = DetectedMsg
|
||||
|
||||
self:T( DetectedMsg )
|
||||
|
||||
MENU_CLIENT_COMMAND:New( self.EscortClient,
|
||||
DetectedItemReportSummary,
|
||||
MENU_GROUP_COMMAND:New( self.EscortClient:GetGroup(),
|
||||
DetectedMsg,
|
||||
self.EscortMenuAttackNearbyTargets,
|
||||
ESCORT._AttackTarget,
|
||||
self,
|
||||
DetectedItemID
|
||||
DetectedItem
|
||||
)
|
||||
else
|
||||
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,
|
||||
local DetectedMsg = DetectedItemReportSummary:Text("\n")
|
||||
self:T( DetectedMsg )
|
||||
|
||||
local MenuTargetAssistance = MENU_GROUP:New( self.EscortClient:GetGroup(), EscortGroupData.EscortName, self.EscortMenuTargetAssistance )
|
||||
MENU_GROUP_COMMAND:New( self.EscortClient:GetGroup(),
|
||||
DetectedMsg,
|
||||
MenuTargetAssistance,
|
||||
ESCORT._AssistTarget,
|
||||
self,
|
||||
EscortGroupData.EscortGroup,
|
||||
DetectedItemID
|
||||
DetectedItem
|
||||
)
|
||||
end
|
||||
end
|
||||
@@ -1197,9 +1205,9 @@ function ESCORT:_ReportTargetsScheduler()
|
||||
|
||||
end
|
||||
end
|
||||
self:E( DetectedMsgs )
|
||||
self:F( DetectedMsgs )
|
||||
if DetectedTargets then
|
||||
self.EscortGroup:MessageToClient( "Detected targets:\n" .. table.concat( DetectedMsgs, "\n" ), 20, self.EscortClient )
|
||||
self.EscortGroup:MessageToClient( "Reporting detected targets:\n" .. table.concat( DetectedMsgs, "\n" ), 20, self.EscortClient )
|
||||
else
|
||||
self.EscortGroup:MessageToClient( "No targets detected.", 10, self.EscortClient )
|
||||
end
|
||||
@@ -1319,7 +1327,7 @@ function ESCORT:_ReportTargetsScheduler()
|
||||
--
|
||||
-- if ClientEscortGroupName == EscortGroupName then
|
||||
--
|
||||
-- MENU_CLIENT_COMMAND:New( self.EscortClient,
|
||||
-- MENU_GROUP_COMMAND:New( self.EscortClient,
|
||||
-- EscortTargetMessage,
|
||||
-- self.EscortMenuAttackNearbyTargets,
|
||||
-- ESCORT._AttackTarget,
|
||||
@@ -1330,8 +1338,8 @@ function ESCORT:_ReportTargetsScheduler()
|
||||
-- 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,
|
||||
-- local MenuTargetAssistance = MENU_GROUP:New( self.EscortClient, EscortGroupData.EscortName, self.EscortMenuTargetAssistance )
|
||||
-- MENU_GROUP_COMMAND:New( self.EscortClient,
|
||||
-- EscortTargetMessage,
|
||||
-- MenuTargetAssistance,
|
||||
-- ESCORT._AssistTarget,
|
||||
@@ -1370,7 +1378,7 @@ function ESCORT:_ReportTargetsScheduler()
|
||||
-- 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 } )
|
||||
-- MENU_GROUP_COMMAND:New( self.EscortClient, "Waypoint " .. WayPointID .. " at " .. string.format( "%.2f", Distance ).. "km", self.EscortMenuResumeMission, ESCORT._ResumeMission, { ParamSelf = self, ParamWayPoint = WayPointID } )
|
||||
-- end
|
||||
-- end
|
||||
--
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
--- This module contains the MISSILETRAINER class.
|
||||
--- **Functional** -- MISSILETRAINER helps you to train missile avoidance.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- 1) @{MissileTrainer#MISSILETRAINER} class, extends @{Base#BASE}
|
||||
-- ===============================================================
|
||||
-- ===
|
||||
-- The @{#MISSILETRAINER} class uses the DCS world messaging system to be alerted of any missiles fired, and when a missile would hit your aircraft,
|
||||
-- the class will destroy the missile within a certain range, to avoid damage to your aircraft.
|
||||
-- It suports the following functionality:
|
||||
@@ -71,7 +71,7 @@
|
||||
-- ===
|
||||
--
|
||||
-- CREDITS
|
||||
-- =======
|
||||
-- ===
|
||||
-- **Stuka (Danny)** Who you can search on the Eagle Dynamics Forums.
|
||||
-- Working together with Danny has resulted in the MISSILETRAINER class.
|
||||
-- Danny has shared his ideas and together we made a design.
|
||||
@@ -99,39 +99,39 @@ function MISSILETRAINER._Alive( Client, self )
|
||||
if self.MenusOnOff == true then
|
||||
Client:Message( "Use the 'Radio Menu' -> 'Other (F10)' -> 'Missile Trainer' menu options to change the Missile Trainer settings (for all players).", 15, "Trainer" )
|
||||
|
||||
Client.MainMenu = MENU_CLIENT:New( Client, "Missile Trainer", nil ) -- Menu#MENU_CLIENT
|
||||
Client.MainMenu = MENU_GROUP:New( Client:GetGroup(), "Missile Trainer", nil ) -- Menu#MENU_GROUP
|
||||
|
||||
Client.MenuMessages = MENU_CLIENT:New( Client, "Messages", Client.MainMenu )
|
||||
Client.MenuOn = MENU_CLIENT_COMMAND:New( Client, "Messages On", Client.MenuMessages, self._MenuMessages, { MenuSelf = self, MessagesOnOff = true } )
|
||||
Client.MenuOff = MENU_CLIENT_COMMAND:New( Client, "Messages Off", Client.MenuMessages, self._MenuMessages, { MenuSelf = self, MessagesOnOff = false } )
|
||||
Client.MenuMessages = MENU_GROUP:New( Client:GetGroup(), "Messages", Client.MainMenu )
|
||||
Client.MenuOn = MENU_GROUP_COMMAND:New( Client:GetGroup(), "Messages On", Client.MenuMessages, self._MenuMessages, { MenuSelf = self, MessagesOnOff = true } )
|
||||
Client.MenuOff = MENU_GROUP_COMMAND:New( Client:GetGroup(), "Messages Off", Client.MenuMessages, self._MenuMessages, { MenuSelf = self, MessagesOnOff = false } )
|
||||
|
||||
Client.MenuTracking = MENU_CLIENT:New( Client, "Tracking", Client.MainMenu )
|
||||
Client.MenuTrackingToAll = MENU_CLIENT_COMMAND:New( Client, "To All", Client.MenuTracking, self._MenuMessages, { MenuSelf = self, TrackingToAll = true } )
|
||||
Client.MenuTrackingToTarget = MENU_CLIENT_COMMAND:New( Client, "To Target", Client.MenuTracking, self._MenuMessages, { MenuSelf = self, TrackingToAll = false } )
|
||||
Client.MenuTrackOn = MENU_CLIENT_COMMAND:New( Client, "Tracking On", Client.MenuTracking, self._MenuMessages, { MenuSelf = self, TrackingOnOff = true } )
|
||||
Client.MenuTrackOff = MENU_CLIENT_COMMAND:New( Client, "Tracking Off", Client.MenuTracking, self._MenuMessages, { MenuSelf = self, TrackingOnOff = false } )
|
||||
Client.MenuTrackIncrease = MENU_CLIENT_COMMAND:New( Client, "Frequency Increase", Client.MenuTracking, self._MenuMessages, { MenuSelf = self, TrackingFrequency = -1 } )
|
||||
Client.MenuTrackDecrease = MENU_CLIENT_COMMAND:New( Client, "Frequency Decrease", Client.MenuTracking, self._MenuMessages, { MenuSelf = self, TrackingFrequency = 1 } )
|
||||
Client.MenuTracking = MENU_GROUP:New( Client:GetGroup(), "Tracking", Client.MainMenu )
|
||||
Client.MenuTrackingToAll = MENU_GROUP_COMMAND:New( Client:GetGroup(), "To All", Client.MenuTracking, self._MenuMessages, { MenuSelf = self, TrackingToAll = true } )
|
||||
Client.MenuTrackingToTarget = MENU_GROUP_COMMAND:New( Client:GetGroup(), "To Target", Client.MenuTracking, self._MenuMessages, { MenuSelf = self, TrackingToAll = false } )
|
||||
Client.MenuTrackOn = MENU_GROUP_COMMAND:New( Client:GetGroup(), "Tracking On", Client.MenuTracking, self._MenuMessages, { MenuSelf = self, TrackingOnOff = true } )
|
||||
Client.MenuTrackOff = MENU_GROUP_COMMAND:New( Client:GetGroup(), "Tracking Off", Client.MenuTracking, self._MenuMessages, { MenuSelf = self, TrackingOnOff = false } )
|
||||
Client.MenuTrackIncrease = MENU_GROUP_COMMAND:New( Client:GetGroup(), "Frequency Increase", Client.MenuTracking, self._MenuMessages, { MenuSelf = self, TrackingFrequency = -1 } )
|
||||
Client.MenuTrackDecrease = MENU_GROUP_COMMAND:New( Client:GetGroup(), "Frequency Decrease", Client.MenuTracking, self._MenuMessages, { MenuSelf = self, TrackingFrequency = 1 } )
|
||||
|
||||
Client.MenuAlerts = MENU_CLIENT:New( Client, "Alerts", Client.MainMenu )
|
||||
Client.MenuAlertsToAll = MENU_CLIENT_COMMAND:New( Client, "To All", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsToAll = true } )
|
||||
Client.MenuAlertsToTarget = MENU_CLIENT_COMMAND:New( Client, "To Target", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsToAll = false } )
|
||||
Client.MenuHitsOn = MENU_CLIENT_COMMAND:New( Client, "Hits On", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsHitsOnOff = true } )
|
||||
Client.MenuHitsOff = MENU_CLIENT_COMMAND:New( Client, "Hits Off", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsHitsOnOff = false } )
|
||||
Client.MenuLaunchesOn = MENU_CLIENT_COMMAND:New( Client, "Launches On", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsLaunchesOnOff = true } )
|
||||
Client.MenuLaunchesOff = MENU_CLIENT_COMMAND:New( Client, "Launches Off", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsLaunchesOnOff = false } )
|
||||
Client.MenuAlerts = MENU_GROUP:New( Client:GetGroup(), "Alerts", Client.MainMenu )
|
||||
Client.MenuAlertsToAll = MENU_GROUP_COMMAND:New( Client:GetGroup(), "To All", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsToAll = true } )
|
||||
Client.MenuAlertsToTarget = MENU_GROUP_COMMAND:New( Client:GetGroup(), "To Target", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsToAll = false } )
|
||||
Client.MenuHitsOn = MENU_GROUP_COMMAND:New( Client:GetGroup(), "Hits On", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsHitsOnOff = true } )
|
||||
Client.MenuHitsOff = MENU_GROUP_COMMAND:New( Client:GetGroup(), "Hits Off", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsHitsOnOff = false } )
|
||||
Client.MenuLaunchesOn = MENU_GROUP_COMMAND:New( Client:GetGroup(), "Launches On", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsLaunchesOnOff = true } )
|
||||
Client.MenuLaunchesOff = MENU_GROUP_COMMAND:New( Client:GetGroup(), "Launches Off", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsLaunchesOnOff = false } )
|
||||
|
||||
Client.MenuDetails = MENU_CLIENT:New( Client, "Details", Client.MainMenu )
|
||||
Client.MenuDetailsDistanceOn = MENU_CLIENT_COMMAND:New( Client, "Range On", Client.MenuDetails, self._MenuMessages, { MenuSelf = self, DetailsRangeOnOff = true } )
|
||||
Client.MenuDetailsDistanceOff = MENU_CLIENT_COMMAND:New( Client, "Range Off", Client.MenuDetails, self._MenuMessages, { MenuSelf = self, DetailsRangeOnOff = false } )
|
||||
Client.MenuDetailsBearingOn = MENU_CLIENT_COMMAND:New( Client, "Bearing On", Client.MenuDetails, self._MenuMessages, { MenuSelf = self, DetailsBearingOnOff = true } )
|
||||
Client.MenuDetailsBearingOff = MENU_CLIENT_COMMAND:New( Client, "Bearing Off", Client.MenuDetails, self._MenuMessages, { MenuSelf = self, DetailsBearingOnOff = false } )
|
||||
Client.MenuDetails = MENU_GROUP:New( Client:GetGroup(), "Details", Client.MainMenu )
|
||||
Client.MenuDetailsDistanceOn = MENU_GROUP_COMMAND:New( Client:GetGroup(), "Range On", Client.MenuDetails, self._MenuMessages, { MenuSelf = self, DetailsRangeOnOff = true } )
|
||||
Client.MenuDetailsDistanceOff = MENU_GROUP_COMMAND:New( Client:GetGroup(), "Range Off", Client.MenuDetails, self._MenuMessages, { MenuSelf = self, DetailsRangeOnOff = false } )
|
||||
Client.MenuDetailsBearingOn = MENU_GROUP_COMMAND:New( Client:GetGroup(), "Bearing On", Client.MenuDetails, self._MenuMessages, { MenuSelf = self, DetailsBearingOnOff = true } )
|
||||
Client.MenuDetailsBearingOff = MENU_GROUP_COMMAND:New( Client:GetGroup(), "Bearing Off", Client.MenuDetails, self._MenuMessages, { MenuSelf = self, DetailsBearingOnOff = false } )
|
||||
|
||||
Client.MenuDistance = MENU_CLIENT:New( Client, "Set distance to plane", Client.MainMenu )
|
||||
Client.MenuDistance50 = MENU_CLIENT_COMMAND:New( Client, "50 meter", Client.MenuDistance, self._MenuMessages, { MenuSelf = self, Distance = 50 / 1000 } )
|
||||
Client.MenuDistance100 = MENU_CLIENT_COMMAND:New( Client, "100 meter", Client.MenuDistance, self._MenuMessages, { MenuSelf = self, Distance = 100 / 1000 } )
|
||||
Client.MenuDistance150 = MENU_CLIENT_COMMAND:New( Client, "150 meter", Client.MenuDistance, self._MenuMessages, { MenuSelf = self, Distance = 150 / 1000 } )
|
||||
Client.MenuDistance200 = MENU_CLIENT_COMMAND:New( Client, "200 meter", Client.MenuDistance, self._MenuMessages, { MenuSelf = self, Distance = 200 / 1000 } )
|
||||
Client.MenuDistance = MENU_GROUP:New( Client:GetGroup(), "Set distance to plane", Client.MainMenu )
|
||||
Client.MenuDistance50 = MENU_GROUP_COMMAND:New( Client:GetGroup(), "50 meter", Client.MenuDistance, self._MenuMessages, { MenuSelf = self, Distance = 50 / 1000 } )
|
||||
Client.MenuDistance100 = MENU_GROUP_COMMAND:New( Client:GetGroup(), "100 meter", Client.MenuDistance, self._MenuMessages, { MenuSelf = self, Distance = 100 / 1000 } )
|
||||
Client.MenuDistance150 = MENU_GROUP_COMMAND:New( Client:GetGroup(), "150 meter", Client.MenuDistance, self._MenuMessages, { MenuSelf = self, Distance = 150 / 1000 } )
|
||||
Client.MenuDistance200 = MENU_GROUP_COMMAND:New( Client:GetGroup(), "200 meter", Client.MenuDistance, self._MenuMessages, { MenuSelf = self, Distance = 200 / 1000 } )
|
||||
else
|
||||
if Client.MainMenu then
|
||||
Client.MainMenu:Remove()
|
||||
@@ -177,13 +177,13 @@ function MISSILETRAINER:New( Distance, Briefing )
|
||||
|
||||
|
||||
-- for ClientID, Client in pairs( self.DBClients.Database ) do
|
||||
-- self:E( "ForEach:" .. Client.UnitName )
|
||||
-- self:F( "ForEach:" .. Client.UnitName )
|
||||
-- Client:Alive( self._Alive, self )
|
||||
-- end
|
||||
--
|
||||
self.DBClients:ForEachClient(
|
||||
function( Client )
|
||||
self:E( "ForEach:" .. Client.UnitName )
|
||||
self:F( "ForEach:" .. Client.UnitName )
|
||||
Client:Alive( self._Alive, self )
|
||||
end
|
||||
)
|
||||
@@ -442,7 +442,7 @@ function MISSILETRAINER._MenuMessages( MenuParameters )
|
||||
|
||||
if MenuParameters.Distance ~= nil then
|
||||
self.Distance = MenuParameters.Distance
|
||||
MESSAGE:New( "Hit detection distance set to " .. self.Distance * 1000 .. " meters", 15, "Menu" ):ToAll()
|
||||
MESSAGE:New( "Hit detection distance set to " .. ( self.Distance * 1000 ) .. " meters", 15, "Menu" ):ToAll()
|
||||
end
|
||||
|
||||
end
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
--- 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
|
||||
|
||||
305
Moose Development/Moose/Functional/Protect.lua
Normal file
305
Moose Development/Moose/Functional/Protect.lua
Normal file
@@ -0,0 +1,305 @@
|
||||
--- **Functional** -- The PROTECT class handles the protection of objects, which can be zones, units, scenery.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions: **MillerTime**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module Protect
|
||||
|
||||
--- @type PROTECT.__ Methods which are not intended for mission designers, but which are used interally by the moose designer :-)
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
--- @type PROTECT
|
||||
-- @extends #PROTECT.__
|
||||
|
||||
--- # PROTECT, extends @{Base#BASE}
|
||||
--
|
||||
-- @field #PROTECT
|
||||
PROTECT = {
|
||||
ClassName = "PROTECT",
|
||||
}
|
||||
|
||||
--- Get the ProtectZone
|
||||
-- @param #PROTECT self
|
||||
-- @return Core.Zone#ZONE_BASE
|
||||
function PROTECT:GetProtectZone()
|
||||
return self.ProtectZone
|
||||
end
|
||||
|
||||
|
||||
--- Get the name of the ProtectZone
|
||||
-- @param #PROTECT self
|
||||
-- @return #string
|
||||
function PROTECT:GetProtectZoneName()
|
||||
return self.ProtectZone:GetName()
|
||||
end
|
||||
|
||||
|
||||
--- Set the owning coalition of the zone.
|
||||
-- @param #PROTECT self
|
||||
-- @param DCSCoalition.DCSCoalition#coalition Coalition
|
||||
function PROTECT:SetCoalition( Coalition )
|
||||
self.Coalition = Coalition
|
||||
end
|
||||
|
||||
|
||||
--- Get the owning coalition of the zone.
|
||||
-- @param #PROTECT self
|
||||
-- @return DCSCoalition.DCSCoalition#coalition Coalition.
|
||||
function PROTECT:GetCoalition()
|
||||
return self.Coalition
|
||||
end
|
||||
|
||||
|
||||
--- Get the owning coalition name of the zone.
|
||||
-- @param #PROTECT self
|
||||
-- @return #string Coalition name.
|
||||
function PROTECT:GetCoalitionName()
|
||||
|
||||
if self.Coalition == coalition.side.BLUE then
|
||||
return "Blue"
|
||||
end
|
||||
|
||||
if self.Coalition == coalition.side.RED then
|
||||
return "Red"
|
||||
end
|
||||
|
||||
if self.Coalition == coalition.side.NEUTRAL then
|
||||
return "Neutral"
|
||||
end
|
||||
|
||||
return ""
|
||||
end
|
||||
|
||||
|
||||
function PROTECT:IsGuarded()
|
||||
|
||||
local IsGuarded = self.ProtectZone:IsAllInZoneOfCoalition( self.Coalition )
|
||||
self:F( { IsGuarded = IsGuarded } )
|
||||
return IsGuarded
|
||||
end
|
||||
|
||||
function PROTECT:IsCaptured()
|
||||
|
||||
local IsCaptured = self.ProtectZone:IsAllInZoneOfOtherCoalition( self.Coalition )
|
||||
self:F( { IsCaptured = IsCaptured } )
|
||||
return IsCaptured
|
||||
end
|
||||
|
||||
|
||||
function PROTECT:IsAttacked()
|
||||
|
||||
local IsAttacked = self.ProtectZone:IsSomeInZoneOfCoalition( self.Coalition )
|
||||
self:F( { IsAttacked = IsAttacked } )
|
||||
return IsAttacked
|
||||
end
|
||||
|
||||
|
||||
function PROTECT:IsEmpty()
|
||||
|
||||
local IsEmpty = self.ProtectZone:IsNoneInZone()
|
||||
self:F( { IsEmpty = IsEmpty } )
|
||||
return IsEmpty
|
||||
end
|
||||
|
||||
|
||||
--- Check if the units are still alive.
|
||||
-- @param #PROTECT self
|
||||
function PROTECT:AreProtectUnitsAlive()
|
||||
|
||||
local IsAlive = false
|
||||
|
||||
local UnitSet = self.ProtectUnitSet
|
||||
UnitSet:Flush( self )
|
||||
local UnitList = UnitSet:GetSet()
|
||||
|
||||
for UnitID, ProtectUnit in pairs( UnitList ) do
|
||||
local IsUnitAlive = ProtectUnit:IsAlive()
|
||||
if IsUnitAlive == true then
|
||||
IsAlive = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return IsAlive
|
||||
end
|
||||
|
||||
--- Check if the statics are still alive.
|
||||
-- @param #PROTECT self
|
||||
function PROTECT:AreProtectStaticsAlive()
|
||||
|
||||
local IsAlive = false
|
||||
|
||||
local StaticSet = self.ProtectStaticSet
|
||||
StaticSet:Flush( self )
|
||||
local StaticList = StaticSet:GetSet()
|
||||
|
||||
for UnitID, ProtectStatic in pairs( StaticList ) do
|
||||
local IsStaticAlive = ProtectStatic:IsAlive()
|
||||
if IsStaticAlive == true then
|
||||
IsAlive = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return IsAlive
|
||||
end
|
||||
|
||||
|
||||
--- Check if there is a capture unit in the zone.
|
||||
-- @param #PROTECT self
|
||||
function PROTECT:IsCaptureUnitInZone()
|
||||
|
||||
local CaptureUnitSet = self.CaptureUnitSet
|
||||
CaptureUnitSet:Flush( self )
|
||||
|
||||
local IsInZone = self.CaptureUnitSet:IsPartiallyInZone( self.ProtectZone )
|
||||
|
||||
self:F({IsInZone = IsInZone})
|
||||
|
||||
return IsInZone
|
||||
end
|
||||
|
||||
--- Smoke.
|
||||
-- @param #PROTECT self
|
||||
-- @param #SMOKECOLOR.Color SmokeColor
|
||||
function PROTECT:Smoke( SmokeColor )
|
||||
|
||||
self.SmokeColor = SmokeColor
|
||||
end
|
||||
|
||||
|
||||
--- Flare.
|
||||
-- @param #PROTECT self
|
||||
-- @param #SMOKECOLOR.Color FlareColor
|
||||
function PROTECT:Flare( FlareColor )
|
||||
self.ProtectZone:FlareZone( FlareColor, math.random( 1, 360 ) )
|
||||
end
|
||||
|
||||
|
||||
--- Mark.
|
||||
-- @param #PROTECT self
|
||||
function PROTECT:Mark()
|
||||
|
||||
local Coord = self.ProtectZone:GetCoordinate()
|
||||
local ZoneName = self:GetProtectZoneName()
|
||||
local State = self:GetState()
|
||||
|
||||
if self.MarkRed and self.MarkBlue then
|
||||
self:F( { MarkRed = self.MarkRed, MarkBlue = self.MarkBlue } )
|
||||
Coord:RemoveMark( self.MarkRed )
|
||||
Coord:RemoveMark( self.MarkBlue )
|
||||
end
|
||||
|
||||
if self.Coalition == coalition.side.BLUE then
|
||||
self.MarkBlue = Coord:MarkToCoalitionBlue( "Guard Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||
self.MarkRed = Coord:MarkToCoalitionRed( "Capture Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||
else
|
||||
self.MarkRed = Coord:MarkToCoalitionRed( "Guard Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||
self.MarkBlue = Coord:MarkToCoalitionBlue( "Capture Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Bound.
|
||||
-- @param #PROTECT self
|
||||
function PROTECT:onafterStart()
|
||||
|
||||
self:ScheduleRepeat( 5, 15, 0.1, nil, self.StatusCoalition, self )
|
||||
self:ScheduleRepeat( 5, 15, 0.1, nil, self.StatusZone, self )
|
||||
self:ScheduleRepeat( 10, 15, 0, nil, self.StatusSmoke, self )
|
||||
end
|
||||
|
||||
--- Bound.
|
||||
-- @param #PROTECT self
|
||||
function PROTECT:onenterGuarded()
|
||||
|
||||
|
||||
if self.Coalition == coalition.side.BLUE then
|
||||
--elf.ProtectZone:BoundZone( 12, country.id.USA )
|
||||
else
|
||||
--self.ProtectZone:BoundZone( 12, country.id.RUSSIA )
|
||||
end
|
||||
|
||||
self:Mark()
|
||||
|
||||
end
|
||||
|
||||
function PROTECT:onenterCaptured()
|
||||
|
||||
local NewCoalition = self.ProtectZone:GetCoalition()
|
||||
self:F( { NewCoalition = NewCoalition } )
|
||||
self:SetCoalition( NewCoalition )
|
||||
|
||||
self:Mark()
|
||||
end
|
||||
|
||||
|
||||
function PROTECT:onenterEmpty()
|
||||
|
||||
self:Mark()
|
||||
end
|
||||
|
||||
|
||||
function PROTECT:onenterAttacked()
|
||||
|
||||
self:Mark()
|
||||
end
|
||||
|
||||
|
||||
--- Check status Coalition ownership.
|
||||
-- @param #PROTECT self
|
||||
function PROTECT:StatusCoalition()
|
||||
|
||||
self:F( { State = self:GetState() } )
|
||||
|
||||
self.ProtectZone:Scan()
|
||||
|
||||
if self:IsGuarded() then
|
||||
self:Guard()
|
||||
else
|
||||
if self:IsCaptured() then
|
||||
self:Capture()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Check status Zone.
|
||||
-- @param #PROTECT self
|
||||
function PROTECT:StatusZone()
|
||||
|
||||
self:F( { State = self:GetState() } )
|
||||
|
||||
self.ProtectZone:Scan()
|
||||
|
||||
if self:IsAttacked() then
|
||||
self:Attack()
|
||||
else
|
||||
if self:IsEmpty() then
|
||||
self:Empty()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Check status Smoke.
|
||||
-- @param #PROTECT self
|
||||
function PROTECT:StatusSmoke()
|
||||
|
||||
local CurrentTime = timer.getTime()
|
||||
|
||||
if self.SmokeTime == nil or self.SmokeTime + 300 <= CurrentTime then
|
||||
if self.SmokeColor then
|
||||
self.ProtectZone:GetCoordinate():Smoke( self.SmokeColor )
|
||||
--self.SmokeColor = nil
|
||||
self.SmokeTime = CurrentTime
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user