mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-08-15 10:47:21 +00:00
Compare commits
799 Commits
2.4.0
...
v2.5.0pre2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be1f8cde34 | ||
|
|
29028bf128 | ||
|
|
c30ca2b18a | ||
|
|
d1b40b63c2 | ||
|
|
0b7ac754c5 | ||
|
|
5b514d223b | ||
|
|
7d8e27c83e | ||
|
|
dcd1060496 | ||
|
|
533e2abf17 | ||
|
|
6b318d0309 | ||
|
|
bee44b97fd | ||
|
|
77de1dda2b | ||
|
|
5b03bc5066 | ||
|
|
0d6f32581c | ||
|
|
2620370890 | ||
|
|
f866af0d4a | ||
|
|
b86eac33b6 | ||
|
|
c78bd2652a | ||
|
|
392d465b30 | ||
|
|
056cc1630d | ||
|
|
364ce927e7 | ||
|
|
517d5860fb | ||
|
|
4e49184da2 | ||
|
|
68ece29ab5 | ||
|
|
c3cc76c15b | ||
|
|
0a570da986 | ||
|
|
b25697e261 | ||
|
|
863e239204 | ||
|
|
457b30ed07 | ||
|
|
7360c51a8f | ||
|
|
df512f7a58 | ||
|
|
da05c41c27 | ||
|
|
3c428723f8 | ||
|
|
4c3fd1d867 | ||
|
|
909c028e48 | ||
|
|
14e12dc36b | ||
|
|
0502e0a020 | ||
|
|
36aebd3c04 | ||
|
|
007831bb9a | ||
|
|
def44bde89 | ||
|
|
4b369fae95 | ||
|
|
cb4a44b512 | ||
|
|
53fbec9c15 | ||
|
|
d476922af2 | ||
|
|
861f2da4d6 | ||
|
|
6d75b25f57 | ||
|
|
bfb3ae33f4 | ||
|
|
a796c7a594 | ||
|
|
af63c1a219 | ||
|
|
72da7a2c04 | ||
|
|
f9bdbf4e3f | ||
|
|
850951bcb4 | ||
|
|
3d0f002f73 | ||
|
|
ff8c454752 | ||
|
|
6882c4b42f | ||
|
|
9b0f474edf | ||
|
|
ceaae5421c | ||
|
|
36666df76f | ||
|
|
3616e4264a | ||
|
|
a704693abf | ||
|
|
4dc303a166 | ||
|
|
71b5140e69 | ||
|
|
00e5499f81 | ||
|
|
7cf73af707 | ||
|
|
4ef827836c | ||
|
|
e3d6337481 | ||
|
|
f258e9263e | ||
|
|
48d99a19c5 | ||
|
|
f34511bd8c | ||
|
|
63a5d2e3ac | ||
|
|
3fb52d4fa1 | ||
|
|
463403cef4 | ||
|
|
fedee49eb1 | ||
|
|
042cc7a85f | ||
|
|
9583168c4d | ||
|
|
0c32c35c27 | ||
|
|
6ab85072d2 | ||
|
|
04da941c36 | ||
|
|
d6c919a097 | ||
|
|
1f9ffcd92d | ||
|
|
12ef34acaa | ||
|
|
ce60e7ce74 | ||
|
|
aa5d36db88 | ||
|
|
a2759b1cc0 | ||
|
|
0da0b025b5 | ||
|
|
f685064c0c | ||
|
|
b68e254bc0 | ||
|
|
d43f77706e | ||
|
|
5e1fb36d01 | ||
|
|
6d3bbe88e6 | ||
|
|
e4906eb083 | ||
|
|
19eb81866a | ||
|
|
cd34755036 | ||
|
|
c72625c60f | ||
|
|
1e1154d190 | ||
|
|
ff0f62c01b | ||
|
|
e6e5787fc6 | ||
|
|
431e8a05f9 | ||
|
|
b91a8cf4c8 | ||
|
|
3e8455410e | ||
|
|
3a9ad1b7cc | ||
|
|
6000421957 | ||
|
|
231ceef41f | ||
|
|
4661f6981e | ||
|
|
fcfcfeae00 | ||
|
|
09ed562f61 | ||
|
|
b1f2bab87e | ||
|
|
aa6515e1ca | ||
|
|
d5b8ed62ae | ||
|
|
6208c79d1f | ||
|
|
740ee74f61 | ||
|
|
0bb0c70667 | ||
|
|
9fb311bf7d | ||
|
|
8915657e91 | ||
|
|
8b506a2f20 | ||
|
|
6dd73dba44 | ||
|
|
d3a3d96436 | ||
|
|
8337058306 | ||
|
|
389931fbf0 | ||
|
|
b9fd6c0e31 | ||
|
|
f48f53fb37 | ||
|
|
07edcd7e73 | ||
|
|
a193d342d7 | ||
|
|
5b9978e390 | ||
|
|
c3c984381e | ||
|
|
ca49e8f8ae | ||
|
|
59c46f9b4b | ||
|
|
b9384831fb | ||
|
|
0d57ad5584 | ||
|
|
cf34ec0905 | ||
|
|
1c99e474b2 | ||
|
|
e45134f086 | ||
|
|
1b5e9df586 | ||
|
|
4b6612e114 | ||
|
|
e4473010eb | ||
|
|
bb259b2572 | ||
|
|
e16e2851c1 | ||
|
|
169c5a674c | ||
|
|
60042e14dc | ||
|
|
14de37f390 | ||
|
|
e67b819df9 | ||
|
|
efccefecad | ||
|
|
faa2debbae | ||
|
|
ddf61d1bf3 | ||
|
|
c5d514efdf | ||
|
|
f1c16def9a | ||
|
|
626db45a7e | ||
|
|
21a7594134 | ||
|
|
5fd20477d9 | ||
|
|
01dd3c93d2 | ||
|
|
b733e3b6d8 | ||
|
|
fe2ef11f8d | ||
|
|
9b6994749b | ||
|
|
9b209d6ce0 | ||
|
|
ed649747bd | ||
|
|
f5f1c9043b | ||
|
|
c7c371dc34 | ||
|
|
1cea10f03a | ||
|
|
3e9f54aaa5 | ||
|
|
ab1cd41d79 | ||
|
|
dc9f730d7d | ||
|
|
c0e48288de | ||
|
|
a13b79ced1 | ||
|
|
cd845d385f | ||
|
|
dea7a5674c | ||
|
|
8d1d2179f9 | ||
|
|
9b96ff5978 | ||
|
|
6cc233cac7 | ||
|
|
ad2ce8df78 | ||
|
|
4ea44308bc | ||
|
|
5cdaf53727 | ||
|
|
fb875077d7 | ||
|
|
c579aad606 | ||
|
|
8c54b7298d | ||
|
|
b309c25619 | ||
|
|
04a699f990 | ||
|
|
d51fc5de8a | ||
|
|
e458c70a7b | ||
|
|
2d4a4fb2ca | ||
|
|
e23ebd622a | ||
|
|
98e2f2c09b | ||
|
|
1fd9cbec1f | ||
|
|
1aedcf1ae4 | ||
|
|
e0075cc9ac | ||
|
|
372dd704d2 | ||
|
|
14b35cb069 | ||
|
|
c1b240857f | ||
|
|
0f5eb30a0f | ||
|
|
9e0f2c22b6 | ||
|
|
c0771c0096 | ||
|
|
c716ba16b3 | ||
|
|
96a904c077 | ||
|
|
f951aae3ee | ||
|
|
7a859f20bb | ||
|
|
3bfa8830a5 | ||
|
|
ec85b42a38 | ||
|
|
a216eb4e74 | ||
|
|
94476e418e | ||
|
|
fd1eb5f601 | ||
|
|
db7ec60c86 | ||
|
|
97e1a65b52 | ||
|
|
23497dddd4 | ||
|
|
e8a7def26e | ||
|
|
28a23c21f4 | ||
|
|
f32094aa43 | ||
|
|
c215255ae2 | ||
|
|
53b998cfab | ||
|
|
0a0a7d0fc3 | ||
|
|
d84ae236eb | ||
|
|
6dff84f435 | ||
|
|
1bfa5b5ee0 | ||
|
|
c3618bdc26 | ||
|
|
eebc5d90d1 | ||
|
|
596b3ddb4a | ||
|
|
e770af9fc9 | ||
|
|
d3c76da7a2 | ||
|
|
5f9880f17d | ||
|
|
d6b1018700 | ||
|
|
f934c8cb77 | ||
|
|
db4b5383fa | ||
|
|
4982cabcfb | ||
|
|
5594cd8fdf | ||
|
|
fbb32a9e45 | ||
|
|
1c559110a2 | ||
|
|
6f170db344 | ||
|
|
27a1401ae1 | ||
|
|
debf784d0c | ||
|
|
5a97f68778 | ||
|
|
d2ef75b22c | ||
|
|
115743263b | ||
|
|
59447999f0 | ||
|
|
4554ac1f3e | ||
|
|
d08d8db298 | ||
|
|
099f95f3cc | ||
|
|
20cf69c182 | ||
|
|
f931af2ee9 | ||
|
|
96fe9d51d6 | ||
|
|
d264c6f6a5 | ||
|
|
e17e635710 | ||
|
|
d4b9fc9e40 | ||
|
|
d66672a29c | ||
|
|
345b0055f3 | ||
|
|
7fdc049079 | ||
|
|
5caab9c6f3 | ||
|
|
03042c8282 | ||
|
|
c46028ff2d | ||
|
|
bd83344c21 | ||
|
|
183c05bcf5 | ||
|
|
a94591d811 | ||
|
|
6973e8c028 | ||
|
|
ab0b3f9b91 | ||
|
|
6f140be710 | ||
|
|
8780ec3687 | ||
|
|
1afe1c48b4 | ||
|
|
14b834e8ad | ||
|
|
0b53bd8f7e | ||
|
|
376a3553fa | ||
|
|
35653898cd | ||
|
|
ab1ba9595d | ||
|
|
2a7b9cf898 | ||
|
|
1224fb2d5a | ||
|
|
ac1d07ec66 | ||
|
|
1a534108bf | ||
|
|
c88f7bede5 | ||
|
|
598ef58849 | ||
|
|
2a485ef85a | ||
|
|
98ce42e9aa | ||
|
|
3612d5d171 | ||
|
|
83f8e76584 | ||
|
|
a108751e0c | ||
|
|
6f4d6c3daf | ||
|
|
a907369082 | ||
|
|
bc9060adfc | ||
|
|
4296dbade7 | ||
|
|
b1cee9969f | ||
|
|
9c0c4a11b1 | ||
|
|
b4324cb057 | ||
|
|
1d4dad0180 | ||
|
|
1e0e67c13d | ||
|
|
9c523cad52 | ||
|
|
6a1bf50700 | ||
|
|
d34cff1675 | ||
|
|
0a027caa47 | ||
|
|
10bfefdd06 | ||
|
|
ead1aed8d5 | ||
|
|
bc229f1a86 | ||
|
|
94f9cb4a79 | ||
|
|
acd5b1bdca | ||
|
|
9fed91b16f | ||
|
|
1627c92460 | ||
|
|
7bae22248a | ||
|
|
7b5b57c087 | ||
|
|
271217cf04 | ||
|
|
c5236d337e | ||
|
|
747bb949da | ||
|
|
a53bb1aac6 | ||
|
|
d3ecbac40a | ||
|
|
28fe8c5a0e | ||
|
|
6d79bbfb45 | ||
|
|
944c76c250 | ||
|
|
256ef2c7b4 | ||
|
|
2a0ae95e3e | ||
|
|
604ff07933 | ||
|
|
be8fc4e2e6 | ||
|
|
e92a625c5c | ||
|
|
e141823556 | ||
|
|
fbd0e9839d | ||
|
|
889ce8f64b | ||
|
|
6be56b1b86 | ||
|
|
78a5e89928 | ||
|
|
e09a02ec14 | ||
|
|
32ff4db36a | ||
|
|
9bb1c83999 | ||
|
|
93951dd4b4 | ||
|
|
1440f6c626 | ||
|
|
aacf83fd11 | ||
|
|
6c35892f05 | ||
|
|
3657a19645 | ||
|
|
ab423b3ba4 | ||
|
|
b4d6f78e42 | ||
|
|
49217291ad | ||
|
|
315d6bf82a | ||
|
|
486fc83323 | ||
|
|
3dbb013e16 | ||
|
|
b21ff707ba | ||
|
|
d985d9a41d | ||
|
|
178ee09614 | ||
|
|
95b67550eb | ||
|
|
48e0f8add6 | ||
|
|
d4f6ba2bdb | ||
|
|
e26b2f4919 | ||
|
|
208761fa46 | ||
|
|
03c47cc9f9 | ||
|
|
eb62810911 | ||
|
|
bf449492b5 | ||
|
|
4c08002d3e | ||
|
|
5e67861ea9 | ||
|
|
c657ba9218 | ||
|
|
ddfc22bb50 | ||
|
|
f9f4b3a9fd | ||
|
|
0e14624d39 | ||
|
|
f1d217b6d7 | ||
|
|
8126b198ba | ||
|
|
c2b46ceea1 | ||
|
|
fcceb278ec | ||
|
|
7a89960d21 | ||
|
|
4abdad5f35 | ||
|
|
6593a262bb | ||
|
|
b2f6b141d4 | ||
|
|
4bcf43122f | ||
|
|
70f30d2c53 | ||
|
|
a29a8d7d81 | ||
|
|
77f1bb4fdd | ||
|
|
53166b555f | ||
|
|
5f58fd3550 | ||
|
|
b7b6034d76 | ||
|
|
59974ea33e | ||
|
|
e4935d8717 | ||
|
|
de0c8e3789 | ||
|
|
bd290ffdab | ||
|
|
bfb11b6f79 | ||
|
|
8667c98449 | ||
|
|
4c72496599 | ||
|
|
aa8498ce0b | ||
|
|
ec155ed80c | ||
|
|
f849732f55 | ||
|
|
9856a8625d | ||
|
|
b1e99dc317 | ||
|
|
310ba9bb8a | ||
|
|
565a3062cb | ||
|
|
e8b74f0fc7 | ||
|
|
ca65154ecd | ||
|
|
e996a30333 | ||
|
|
e33a38556e | ||
|
|
3f2b3e0e1a | ||
|
|
dab0aef1e3 | ||
|
|
d9fb9c4928 | ||
|
|
964a6d0708 | ||
|
|
985282ba55 | ||
|
|
8534afe0e3 | ||
|
|
a70fa066fe | ||
|
|
3f8468dcc7 | ||
|
|
d9d9235fba | ||
|
|
5e20b244c5 | ||
|
|
40ebc4e990 | ||
|
|
70e7857b62 | ||
|
|
02a486e457 | ||
|
|
9e09a5bac9 | ||
|
|
ece7fc3ea9 | ||
|
|
0ea5c7fa48 | ||
|
|
3f64a1b33f | ||
|
|
02ee12402a | ||
|
|
837361e899 | ||
|
|
cff4f60923 | ||
|
|
fb23ac1d55 | ||
|
|
87a44f7f7f | ||
|
|
3b520ab0c4 | ||
|
|
044b5cba7c | ||
|
|
0f9591464a | ||
|
|
6a301060bf | ||
|
|
1e0b51a0a9 | ||
|
|
e19f0402da | ||
|
|
925ce3ad63 | ||
|
|
afc918c5e5 | ||
|
|
10b96f6cce | ||
|
|
d72e89d52b | ||
|
|
ef1a9330a4 | ||
|
|
eee1aca14f | ||
|
|
c8c95e063d | ||
|
|
0af9e57b82 | ||
|
|
e0899a8a9d | ||
|
|
093dadc0f6 | ||
|
|
23f92e49dd | ||
|
|
d1c909e9ed | ||
|
|
5dbc3b1ba1 | ||
|
|
85febea3d9 | ||
|
|
dca772cac6 | ||
|
|
26347ce779 | ||
|
|
b49f3c5b0c | ||
|
|
79be7cbd2c | ||
|
|
6fe8667885 | ||
|
|
b56d62c777 | ||
|
|
6f13aa2c5b | ||
|
|
d4510af249 | ||
|
|
fb784a9a96 | ||
|
|
25c7b1ddac | ||
|
|
71c7e15fc3 | ||
|
|
e6e0cf5977 | ||
|
|
775757e657 | ||
|
|
1a303ec7c2 | ||
|
|
c16edd65b0 | ||
|
|
1ff41ec7ec | ||
|
|
d874456361 | ||
|
|
6853eecffa | ||
|
|
71001adc5e | ||
|
|
b6a19d34c6 | ||
|
|
1364115b07 | ||
|
|
48c7254c84 | ||
|
|
60b6d2ade0 | ||
|
|
df43ff0841 | ||
|
|
5ee0ae44cd | ||
|
|
9df35840fe | ||
|
|
cff9557217 | ||
|
|
b5b6d398ae | ||
|
|
aba0c19215 | ||
|
|
5050371e0d | ||
|
|
81b36ef37d | ||
|
|
59e219e3d6 | ||
|
|
412d9e7a82 | ||
|
|
d01b55c790 | ||
|
|
5ed768065c | ||
|
|
e64703de03 | ||
|
|
71126eb34d | ||
|
|
6992e47a66 | ||
|
|
70d1f96681 | ||
|
|
83ed456c29 | ||
|
|
26607776a2 | ||
|
|
7a075e73ca | ||
|
|
6c15091df2 | ||
|
|
e063379cd8 | ||
|
|
3a49f2258b | ||
|
|
dddf056b20 | ||
|
|
bdbfed13ff | ||
|
|
9eb0800b66 | ||
|
|
713ec61031 | ||
|
|
264e84649e | ||
|
|
c4016cfa4d | ||
|
|
1cb248692b | ||
|
|
331315a83e | ||
|
|
e325b2192b | ||
|
|
86094bc6d2 | ||
|
|
f2b2b1974d | ||
|
|
080c496cc3 | ||
|
|
bcfa0f9ac5 | ||
|
|
746c0d25f7 | ||
|
|
9e450ce76b | ||
|
|
77bee89ea5 | ||
|
|
dbc80183e6 | ||
|
|
ff0371faa5 | ||
|
|
ebaddb3fe4 | ||
|
|
8f3aeeb182 | ||
|
|
3157e4f986 | ||
|
|
0fe18ee5b4 | ||
|
|
265d6da331 | ||
|
|
1c063ca308 | ||
|
|
5a5340431e | ||
|
|
869a8a57e1 | ||
|
|
dd15da28b2 | ||
|
|
47778c6fe7 | ||
|
|
0d90eed270 | ||
|
|
d8147348c6 | ||
|
|
6b85141a39 | ||
|
|
0fcc379ede | ||
|
|
f22f62339f | ||
|
|
934b9d9cf1 | ||
|
|
47dd068655 | ||
|
|
382dfd797c | ||
|
|
8e66031afe | ||
|
|
eea1d56468 | ||
|
|
6edcc58b9a | ||
|
|
7a8197208c | ||
|
|
acd6e0f423 | ||
|
|
e741e0d1d6 | ||
|
|
09f28a555f | ||
|
|
a9590561d2 | ||
|
|
9700776468 | ||
|
|
cec6940aeb | ||
|
|
e082ee17e4 | ||
|
|
2327fa7e4b | ||
|
|
bd25a8ccb4 | ||
|
|
fff0c8ccd3 | ||
|
|
df6a10862e | ||
|
|
a33b7b2769 | ||
|
|
5e01db8809 | ||
|
|
32c064a1be | ||
|
|
610938b6e8 | ||
|
|
ea93e3863b | ||
|
|
ab38efd69e | ||
|
|
8dc5642599 | ||
|
|
d8c5ab7eae | ||
|
|
49e7a24e28 | ||
|
|
6f2de65b64 | ||
|
|
34603d69ab | ||
|
|
afb79391dd | ||
|
|
7f1dcf93d9 | ||
|
|
04722a7d83 | ||
|
|
fd6a319928 | ||
|
|
e91a744bd9 | ||
|
|
876b369c0d | ||
|
|
75ac76a8e5 | ||
|
|
6c4bde4ceb | ||
|
|
cfc9c655c5 | ||
|
|
d576573cf8 | ||
|
|
1559f14f11 | ||
|
|
242151eedf | ||
|
|
7cf10e90f8 | ||
|
|
5201c73d35 | ||
|
|
07f313a4a6 | ||
|
|
fa08434690 | ||
|
|
8969c58ca5 | ||
|
|
810cebfe0a | ||
|
|
d607b96021 | ||
|
|
d098930351 | ||
|
|
143e729a5e | ||
|
|
5ef4b593a2 | ||
|
|
7eb98c05eb | ||
|
|
43ec58fe85 | ||
|
|
0a5f042555 | ||
|
|
44f8c2a933 | ||
|
|
ecbdbedd96 | ||
|
|
c6a4e4c533 | ||
|
|
afb0a8af33 | ||
|
|
8d222fd0c3 | ||
|
|
9795d5655f | ||
|
|
db6c7f1a2c | ||
|
|
cfdce853a3 | ||
|
|
01b2f238c5 | ||
|
|
5a327b1d6b | ||
|
|
96b60d8ac0 | ||
|
|
e7f1d98b64 | ||
|
|
c2ddf17aa2 | ||
|
|
302d9dfad4 | ||
|
|
a83008aad3 | ||
|
|
047df5917a | ||
|
|
6b53e9f9a8 | ||
|
|
d66028a1b9 | ||
|
|
51de6756a7 | ||
|
|
74d97cc220 | ||
|
|
cf2ad6f277 | ||
|
|
b05d3fbde2 | ||
|
|
7b0db24e00 | ||
|
|
21f0094d7c | ||
|
|
99c2e76e53 | ||
|
|
50c40cb4e9 | ||
|
|
5e8b461478 | ||
|
|
73db4b365b | ||
|
|
0cf5bee09a | ||
|
|
3d54d78be8 | ||
|
|
bd537ece00 | ||
|
|
1f282693b3 | ||
|
|
e16f306e1d | ||
|
|
6b24673b6f | ||
|
|
58886dc42d | ||
|
|
8cea501748 | ||
|
|
c5171e8722 | ||
|
|
5af235a345 | ||
|
|
8133ef2036 | ||
|
|
9269e1e943 | ||
|
|
b4c82d0aac | ||
|
|
39ffc28cb4 | ||
|
|
e23488a723 | ||
|
|
ba1cb61f4d | ||
|
|
921ec8732c | ||
|
|
51a1f56011 | ||
|
|
2feb293c12 | ||
|
|
9d9f233c15 | ||
|
|
9ad948632c | ||
|
|
9b40ebe00c | ||
|
|
dab3ed5fbe | ||
|
|
34d7b18c26 | ||
|
|
dc4115d4e3 | ||
|
|
5cfd0aac1c | ||
|
|
9481cbf7e8 | ||
|
|
748f9b52aa | ||
|
|
c5ce3342e2 | ||
|
|
6c4ca046a9 | ||
|
|
256697c39e | ||
|
|
4017876fe2 | ||
|
|
9651614894 | ||
|
|
3bc2baaf9d | ||
|
|
fa05352882 | ||
|
|
849ca24ac8 | ||
|
|
84e9d225e9 | ||
|
|
7b53a43c5c | ||
|
|
e3121781d0 | ||
|
|
2c788b1b38 | ||
|
|
369606f0f1 | ||
|
|
8d4ce5e486 | ||
|
|
1b691e5887 | ||
|
|
2d7d19880f | ||
|
|
8b06af01b0 | ||
|
|
ea3899f469 | ||
|
|
adb8b6cbca | ||
|
|
252950af73 | ||
|
|
14189e1bca | ||
|
|
ed60c5dca4 | ||
|
|
c4984a7576 | ||
|
|
6a1052d95f | ||
|
|
caa3bcb02b | ||
|
|
74123d8ff3 | ||
|
|
70b858d200 | ||
|
|
3ca712a223 | ||
|
|
cf4be99093 | ||
|
|
900a55614b | ||
|
|
f76b0dc7d3 | ||
|
|
4542c96eae | ||
|
|
dd242bc1fe | ||
|
|
456a38022f | ||
|
|
34d73af8bf | ||
|
|
238b1cb925 | ||
|
|
604255bfba | ||
|
|
cfc0367c55 | ||
|
|
488804308b | ||
|
|
6fac8fe940 | ||
|
|
b53b1edd29 | ||
|
|
5a2bac9979 | ||
|
|
e7181c255b | ||
|
|
ac074ca23d | ||
|
|
b21bb3d433 | ||
|
|
0e656c8520 | ||
|
|
708ff794b0 | ||
|
|
6e34f12ac8 | ||
|
|
f4a89d62e3 | ||
|
|
a36e39205b | ||
|
|
6d45c09ea2 | ||
|
|
9fac65f31f | ||
|
|
3789674666 | ||
|
|
a22774b278 | ||
|
|
81bb8dd65f | ||
|
|
9d3796b605 | ||
|
|
ba4caf179a | ||
|
|
1febe765bf | ||
|
|
087ac992a2 | ||
|
|
3e3dfc83aa | ||
|
|
27bf6069d7 | ||
|
|
3153d196a2 | ||
|
|
8c320f729a | ||
|
|
c7fdc77ec8 | ||
|
|
dfd20ced59 | ||
|
|
5f40feb1af | ||
|
|
ae754fe334 | ||
|
|
e062db9411 | ||
|
|
023eae825d | ||
|
|
1a4baeafb6 | ||
|
|
b754972490 | ||
|
|
0c36e4e40d | ||
|
|
1f97495fdd | ||
|
|
c164c0f9e3 | ||
|
|
f0b5ec1025 | ||
|
|
b72ea2d02c | ||
|
|
238fcf1176 | ||
|
|
c354fecc6d | ||
|
|
4434d1da21 | ||
|
|
17ffc7cef9 | ||
|
|
006fd25e56 | ||
|
|
f2eafe0302 | ||
|
|
52069cc1d0 | ||
|
|
d9374f0389 | ||
|
|
19520f9688 | ||
|
|
1eda5af7df | ||
|
|
2ea8487e04 | ||
|
|
b0885ada00 | ||
|
|
1c6c0c4d81 | ||
|
|
3011b0ac8e | ||
|
|
e4334cccb3 | ||
|
|
645ca570a8 | ||
|
|
fd30da2f95 | ||
|
|
b378ad2885 | ||
|
|
9c45031a33 | ||
|
|
e4606b4d05 | ||
|
|
07e690caf0 | ||
|
|
223de9d1aa | ||
|
|
a22b9e925a | ||
|
|
90e7688cea | ||
|
|
9ecd48f523 | ||
|
|
ce9d1837a7 | ||
|
|
bbf83eca3e | ||
|
|
65c28d6628 | ||
|
|
7d7c521bce | ||
|
|
f0ac7818b3 | ||
|
|
07d666b030 | ||
|
|
baaee9f92e | ||
|
|
3a9be7a890 | ||
|
|
3c1547bd6c | ||
|
|
92bf8b59e6 | ||
|
|
4b39312c2c | ||
|
|
ea767650ae | ||
|
|
e0afd19a94 | ||
|
|
767bb58ac9 | ||
|
|
8a78308dbc | ||
|
|
d2db1e56cc | ||
|
|
5f8ebbaf6a | ||
|
|
52758b6900 | ||
|
|
3bb2405563 | ||
|
|
c6403325f5 | ||
|
|
e17b5356dd | ||
|
|
d87cb41b75 | ||
|
|
40154788d6 | ||
|
|
a8f42d61fa | ||
|
|
b923f66c56 | ||
|
|
a0355af13b | ||
|
|
112945b3f6 | ||
|
|
09c46595e5 | ||
|
|
003b0c875e | ||
|
|
ee2668ac65 | ||
|
|
f47bf4771f | ||
|
|
8551830517 | ||
|
|
8b78090a57 | ||
|
|
d2bdb2a791 | ||
|
|
f8f0114f06 | ||
|
|
8619d48c80 | ||
|
|
4171cc45f5 | ||
|
|
5bccd2c6c9 | ||
|
|
a60626468c | ||
|
|
f9cb5d5966 | ||
|
|
cad1c8c462 | ||
|
|
19e2af08e0 | ||
|
|
4386b19b06 | ||
|
|
da90826bd0 | ||
|
|
33509a49e0 | ||
|
|
514d9f3eb3 | ||
|
|
690805d7ca | ||
|
|
eda359e20f | ||
|
|
46882ed192 | ||
|
|
f1f37b6598 | ||
|
|
08c593578f | ||
|
|
6625de9005 | ||
|
|
bbd5766688 | ||
|
|
ccf2f60068 | ||
|
|
fa4abed028 | ||
|
|
804a356f13 | ||
|
|
f03a2e3657 | ||
|
|
e6f51af34c | ||
|
|
db85883026 | ||
|
|
a3e29a8d5c | ||
|
|
e5e9bc2ef7 | ||
|
|
41aee35b5d | ||
|
|
7081a9eff5 | ||
|
|
be839f3a3b | ||
|
|
f35b27451f | ||
|
|
016e59860f | ||
|
|
d9719923c1 | ||
|
|
15a6c5f6a8 | ||
|
|
88d4580f9e | ||
|
|
87d82aa674 | ||
|
|
d58c7f8fab | ||
|
|
8d30dfacd5 | ||
|
|
7340b0aad3 | ||
|
|
03fd8c93eb | ||
|
|
f727aa0a17 | ||
|
|
502c356784 | ||
|
|
0e7291a912 | ||
|
|
2dc14097b8 | ||
|
|
9655d60bf8 | ||
|
|
cc920682fd | ||
|
|
205f69b3ab | ||
|
|
c4376f081b | ||
|
|
1ae062dc6e | ||
|
|
ea3bae39cc | ||
|
|
0bb62ce43f | ||
|
|
a4595090c4 | ||
|
|
9df3f52fd5 | ||
|
|
6f37ff1831 | ||
|
|
cba7bec477 | ||
|
|
e990db8070 | ||
|
|
f8404f336d | ||
|
|
74d2ee9430 | ||
|
|
856bd56cde | ||
|
|
4c91880a85 |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -221,3 +221,8 @@ _gsdata_/
|
||||
.gitattributes
|
||||
.gitignore
|
||||
Moose Test Missions/MOOSE_Test_Template.miz
|
||||
Moose Development/Moose/.vscode/launch.json
|
||||
MooseCodeWS.code-workspace
|
||||
.gitignore
|
||||
.gitignore
|
||||
/.gitignore
|
||||
|
||||
0
.scannerwork/.sonar_lock
Normal file
0
.scannerwork/.sonar_lock
Normal file
6
.scannerwork/report-task.txt
Normal file
6
.scannerwork/report-task.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
projectKey=Test
|
||||
serverUrl=http://localhost:9000
|
||||
serverVersion=8.1.0.31237
|
||||
dashboardUrl=http://localhost:9000/dashboard?id=Test
|
||||
ceTaskId=AXAlUJO97YLjwz1VUDXR
|
||||
ceTaskUrl=http://localhost:9000/api/ce/task?id=AXAlUJO97YLjwz1VUDXR
|
||||
@@ -1,66 +1,67 @@
|
||||
--- **AI** -- (R2.2) - Models the process of Combat Air Patrol (CAP) for airplanes.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_A2A_Cap
|
||||
-- @image AI_Combat_Air_Patrol.JPG
|
||||
|
||||
--- @type AI_A2A_CAP
|
||||
-- @extends AI.AI_A2A_Patrol#AI_A2A_PATROL
|
||||
-- @extends AI.AI_Air_Patrol#AI_AIR_PATROL
|
||||
-- @extends AI.AI_Air_Engage#AI_AIR_ENGAGE
|
||||
|
||||
|
||||
--- The AI_A2A_CAP class implements the core functions to patrol a @{Zone} by an AI @{Wrapper.Group} or @{Wrapper.Group}
|
||||
--- The AI_A2A_CAP class implements the core functions to patrol a @{Zone} by an AI @{Wrapper.Group} or @{Wrapper.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 @{Wrapper.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.AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process.
|
||||
-- * **@{AI.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.
|
||||
@@ -73,30 +74,61 @@
|
||||
-- * **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,
|
||||
--
|
||||
-- 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.AI_CAP#AI_A2A_CAP.SetEngageRange}() to define that range.
|
||||
--
|
||||
-- ## 4. Set the Zone of Engagement
|
||||
--
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- An optional @{Zone} can be set,
|
||||
--
|
||||
-- An optional @{Zone} can be set,
|
||||
-- that will define when the AI will engage with the detected airborne enemy targets.
|
||||
-- Use the method @{AI.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 DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
|
||||
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
|
||||
-- @param DCS#AltitudeType EngageAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO".
|
||||
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed.
|
||||
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO".
|
||||
-- @return #AI_A2A_CAP
|
||||
function AI_A2A_CAP:New2( AICap, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType, PatrolZone, PatrolMinSpeed, PatrolMaxSpeed, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolAltType )
|
||||
|
||||
-- Multiple inheritance ... :-)
|
||||
local AI_Air = AI_AIR:New( AICap )
|
||||
local AI_Air_Patrol = AI_AIR_PATROL:New( AI_Air, AICap, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) -- #AI_AIR_PATROL
|
||||
local AI_Air_Engage = AI_AIR_ENGAGE:New( AI_Air_Patrol, AICap, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType )
|
||||
local self = BASE:Inherit( self, AI_Air_Engage ) --#AI_A2A_CAP
|
||||
|
||||
self:SetFuelThreshold( .2, 60 )
|
||||
self:SetDamageThreshold( 0.4 )
|
||||
self:SetDisengageRadius( 70000 )
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Creates a new AI_A2A_CAP object
|
||||
-- @param #AI_A2A_CAP self
|
||||
-- @param Wrapper.Group#GROUP AICap
|
||||
@@ -111,174 +143,8 @@ AI_A2A_CAP = {
|
||||
-- @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
|
||||
return self:New2( AICap, EngageMinSpeed, EngageMaxSpeed, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolZone, PatrolMinSpeed, PatrolMaxSpeed, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolAltType, PatrolAltType )
|
||||
|
||||
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.
|
||||
@@ -289,200 +155,58 @@ end
|
||||
-- @param #string To The To State string.
|
||||
function AI_A2A_CAP:onafterStart( AICap, From, Event, To )
|
||||
|
||||
self:GetParent( self ).onafterStart( self, AICap, From, Event, To )
|
||||
self:GetParent( self, AI_A2A_CAP ).onafterStart( self, AICap, From, Event, To )
|
||||
AICap:HandleEvent( EVENTS.Takeoff, nil, self )
|
||||
|
||||
end
|
||||
|
||||
--- Set the Engage Zone which defines where the AI will engage bogies.
|
||||
--- 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
|
||||
if EngageZone then
|
||||
self.EngageZone = EngageZone
|
||||
else
|
||||
self.EngageZone = nil
|
||||
end
|
||||
end
|
||||
|
||||
--- Set the Engage Range when the AI will engage with airborne enemies.
|
||||
--- 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
|
||||
if EngageRange then
|
||||
self.EngageRange = EngageRange
|
||||
else
|
||||
self.EngageRange = nil
|
||||
end
|
||||
end
|
||||
|
||||
--- onafter State Transition for Event Patrol.
|
||||
--- Evaluate the attack and create an AttackUnitTask list.
|
||||
-- @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 )
|
||||
-- @param Core.Set#SET_UNIT AttackSetUnit The set of units to attack.
|
||||
-- @param Wrappper.Group#GROUP DefenderGroup The group of defenders.
|
||||
-- @param #number EngageAltitude The altitude to engage the targets.
|
||||
-- @return #AI_A2A_CAP self
|
||||
function AI_A2A_CAP:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageAltitude )
|
||||
|
||||
-- Call the parent Start event handler
|
||||
self:GetParent(self).onafterPatrol( self, AICap, From, Event, To )
|
||||
self:HandleEvent( EVENTS.Dead )
|
||||
local AttackUnitTasks = {}
|
||||
|
||||
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 )
|
||||
for AttackUnitID, AttackUnit in pairs( self.AttackSetUnit:GetSet() ) do
|
||||
local AttackUnit = AttackUnit -- Wrapper.Unit#UNIT
|
||||
if AttackUnit and AttackUnit:IsAlive() and AttackUnit:IsAir() then
|
||||
-- TODO: Add coalition check? Only attack units of if AttackUnit:GetCoalition()~=AICap:GetCoalition()
|
||||
-- Maybe the detected set also contains
|
||||
self:T( { "Attacking Task:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsAir() } )
|
||||
AttackUnitTasks[#AttackUnitTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit )
|
||||
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
|
||||
|
||||
|
||||
return AttackUnitTasks
|
||||
end
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,12 @@
|
||||
--- **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.AI_A2A_GCI
|
||||
-- @image AI_Ground_Control_Intercept.JPG
|
||||
@@ -18,52 +18,52 @@
|
||||
|
||||
|
||||
--- Implements the core functions to intercept intruders. Use the Engage trigger to intercept intruders.
|
||||
--
|
||||
--
|
||||
-- 
|
||||
--
|
||||
--
|
||||
-- The AI_A2A_GCI is assigned a @{Wrapper.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.AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process.
|
||||
-- * **@{AI.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.
|
||||
@@ -76,25 +76,25 @@
|
||||
-- * **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,
|
||||
--
|
||||
-- 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.AI_GCI#AI_A2A_GCI.SetEngageRange}() to define that range.
|
||||
--
|
||||
-- ## 4. Set the Zone of Engagement
|
||||
--
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- An optional @{Zone} can be set,
|
||||
--
|
||||
-- An optional @{Zone} can be set,
|
||||
-- that will define when the AI will engage with the detected airborne enemy targets.
|
||||
-- Use the method @{AI.AI_Cap#AI_A2A_GCI.SetEngageZone}() to define that Zone.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- @field #AI_A2A_GCI
|
||||
AI_A2A_GCI = {
|
||||
ClassName = "AI_A2A_GCI",
|
||||
@@ -105,183 +105,39 @@ AI_A2A_GCI = {
|
||||
--- Creates a new AI_A2A_GCI object
|
||||
-- @param #AI_A2A_GCI self
|
||||
-- @param Wrapper.Group#GROUP AIIntercept
|
||||
-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
|
||||
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
|
||||
-- @param DCS#AltitudeType EngageAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO".
|
||||
-- @return #AI_A2A_GCI
|
||||
function AI_A2A_GCI:New( AIIntercept, EngageMinSpeed, EngageMaxSpeed )
|
||||
function AI_A2A_GCI:New2( AIIntercept, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType )
|
||||
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, AI_A2A:New( AIIntercept ) ) -- #AI_A2A_GCI
|
||||
local AI_Air = AI_AIR:New( AIIntercept )
|
||||
local AI_Air_Engage = AI_AIR_ENGAGE:New( AI_Air, AIIntercept, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType )
|
||||
local self = BASE:Inherit( self, AI_Air_Engage ) -- #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.
|
||||
self:SetFuelThreshold( .2, 60 )
|
||||
self:SetDamageThreshold( 0.4 )
|
||||
self:SetDisengageRadius( 70000 )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Creates a new AI_A2A_GCI object
|
||||
-- @param #AI_A2A_GCI self
|
||||
-- @param Wrapper.Group#GROUP AIIntercept
|
||||
-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
|
||||
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
|
||||
-- @param DCS#AltitudeType EngageAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO".
|
||||
-- @return #AI_A2A_GCI
|
||||
function AI_A2A_GCI:New( AIIntercept, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType )
|
||||
|
||||
return self:New2( AIIntercept, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType )
|
||||
end
|
||||
|
||||
--- onafter State Transition for Event Patrol.
|
||||
-- @param #AI_A2A_GCI self
|
||||
-- @param Wrapper.Group#GROUP AIIntercept The AI Group managed by the FSM.
|
||||
@@ -290,173 +146,29 @@ end
|
||||
-- @param #string To The To State string.
|
||||
function AI_A2A_GCI:onafterStart( AIIntercept, From, Event, To )
|
||||
|
||||
self:GetParent( self ).onafterStart( self, AIIntercept, From, Event, To )
|
||||
AIIntercept:HandleEvent( EVENTS.Takeoff, nil, self )
|
||||
|
||||
self:GetParent( self, AI_A2A_GCI ).onafterStart( self, AIIntercept, From, Event, To )
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- onafter State Transition for Event Patrol.
|
||||
--- Evaluate the attack and create an AttackUnitTask list.
|
||||
-- @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 )
|
||||
-- @param Core.Set#SET_UNIT AttackSetUnit The set of units to attack.
|
||||
-- @param Wrappper.Group#GROUP DefenderGroup The group of defenders.
|
||||
-- @param #number EngageAltitude The altitude to engage the targets.
|
||||
-- @return #AI_A2A_GCI self
|
||||
function AI_A2A_GCI:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageAltitude )
|
||||
|
||||
self:HandleEvent( EVENTS.Dead )
|
||||
local AttackUnitTasks = {}
|
||||
|
||||
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 )
|
||||
|
||||
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
|
||||
-- TODO: Add coalition check? Only attack units of if AttackUnit:GetCoalition()~=AICap:GetCoalition()
|
||||
-- Maybe the detected set also contains
|
||||
AttackUnitTasks[#AttackUnitTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit )
|
||||
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
|
||||
|
||||
return AttackUnitTasks
|
||||
end
|
||||
|
||||
@@ -121,13 +121,13 @@ AI_A2A_PATROL = {
|
||||
|
||||
--- Creates a new AI_A2A_PATROL object
|
||||
-- @param #AI_A2A_PATROL self
|
||||
-- @param Wrapper.Group#GROUP AIPatrol
|
||||
-- @param Wrapper.Group#GROUP AIPatrol The patrol group object.
|
||||
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed.
|
||||
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
|
||||
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to BARO
|
||||
-- @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.
|
||||
@@ -136,8 +136,14 @@ AI_A2A_PATROL = {
|
||||
-- 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
|
||||
local AI_Air = AI_AIR:New( AIPatrol )
|
||||
local AI_Air_Patrol = AI_AIR_PATROL:New( AI_Air, AIPatrol, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
|
||||
local self = BASE:Inherit( self, AI_Air_Patrol ) -- #AI_A2A_PATROL
|
||||
|
||||
self:SetFuelThreshold( .2, 60 )
|
||||
self:SetDamageThreshold( 0.4 )
|
||||
self:SetDisengageRadius( 70000 )
|
||||
|
||||
|
||||
self.PatrolZone = PatrolZone
|
||||
self.PatrolFloorAltitude = PatrolFloorAltitude
|
||||
@@ -145,8 +151,8 @@ function AI_A2A_PATROL:New( AIPatrol, PatrolZone, PatrolFloorAltitude, PatrolCei
|
||||
self.PatrolMinSpeed = PatrolMinSpeed
|
||||
self.PatrolMaxSpeed = PatrolMaxSpeed
|
||||
|
||||
-- defafult PatrolAltType to "RADIO" if not specified
|
||||
self.PatrolAltType = PatrolAltType or "RADIO"
|
||||
-- defafult PatrolAltType to "BARO" if not specified
|
||||
self.PatrolAltType = PatrolAltType or "BARO"
|
||||
|
||||
self:AddTransition( { "Started", "Airborne", "Refuelling" }, "Patrol", "Patrolling" )
|
||||
|
||||
@@ -281,15 +287,15 @@ function AI_A2A_PATROL:onafterPatrol( AIPatrol, From, Event, To )
|
||||
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.
|
||||
--- 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.
|
||||
-- @param Wrapper.Group#GROUP AIPatrol The AI group.
|
||||
-- @param #AI_A2A_PATROL Fsm The FSM.
|
||||
function AI_A2A_PATROL.PatrolRoute( AIPatrol, Fsm )
|
||||
|
||||
AIPatrol:F( { "AI_A2A_PATROL.PatrolRoute:", AIPatrol:GetName() } )
|
||||
|
||||
if AIPatrol:IsAlive() then
|
||||
if AIPatrol and AIPatrol:IsAlive() then
|
||||
Fsm:Route()
|
||||
end
|
||||
|
||||
@@ -303,7 +309,6 @@ end
|
||||
-- @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.
|
||||
@@ -312,7 +317,7 @@ function AI_A2A_PATROL:onafterRoute( AIPatrol, From, Event, To )
|
||||
end
|
||||
|
||||
|
||||
if AIPatrol:IsAlive() then
|
||||
if AIPatrol and AIPatrol:IsAlive() then
|
||||
|
||||
local PatrolRoute = {}
|
||||
|
||||
@@ -320,44 +325,80 @@ function AI_A2A_PATROL:onafterRoute( AIPatrol, From, Event, To )
|
||||
|
||||
local CurrentCoord = AIPatrol:GetCoordinate()
|
||||
|
||||
local ToTargetCoord = self.PatrolZone:GetRandomPointVec2()
|
||||
ToTargetCoord:SetAlt( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ) )
|
||||
self:SetTargetDistance( ToTargetCoord ) -- For RTB status check
|
||||
-- Random altitude.
|
||||
local altitude=math.random(self.PatrolFloorAltitude, self.PatrolCeilingAltitude)
|
||||
|
||||
-- Random speed in km/h.
|
||||
local speedkmh = math.random(self.PatrolMinSpeed, self.PatrolMaxSpeed)
|
||||
|
||||
local ToTargetSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed )
|
||||
-- First waypoint is current position.
|
||||
PatrolRoute[1]=CurrentCoord:WaypointAirTurningPoint(nil, speedkmh, {}, "Current")
|
||||
|
||||
--- Create a route point of type air.
|
||||
local ToPatrolRoutePoint = ToTargetCoord:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
ToTargetSpeed,
|
||||
true
|
||||
)
|
||||
if self.racetrack then
|
||||
|
||||
-- Random heading.
|
||||
local heading = math.random(self.racetrackheadingmin, self.racetrackheadingmax)
|
||||
|
||||
-- Random leg length.
|
||||
local leg=math.random(self.racetracklegmin, self.racetracklegmax)
|
||||
|
||||
-- Random duration if any.
|
||||
local duration = self.racetrackdurationmin
|
||||
if self.racetrackdurationmax then
|
||||
duration=math.random(self.racetrackdurationmin, self.racetrackdurationmax)
|
||||
end
|
||||
|
||||
-- CAP coordinate.
|
||||
local c0=self.PatrolZone:GetRandomCoordinate()
|
||||
if self.racetrackcapcoordinates and #self.racetrackcapcoordinates>0 then
|
||||
c0=self.racetrackcapcoordinates[math.random(#self.racetrackcapcoordinates)]
|
||||
end
|
||||
|
||||
-- Race track points.
|
||||
local c1=c0:SetAltitude(altitude) --Core.Point#COORDINATE
|
||||
local c2=c1:Translate(leg, heading):SetAltitude(altitude)
|
||||
|
||||
self:SetTargetDistance(c0) -- For RTB status check
|
||||
|
||||
-- Debug:
|
||||
self:T(string.format("Patrol zone race track: v=%.1f knots, h=%.1f ft, heading=%03d, leg=%d m, t=%s sec", UTILS.KmphToKnots(speedkmh), UTILS.MetersToFeet(altitude), heading, leg, tostring(duration)))
|
||||
--c1:MarkToAll("Race track c1")
|
||||
--c2:MarkToAll("Race track c2")
|
||||
|
||||
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 )
|
||||
|
||||
-- Task to orbit.
|
||||
local taskOrbit=AIPatrol:TaskOrbit(c1, altitude, UTILS.KmphToMps(speedkmh), c2)
|
||||
|
||||
-- Task function to redo the patrol at other random position.
|
||||
local taskPatrol=AIPatrol:TaskFunction("AI_A2A_PATROL.PatrolRoute", self)
|
||||
|
||||
-- Controlled task with task condition.
|
||||
local taskCond=AIPatrol:TaskCondition(nil, nil, nil, nil, duration, nil)
|
||||
local taskCont=AIPatrol:TaskControlled(taskOrbit, taskCond)
|
||||
|
||||
-- Second waypoint
|
||||
PatrolRoute[2]=c1:WaypointAirTurningPoint(self.PatrolAltType, speedkmh, {taskCont, taskPatrol}, "CAP Orbit")
|
||||
|
||||
else
|
||||
|
||||
-- Target coordinate.
|
||||
local ToTargetCoord=self.PatrolZone:GetRandomCoordinate() --Core.Point#COORDINATE
|
||||
ToTargetCoord:SetAltitude(altitude)
|
||||
|
||||
self:SetTargetDistance( ToTargetCoord ) -- For RTB status check
|
||||
|
||||
local taskReRoute=AIPatrol:TaskFunction( "AI_A2A_PATROL.PatrolRoute", self )
|
||||
|
||||
PatrolRoute[2]=ToTargetCoord:WaypointAirTurningPoint(self.PatrolAltType, speedkmh, {taskReRoute}, "Patrol Point")
|
||||
|
||||
end
|
||||
|
||||
-- ROE
|
||||
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.AI_A2A#AI_A2A
|
||||
_AI_A2A:__Reset( 1 )
|
||||
_AI_A2A:__Route( 5 )
|
||||
end
|
||||
|
||||
-- Patrol.
|
||||
AIPatrol:Route( PatrolRoute, 0.5)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
99
Moose Development/Moose/AI/AI_A2G_BAI.lua
Normal file
99
Moose Development/Moose/AI/AI_A2G_BAI.lua
Normal file
@@ -0,0 +1,99 @@
|
||||
--- **AI** -- Models the process of air to ground BAI engagement for airplanes and helicopters.
|
||||
--
|
||||
-- This is a class used in the @{AI_A2G_Dispatcher}.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_A2G_BAI
|
||||
-- @image AI_Air_To_Ground_Engage.JPG
|
||||
|
||||
|
||||
|
||||
--- @type AI_A2G_BAI
|
||||
-- @extends AI.AI_A2A_Engage#AI_A2A_Engage
|
||||
|
||||
|
||||
--- Implements the core functions to intercept intruders. Use the Engage trigger to intercept intruders.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #AI_A2G_BAI
|
||||
AI_A2G_BAI = {
|
||||
ClassName = "AI_A2G_BAI",
|
||||
}
|
||||
|
||||
|
||||
|
||||
--- Creates a new AI_A2G_BAI object
|
||||
-- @param #AI_A2G_BAI self
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
|
||||
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
|
||||
-- @param DCS#AltitudeType EngageAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO".
|
||||
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed.
|
||||
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
|
||||
-- @return #AI_A2G_BAI
|
||||
function AI_A2G_BAI:New2( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
|
||||
|
||||
local AI_Air = AI_AIR:New( AIGroup )
|
||||
local AI_Air_Patrol = AI_AIR_PATROL:New( AI_Air, AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) -- #AI_AIR_PATROL
|
||||
local AI_Air_Engage = AI_AIR_ENGAGE:New( AI_Air_Patrol, AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType )
|
||||
local self = BASE:Inherit( self, AI_Air_Engage )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Creates a new AI_A2G_BAI object
|
||||
-- @param #AI_A2G_BAI self
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
|
||||
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
|
||||
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed.
|
||||
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
|
||||
-- @return #AI_A2G_BAI
|
||||
function AI_A2G_BAI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
|
||||
|
||||
return self:New2( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, PatrolAltType, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType)
|
||||
end
|
||||
|
||||
--- Evaluate the attack and create an AttackUnitTask list.
|
||||
-- @param #AI_A2G_BAI self
|
||||
-- @param Core.Set#SET_UNIT AttackSetUnit The set of units to attack.
|
||||
-- @param Wrappper.Group#GROUP DefenderGroup The group of defenders.
|
||||
-- @param #number EngageAltitude The altitude to engage the targets.
|
||||
-- @return #AI_A2G_BAI self
|
||||
function AI_A2G_BAI:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageAltitude )
|
||||
|
||||
local AttackUnitTasks = {}
|
||||
|
||||
local AttackSetUnitPerThreatLevel = AttackSetUnit:GetSetPerThreatLevel( 10, 0 )
|
||||
for AttackUnitIndex, AttackUnit in ipairs( AttackSetUnitPerThreatLevel or {} ) do
|
||||
if AttackUnit then
|
||||
if AttackUnit:IsAlive() and AttackUnit:IsGround() then
|
||||
self:T( { "BAI Unit:", AttackUnit:GetName() } )
|
||||
AttackUnitTasks[#AttackUnitTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit, true, false, nil, nil, EngageAltitude )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return AttackUnitTasks
|
||||
end
|
||||
|
||||
|
||||
100
Moose Development/Moose/AI/AI_A2G_CAS.lua
Normal file
100
Moose Development/Moose/AI/AI_A2G_CAS.lua
Normal file
@@ -0,0 +1,100 @@
|
||||
--- **AI** -- Models the process of air to ground engagement for airplanes and helicopters.
|
||||
--
|
||||
-- This is a class used in the @{AI_A2G_Dispatcher}.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_A2G_CAS
|
||||
-- @image AI_Air_To_Ground_Engage.JPG
|
||||
|
||||
|
||||
|
||||
--- @type AI_A2G_CAS
|
||||
-- @extends AI.AI_A2G_Patrol#AI_AIR_PATROL
|
||||
|
||||
|
||||
--- Implements the core functions to intercept intruders. Use the Engage trigger to intercept intruders.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #AI_A2G_CAS
|
||||
AI_A2G_CAS = {
|
||||
ClassName = "AI_A2G_CAS",
|
||||
}
|
||||
|
||||
|
||||
|
||||
--- Creates a new AI_A2G_CAS object
|
||||
-- @param #AI_A2G_CAS self
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
|
||||
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
|
||||
-- @param DCS#AltitudeType EngageAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO".
|
||||
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed.
|
||||
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
|
||||
-- @return #AI_A2G_CAS
|
||||
function AI_A2G_CAS:New2( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
|
||||
|
||||
local AI_Air = AI_AIR:New( AIGroup )
|
||||
local AI_Air_Patrol = AI_AIR_PATROL:New( AI_Air, AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) -- #AI_AIR_PATROL
|
||||
local AI_Air_Engage = AI_AIR_ENGAGE:New( AI_Air_Patrol, AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType )
|
||||
local self = BASE:Inherit( self, AI_Air_Engage )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Creates a new AI_A2G_CAS object
|
||||
-- @param #AI_A2G_CAS self
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
|
||||
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
|
||||
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed.
|
||||
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
|
||||
-- @return #AI_A2G_CAS
|
||||
function AI_A2G_CAS:New( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
|
||||
|
||||
return self:New2( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, PatrolAltType, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType)
|
||||
end
|
||||
|
||||
--- Evaluate the attack and create an AttackUnitTask list.
|
||||
-- @param #AI_A2G_CAS self
|
||||
-- @param Core.Set#SET_UNIT AttackSetUnit The set of units to attack.
|
||||
-- @param Wrappper.Group#GROUP DefenderGroup The group of defenders.
|
||||
-- @param #number EngageAltitude The altitude to engage the targets.
|
||||
-- @return #AI_A2G_CAS self
|
||||
function AI_A2G_CAS:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageAltitude )
|
||||
|
||||
local AttackUnitTasks = {}
|
||||
|
||||
local AttackSetUnitPerThreatLevel = AttackSetUnit:GetSetPerThreatLevel( 10, 0 )
|
||||
for AttackUnitIndex, AttackUnit in ipairs( AttackSetUnitPerThreatLevel or {} ) do
|
||||
if AttackUnit then
|
||||
if AttackUnit:IsAlive() and AttackUnit:IsGround() then
|
||||
self:T( { "CAS Unit:", AttackUnit:GetName() } )
|
||||
AttackUnitTasks[#AttackUnitTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit, true, false, nil, nil, EngageAltitude )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return AttackUnitTasks
|
||||
end
|
||||
|
||||
|
||||
|
||||
4711
Moose Development/Moose/AI/AI_A2G_Dispatcher.lua
Normal file
4711
Moose Development/Moose/AI/AI_A2G_Dispatcher.lua
Normal file
File diff suppressed because it is too large
Load Diff
152
Moose Development/Moose/AI/AI_A2G_SEAD.lua
Normal file
152
Moose Development/Moose/AI/AI_A2G_SEAD.lua
Normal file
@@ -0,0 +1,152 @@
|
||||
--- **AI** -- Models the process of air to ground SEAD engagement for airplanes and helicopters.
|
||||
--
|
||||
-- This is a class used in the @{AI_A2G_Dispatcher}.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_A2G_SEAD
|
||||
-- @image AI_Air_To_Ground_Engage.JPG
|
||||
|
||||
|
||||
|
||||
--- @type AI_A2G_SEAD
|
||||
-- @extends AI.AI_A2G_Patrol#AI_AIR_PATROL
|
||||
|
||||
|
||||
--- Implements the core functions to SEAD intruders. Use the Engage trigger to intercept intruders.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- The AI_A2G_SEAD is assigned a @{Wrapper.Group} and this must be done before the AI_A2G_SEAD 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_A2G_SEAD constructor
|
||||
--
|
||||
-- * @{#AI_A2G_SEAD.New}(): Creates a new AI_A2G_SEAD object.
|
||||
--
|
||||
-- ## 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.AI_GCI#AI_A2G_SEAD.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.AI_Cap#AI_A2G_SEAD.SetEngageZone}() to define that Zone.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #AI_A2G_SEAD
|
||||
AI_A2G_SEAD = {
|
||||
ClassName = "AI_A2G_SEAD",
|
||||
}
|
||||
|
||||
|
||||
|
||||
--- Creates a new AI_A2G_SEAD object
|
||||
-- @param #AI_A2G_SEAD self
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
|
||||
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
|
||||
-- @param DCS#AltitudeType EngageAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO".
|
||||
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed.
|
||||
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
|
||||
-- @return #AI_A2G_SEAD
|
||||
function AI_A2G_SEAD:New2( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
|
||||
|
||||
local AI_Air = AI_AIR:New( AIGroup )
|
||||
local AI_Air_Patrol = AI_AIR_PATROL:New( AI_Air, AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) -- #AI_AIR_PATROL
|
||||
local AI_Air_Engage = AI_AIR_ENGAGE:New( AI_Air_Patrol, AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType )
|
||||
local self = BASE:Inherit( self, AI_Air_Engage )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Creates a new AI_A2G_SEAD object
|
||||
-- @param #AI_A2G_SEAD self
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Altitude EngageFloorAltitude The lowest altitude in meters where to execute the engagement.
|
||||
-- @param DCS#Altitude EngageCeilingAltitude The highest altitude in meters where to execute the engagement.
|
||||
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed.
|
||||
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
|
||||
-- @return #AI_A2G_SEAD
|
||||
function AI_A2G_SEAD:New( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
|
||||
|
||||
return self:New2( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, PatrolAltType, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
|
||||
end
|
||||
|
||||
|
||||
--- Evaluate the attack and create an AttackUnitTask list.
|
||||
-- @param #AI_A2G_SEAD self
|
||||
-- @param Core.Set#SET_UNIT AttackSetUnit The set of units to attack.
|
||||
-- @param Wrappper.Group#GROUP DefenderGroup The group of defenders.
|
||||
-- @param #number EngageAltitude The altitude to engage the targets.
|
||||
-- @return #AI_A2G_SEAD self
|
||||
function AI_A2G_SEAD:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageAltitude )
|
||||
|
||||
local AttackUnitTasks = {}
|
||||
|
||||
local AttackSetUnitPerThreatLevel = AttackSetUnit:GetSetPerThreatLevel( 10, 0 )
|
||||
for AttackUnitID, AttackUnit in ipairs( AttackSetUnitPerThreatLevel ) do
|
||||
if AttackUnit then
|
||||
if AttackUnit:IsAlive() and AttackUnit:IsGround() then
|
||||
local HasRadar = AttackUnit:HasSEAD()
|
||||
if HasRadar then
|
||||
self:F( { "SEAD Unit:", AttackUnit:GetName() } )
|
||||
AttackUnitTasks[#AttackUnitTasks+1] = DefenderGroup:TaskAttackUnit( AttackUnit, true, false, nil, nil, EngageAltitude )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return AttackUnitTasks
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
--- **AI** -- (R2.2) - Models the process of air operations for airplanes.
|
||||
--- **AI** - Models the process of AI air operations.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -6,102 +6,99 @@
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_A2A
|
||||
-- @image AI_Air_To_Air_Dispatching.JPG
|
||||
-- @module AI.AI_Air
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
--BASE:TraceClass("AI_A2A")
|
||||
|
||||
|
||||
--- @type AI_A2A
|
||||
--- @type AI_AIR
|
||||
-- @extends Core.Fsm#FSM_CONTROLLABLE
|
||||
|
||||
--- The AI_A2A class implements the core functions to operate an AI @{Wrapper.Group} A2A tasking.
|
||||
--- The AI_AIR class implements the core functions to operate an AI @{Wrapper.Group}.
|
||||
--
|
||||
--
|
||||
-- ## AI_A2A constructor
|
||||
-- # 1) AI_AIR constructor
|
||||
--
|
||||
-- * @{#AI_A2A.New}(): Creates a new AI_A2A object.
|
||||
-- * @{#AI_AIR.New}(): Creates a new AI_AIR object.
|
||||
--
|
||||
-- ## 2. AI_A2A is a FSM
|
||||
-- # 2) AI_AIR is a Finite State Machine.
|
||||
--
|
||||
-- 
|
||||
-- This section must be read as follows. Each of the rows indicate a state transition, triggered through an event, and with an ending state of the event was executed.
|
||||
-- The first column is the **From** state, the second column the **Event**, and the third column the **To** state.
|
||||
--
|
||||
-- ### 2.1. AI_A2A States
|
||||
-- So, each of the rows have the following structure.
|
||||
--
|
||||
-- * **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.
|
||||
-- * **From** => **Event** => **To**
|
||||
--
|
||||
-- ### 2.2. AI_A2A Events
|
||||
-- Important to know is that an event can only be executed if the **current state** is the **From** state.
|
||||
-- This, when an **Event** that is being triggered has a **From** state that is equal to the **Current** state of the state machine, the event will be executed,
|
||||
-- and the resulting state will be the **To** state.
|
||||
--
|
||||
-- * **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
|
||||
-- These are the different possible state transitions of this state machine implementation:
|
||||
--
|
||||
-- * @{#AI_A2A.SetControllable}(): Set the AIControllable.
|
||||
-- * @{#AI_A2A.GetControllable}(): Get the AIControllable.
|
||||
-- * Idle => Start => Monitoring
|
||||
--
|
||||
-- @field #AI_A2A
|
||||
AI_A2A = {
|
||||
ClassName = "AI_A2A",
|
||||
-- ## 2.1) AI_AIR States.
|
||||
--
|
||||
-- * **Idle**: The process is idle.
|
||||
--
|
||||
-- ## 2.2) AI_AIR Events.
|
||||
--
|
||||
-- * **Start**: Start the transport process.
|
||||
-- * **Stop**: Stop the transport process.
|
||||
-- * **Monitor**: Monitor and take action.
|
||||
--
|
||||
-- @field #AI_AIR
|
||||
AI_AIR = {
|
||||
ClassName = "AI_AIR",
|
||||
}
|
||||
|
||||
--- 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 )
|
||||
AI_AIR.TaskDelay = 0.5 -- The delay of each task given to the AI.
|
||||
|
||||
--- Creates a new AI_AIR process.
|
||||
-- @param #AI_AIR self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The group object to receive the A2G Process.
|
||||
-- @return #AI_AIR
|
||||
function AI_AIR:New( AIGroup )
|
||||
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- #AI_A2A
|
||||
local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- #AI_AIR
|
||||
|
||||
self:SetControllable( AIGroup )
|
||||
|
||||
self:SetFuelThreshold( .2, 60 )
|
||||
self:SetDamageThreshold( 0.4 )
|
||||
self:SetDisengageRadius( 70000 )
|
||||
|
||||
self:SetStartState( "Stopped" )
|
||||
|
||||
|
||||
self:AddTransition( "*", "Queue", "Queued" )
|
||||
|
||||
self:AddTransition( "*", "Start", "Started" )
|
||||
|
||||
--- Start Handler OnBefore for AI_A2A
|
||||
-- @function [parent=#AI_A2A] OnBeforeStart
|
||||
-- @param #AI_A2A self
|
||||
--- Start Handler OnBefore for AI_AIR
|
||||
-- @function [parent=#AI_AIR] OnBeforeStart
|
||||
-- @param #AI_AIR 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
|
||||
--- Start Handler OnAfter for AI_AIR
|
||||
-- @function [parent=#AI_AIR] OnAfterStart
|
||||
-- @param #AI_AIR 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 Trigger for AI_AIR
|
||||
-- @function [parent=#AI_AIR] Start
|
||||
-- @param #AI_AIR self
|
||||
|
||||
--- Start Asynchronous Trigger for AI_A2A
|
||||
-- @function [parent=#AI_A2A] __Start
|
||||
-- @param #AI_A2A self
|
||||
--- Start Asynchronous Trigger for AI_AIR
|
||||
-- @function [parent=#AI_AIR] __Start
|
||||
-- @param #AI_AIR self
|
||||
-- @param #number Delay
|
||||
|
||||
self:AddTransition( "*", "Stop", "Stopped" )
|
||||
|
||||
--- OnLeave Transition Handler for State Stopped.
|
||||
-- @function [parent=#AI_A2A] OnLeaveStopped
|
||||
-- @param #AI_A2A self
|
||||
-- @function [parent=#AI_AIR] OnLeaveStopped
|
||||
-- @param #AI_AIR 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.
|
||||
@@ -109,16 +106,16 @@ function AI_A2A:New( AIGroup )
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnEnter Transition Handler for State Stopped.
|
||||
-- @function [parent=#AI_A2A] OnEnterStopped
|
||||
-- @param #AI_A2A self
|
||||
-- @function [parent=#AI_AIR] OnEnterStopped
|
||||
-- @param #AI_AIR 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
|
||||
-- @function [parent=#AI_AIR] OnBeforeStop
|
||||
-- @param #AI_AIR 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.
|
||||
@@ -126,27 +123,27 @@ function AI_A2A:New( AIGroup )
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event Stop.
|
||||
-- @function [parent=#AI_A2A] OnAfterStop
|
||||
-- @param #AI_A2A self
|
||||
-- @function [parent=#AI_AIR] OnAfterStop
|
||||
-- @param #AI_AIR 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
|
||||
-- @function [parent=#AI_AIR] Stop
|
||||
-- @param #AI_AIR self
|
||||
|
||||
--- Asynchronous Event Trigger for Event Stop.
|
||||
-- @function [parent=#AI_A2A] __Stop
|
||||
-- @param #AI_A2A self
|
||||
-- @function [parent=#AI_AIR] __Stop
|
||||
-- @param #AI_AIR self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
self:AddTransition( "*", "Status", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A.
|
||||
self:AddTransition( "*", "Status", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR.
|
||||
|
||||
--- OnBefore Transition Handler for Event Status.
|
||||
-- @function [parent=#AI_A2A] OnBeforeStatus
|
||||
-- @param #AI_A2A self
|
||||
-- @function [parent=#AI_AIR] OnBeforeStatus
|
||||
-- @param #AI_AIR 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.
|
||||
@@ -154,27 +151,27 @@ function AI_A2A:New( AIGroup )
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event Status.
|
||||
-- @function [parent=#AI_A2A] OnAfterStatus
|
||||
-- @param #AI_A2A self
|
||||
-- @function [parent=#AI_AIR] OnAfterStatus
|
||||
-- @param #AI_AIR 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
|
||||
-- @function [parent=#AI_AIR] Status
|
||||
-- @param #AI_AIR self
|
||||
|
||||
--- Asynchronous Event Trigger for Event Status.
|
||||
-- @function [parent=#AI_A2A] __Status
|
||||
-- @param #AI_A2A self
|
||||
-- @function [parent=#AI_AIR] __Status
|
||||
-- @param #AI_AIR self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
self:AddTransition( "*", "RTB", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A.
|
||||
self:AddTransition( "*", "RTB", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR.
|
||||
|
||||
--- OnBefore Transition Handler for Event RTB.
|
||||
-- @function [parent=#AI_A2A] OnBeforeRTB
|
||||
-- @param #AI_A2A self
|
||||
-- @function [parent=#AI_AIR] OnBeforeRTB
|
||||
-- @param #AI_AIR 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.
|
||||
@@ -182,25 +179,25 @@ function AI_A2A:New( AIGroup )
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnAfter Transition Handler for Event RTB.
|
||||
-- @function [parent=#AI_A2A] OnAfterRTB
|
||||
-- @param #AI_A2A self
|
||||
-- @function [parent=#AI_AIR] OnAfterRTB
|
||||
-- @param #AI_AIR 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
|
||||
-- @function [parent=#AI_AIR] RTB
|
||||
-- @param #AI_AIR self
|
||||
|
||||
--- Asynchronous Event Trigger for Event RTB.
|
||||
-- @function [parent=#AI_A2A] __RTB
|
||||
-- @param #AI_A2A self
|
||||
-- @function [parent=#AI_AIR] __RTB
|
||||
-- @param #AI_AIR self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
--- OnLeave Transition Handler for State Returning.
|
||||
-- @function [parent=#AI_A2A] OnLeaveReturning
|
||||
-- @param #AI_A2A self
|
||||
-- @function [parent=#AI_AIR] OnLeaveReturning
|
||||
-- @param #AI_AIR 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.
|
||||
@@ -208,8 +205,8 @@ function AI_A2A:New( AIGroup )
|
||||
-- @return #boolean Return false to cancel Transition.
|
||||
|
||||
--- OnEnter Transition Handler for State Returning.
|
||||
-- @function [parent=#AI_A2A] OnEnterReturning
|
||||
-- @param #AI_A2A self
|
||||
-- @function [parent=#AI_AIR] OnEnterReturning
|
||||
-- @param #AI_AIR 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.
|
||||
@@ -217,30 +214,30 @@ function AI_A2A:New( AIGroup )
|
||||
|
||||
self:AddTransition( "Patrolling", "Refuel", "Refuelling" )
|
||||
|
||||
--- Refuel Handler OnBefore for AI_A2A
|
||||
-- @function [parent=#AI_A2A] OnBeforeRefuel
|
||||
-- @param #AI_A2A self
|
||||
--- Refuel Handler OnBefore for AI_AIR
|
||||
-- @function [parent=#AI_AIR] OnBeforeRefuel
|
||||
-- @param #AI_AIR 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
|
||||
--- Refuel Handler OnAfter for AI_AIR
|
||||
-- @function [parent=#AI_AIR] OnAfterRefuel
|
||||
-- @param #AI_AIR 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 Trigger for AI_AIR
|
||||
-- @function [parent=#AI_AIR] Refuel
|
||||
-- @param #AI_AIR self
|
||||
|
||||
--- Refuel Asynchronous Trigger for AI_A2A
|
||||
-- @function [parent=#AI_A2A] __Refuel
|
||||
-- @param #AI_A2A self
|
||||
--- Refuel Asynchronous Trigger for AI_AIR
|
||||
-- @function [parent=#AI_AIR] __Refuel
|
||||
-- @param #AI_AIR self
|
||||
-- @param #number Delay
|
||||
|
||||
self:AddTransition( "*", "Takeoff", "Airborne" )
|
||||
@@ -266,15 +263,17 @@ function GROUP:OnEventTakeoff( EventData, Fsm )
|
||||
self:UnHandleEvent( EVENTS.Takeoff )
|
||||
end
|
||||
|
||||
function AI_A2A:SetDispatcher( Dispatcher )
|
||||
|
||||
|
||||
function AI_AIR:SetDispatcher( Dispatcher )
|
||||
self.Dispatcher = Dispatcher
|
||||
end
|
||||
|
||||
function AI_A2A:GetDispatcher()
|
||||
function AI_AIR:GetDispatcher()
|
||||
return self.Dispatcher
|
||||
end
|
||||
|
||||
function AI_A2A:SetTargetDistance( Coordinate )
|
||||
function AI_AIR:SetTargetDistance( Coordinate )
|
||||
|
||||
local CurrentCoord = self.Controllable:GetCoordinate()
|
||||
self.TargetDistance = CurrentCoord:Get2DDistance( Coordinate )
|
||||
@@ -283,7 +282,7 @@ function AI_A2A:SetTargetDistance( Coordinate )
|
||||
end
|
||||
|
||||
|
||||
function AI_A2A:ClearTargetDistance()
|
||||
function AI_AIR:ClearTargetDistance()
|
||||
|
||||
self.TargetDistance = nil
|
||||
self.ClosestTargetDistance = nil
|
||||
@@ -291,11 +290,11 @@ end
|
||||
|
||||
|
||||
--- Sets (modifies) the minimum and maximum speed of the patrol.
|
||||
-- @param #AI_A2A self
|
||||
-- @param #AI_AIR self
|
||||
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h.
|
||||
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h.
|
||||
-- @return #AI_A2A self
|
||||
function AI_A2A:SetSpeed( PatrolMinSpeed, PatrolMaxSpeed )
|
||||
-- @return #AI_AIR self
|
||||
function AI_AIR:SetSpeed( PatrolMinSpeed, PatrolMaxSpeed )
|
||||
self:F2( { PatrolMinSpeed, PatrolMaxSpeed } )
|
||||
|
||||
self.PatrolMinSpeed = PatrolMinSpeed
|
||||
@@ -303,12 +302,25 @@ function AI_A2A:SetSpeed( PatrolMinSpeed, PatrolMaxSpeed )
|
||||
end
|
||||
|
||||
|
||||
--- Sets (modifies) the minimum and maximum RTB speed of the patrol.
|
||||
-- @param #AI_AIR self
|
||||
-- @param DCS#Speed RTBMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h.
|
||||
-- @param DCS#Speed RTBMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h.
|
||||
-- @return #AI_AIR self
|
||||
function AI_AIR:SetRTBSpeed( RTBMinSpeed, RTBMaxSpeed )
|
||||
self:F( { RTBMinSpeed, RTBMaxSpeed } )
|
||||
|
||||
self.RTBMinSpeed = RTBMinSpeed
|
||||
self.RTBMaxSpeed = RTBMaxSpeed
|
||||
end
|
||||
|
||||
|
||||
--- Sets the floor and ceiling altitude of the patrol.
|
||||
-- @param #AI_A2A self
|
||||
-- @param #AI_AIR self
|
||||
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
|
||||
-- @return #AI_A2A self
|
||||
function AI_A2A:SetAltitude( PatrolFloorAltitude, PatrolCeilingAltitude )
|
||||
-- @return #AI_AIR self
|
||||
function AI_AIR:SetAltitude( PatrolFloorAltitude, PatrolCeilingAltitude )
|
||||
self:F2( { PatrolFloorAltitude, PatrolCeilingAltitude } )
|
||||
|
||||
self.PatrolFloorAltitude = PatrolFloorAltitude
|
||||
@@ -317,20 +329,20 @@ end
|
||||
|
||||
|
||||
--- Sets the home airbase.
|
||||
-- @param #AI_A2A self
|
||||
-- @param #AI_AIR self
|
||||
-- @param Wrapper.Airbase#AIRBASE HomeAirbase
|
||||
-- @return #AI_A2A self
|
||||
function AI_A2A:SetHomeAirbase( HomeAirbase )
|
||||
-- @return #AI_AIR self
|
||||
function AI_AIR:SetHomeAirbase( HomeAirbase )
|
||||
self:F2( { HomeAirbase } )
|
||||
|
||||
self.HomeAirbase = HomeAirbase
|
||||
end
|
||||
|
||||
--- Sets to refuel at the given tanker.
|
||||
-- @param #AI_A2A self
|
||||
-- @param #AI_AIR 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 )
|
||||
-- @return #AI_AIR self
|
||||
function AI_AIR:SetTanker( TankerName )
|
||||
self:F2( { TankerName } )
|
||||
|
||||
self.TankerName = TankerName
|
||||
@@ -338,19 +350,19 @@ 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 #AI_AIR self
|
||||
-- @param #number DisengageRadius The disengage range.
|
||||
-- @return #AI_A2A self
|
||||
function AI_A2A:SetDisengageRadius( DisengageRadius )
|
||||
-- @return #AI_AIR self
|
||||
function AI_AIR: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()
|
||||
-- @param #AI_AIR self
|
||||
-- @return #AI_AIR self
|
||||
function AI_AIR:SetStatusOff()
|
||||
self:F2()
|
||||
|
||||
self.CheckStatus = false
|
||||
@@ -359,16 +371,16 @@ 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.
|
||||
-- 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_AIR.
|
||||
-- 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 )
|
||||
-- @param #AI_AIR self
|
||||
-- @param #number FuelThresholdPercentage The treshold in percentage (between 0 and 1) when the AIControllable is considered to get out of fuel.
|
||||
-- @param #number OutOfFuelOrbitTime The amount of seconds the out of fuel AIControllable will orbit before returning to the base.
|
||||
-- @return #AI_AIR self
|
||||
function AI_AIR:SetFuelThreshold( FuelThresholdPercentage, OutOfFuelOrbitTime )
|
||||
|
||||
self.PatrolFuelThresholdPercentage = PatrolFuelThresholdPercentage
|
||||
self.PatrolOutOfFuelOrbitTime = PatrolOutOfFuelOrbitTime
|
||||
self.FuelThresholdPercentage = FuelThresholdPercentage
|
||||
self.OutOfFuelOrbitTime = OutOfFuelOrbitTime
|
||||
|
||||
self.Controllable:OptionRTBBingoFuel( false )
|
||||
|
||||
@@ -381,10 +393,10 @@ end
|
||||
-- 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 #AI_AIR 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 )
|
||||
-- @return #AI_AIR self
|
||||
function AI_AIR:SetDamageThreshold( PatrolDamageThreshold )
|
||||
|
||||
self.PatrolManageDamage = true
|
||||
self.PatrolDamageThreshold = PatrolDamageThreshold
|
||||
@@ -392,14 +404,16 @@ function AI_A2A:SetDamageThreshold( 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 #AI_AIR self
|
||||
-- @return #AI_AIR 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 )
|
||||
function AI_AIR:onafterStart( Controllable, From, Event, To )
|
||||
|
||||
self:__Status( 10 ) -- Check status status every 30 seconds.
|
||||
|
||||
@@ -411,16 +425,27 @@ function AI_A2A:onafterStart( Controllable, From, Event, To )
|
||||
Controllable:OptionROTVertical()
|
||||
end
|
||||
|
||||
--- Coordinates the approriate returning action.
|
||||
-- @param #AI_AIR self
|
||||
-- @return #AI_AIR 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_AIR:onafterReturn( Controllable, From, Event, To )
|
||||
|
||||
self:__RTB( self.TaskDelay )
|
||||
|
||||
end
|
||||
|
||||
--- @param #AI_A2A self
|
||||
function AI_A2A:onbeforeStatus()
|
||||
--- @param #AI_AIR self
|
||||
function AI_AIR:onbeforeStatus()
|
||||
|
||||
return self.CheckStatus
|
||||
end
|
||||
|
||||
--- @param #AI_A2A self
|
||||
function AI_A2A:onafterStatus()
|
||||
--- @param #AI_AIR self
|
||||
function AI_AIR:onafterStatus()
|
||||
|
||||
if self.Controllable and self.Controllable:IsAlive() then
|
||||
|
||||
@@ -430,36 +455,40 @@ function AI_A2A:onafterStatus()
|
||||
|
||||
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:I( 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
|
||||
|
||||
-- I think this code is not requirement anymore after release 2.5.
|
||||
-- if self:Is( "Fuel" ) or self:Is( "Damaged" ) or self:Is( "LostControl" ) then
|
||||
-- if DistanceFromHomeBase < 5000 then
|
||||
-- self:E( self.Controllable:GetName() .. " is near the home base, RTB!" )
|
||||
-- self:Home( "Destroy" )
|
||||
-- end
|
||||
-- end
|
||||
|
||||
|
||||
if not self:Is( "Fuel" ) and not self:Is( "Home" ) then
|
||||
if not self:Is( "Fuel" ) and not self:Is( "Home" ) and not self:is( "Refuelling" )then
|
||||
|
||||
local Fuel = self.Controllable:GetFuelMin()
|
||||
self:F({Fuel=Fuel, PatrolFuelThresholdPercentage=self.PatrolFuelThresholdPercentage})
|
||||
if Fuel < self.PatrolFuelThresholdPercentage then
|
||||
|
||||
-- If the fuel in the controllable is below the treshold percentage,
|
||||
-- then send for refuel in case of a tanker, otherwise RTB.
|
||||
if Fuel < self.FuelThresholdPercentage then
|
||||
|
||||
if self.TankerName then
|
||||
self:E( self.Controllable:GetName() .. " is out of fuel: " .. Fuel .. " ... Refuelling at Tanker!" )
|
||||
self:I( 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!" )
|
||||
self:I( 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 ) )
|
||||
local TimedOrbitTask = OldAIControllable:TaskControlled( OrbitTask, OldAIControllable:TaskCondition(nil,nil,nil,nil,self.OutOfFuelOrbitTime,nil ) )
|
||||
OldAIControllable:SetTask( TimedOrbitTask, 10 )
|
||||
|
||||
self:Fuel()
|
||||
@@ -468,28 +497,39 @@ function AI_A2A:onafterStatus()
|
||||
else
|
||||
end
|
||||
end
|
||||
|
||||
if self:Is( "Fuel" ) and not self:Is( "Home" ) and not self:is( "Refuelling" ) then
|
||||
RTB = true
|
||||
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 the group is damaged, then RTB.
|
||||
-- Note that a group can consist of more units, so if one unit is damaged of a group, the mission may continue.
|
||||
-- The damaged unit will RTB due to DCS logic, and the others will continue to engage.
|
||||
if ( Damage / InitialLife ) < self.PatrolDamageThreshold then
|
||||
self:E( self.Controllable:GetName() .. " is damaged: " .. Damage .. " ... RTB!" )
|
||||
self:I( self.Controllable:GetName() .. " is damaged: " .. Damage .. " ... RTB!" )
|
||||
self:Damaged()
|
||||
RTB = true
|
||||
self:SetStatusOff()
|
||||
end
|
||||
|
||||
|
||||
-- Check if planes went RTB and are out of control.
|
||||
-- We only check if planes are out of control, when they are in duty.
|
||||
if self.Controllable:HasTask() == false then
|
||||
if not self:Is( "Started" ) and
|
||||
not self:Is( "Stopped" ) and
|
||||
not self:Is( "Fuel" ) and
|
||||
not self:Is( "Damaged" ) and
|
||||
not self:Is( "Home" ) then
|
||||
if self.IdleCount >= 2 then
|
||||
if self.IdleCount >= 10 then
|
||||
if Damage ~= InitialLife then
|
||||
self:Damaged()
|
||||
else
|
||||
self:E( self.Controllable:GetName() .. " control lost! " )
|
||||
self:I( self.Controllable:GetName() .. " control lost! " )
|
||||
|
||||
self:LostControl()
|
||||
end
|
||||
else
|
||||
@@ -501,31 +541,34 @@ function AI_A2A:onafterStatus()
|
||||
end
|
||||
|
||||
if RTB == true then
|
||||
self:__RTB( 0.5 )
|
||||
self:__RTB( self.TaskDelay )
|
||||
end
|
||||
|
||||
if not self:Is("Home") then
|
||||
self:__Status( 10 )
|
||||
end
|
||||
|
||||
self:__Status( 10 )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- @param Wrapper.Group#GROUP AIGroup
|
||||
function AI_A2A.RTBRoute( AIGroup, Fsm )
|
||||
function AI_AIR.RTBRoute( AIGroup, Fsm )
|
||||
|
||||
AIGroup:F( { "AI_A2A.RTBRoute:", AIGroup:GetName() } )
|
||||
AIGroup:F( { "AI_AIR.RTBRoute:", AIGroup:GetName() } )
|
||||
|
||||
if AIGroup:IsAlive() then
|
||||
Fsm:__RTB( 0.5 )
|
||||
Fsm:RTB()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- @param Wrapper.Group#GROUP AIGroup
|
||||
function AI_A2A.RTBHold( AIGroup, Fsm )
|
||||
function AI_AIR.RTBHold( AIGroup, Fsm )
|
||||
|
||||
AIGroup:F( { "AI_A2A.RTBHold:", AIGroup:GetName() } )
|
||||
AIGroup:F( { "AI_AIR.RTBHold:", AIGroup:GetName() } )
|
||||
if AIGroup:IsAlive() then
|
||||
Fsm:__RTB( 0.5 )
|
||||
Fsm:__RTB( Fsm.TaskDelay )
|
||||
Fsm:Return()
|
||||
local Task = AIGroup:TaskOrbitCircle( 4000, 400 )
|
||||
AIGroup:SetTask( Task )
|
||||
@@ -534,74 +577,92 @@ function AI_A2A.RTBHold( AIGroup, Fsm )
|
||||
end
|
||||
|
||||
|
||||
--- @param #AI_A2A self
|
||||
--- @param #AI_AIR self
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
function AI_A2A:onafterRTB( AIGroup, From, Event, To )
|
||||
function AI_AIR: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:I( "Group " .. AIGroup:GetName() .. " ... RTB! ( " .. self:GetState() .. " )" )
|
||||
|
||||
self:ClearTargetDistance()
|
||||
AIGroup:ClearTasks()
|
||||
--AIGroup:ClearTasks()
|
||||
|
||||
local EngageRoute = {}
|
||||
|
||||
--- Calculate the target route point.
|
||||
|
||||
local CurrentCoord = AIGroup:GetCoordinate()
|
||||
local FromCoord = 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 )
|
||||
if not self.RTBMinSpeed and not self.RTBMaxSpeed then
|
||||
local RTBSpeedMax = AIGroup:GetSpeedMax()
|
||||
self:SetRTBSpeed( RTBSpeedMax * 0.25, RTBSpeedMax * 0.25 )
|
||||
end
|
||||
|
||||
local ToAirbaseCoord = CurrentCoord:Translate( 5000, ToAirbaseAngle )
|
||||
local RTBSpeed = math.random( self.RTBMinSpeed, self.RTBMaxSpeed )
|
||||
local ToAirbaseAngle = FromCoord:GetAngleDegrees( FromCoord:GetDirectionVec3( ToTargetCoord ) )
|
||||
|
||||
local Distance = FromCoord:Get2DDistance( ToTargetCoord )
|
||||
|
||||
local ToAirbaseCoord = FromCoord:Translate( 5000, ToAirbaseAngle )
|
||||
if Distance < 5000 then
|
||||
self:E( "RTB and near the airbase!" )
|
||||
self:I( "RTB and near the airbase!" )
|
||||
self:Home()
|
||||
return
|
||||
end
|
||||
|
||||
if not AIGroup:InAir() == true then
|
||||
self:I( "Not anymore in the air, considered Home." )
|
||||
self:Home()
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
--- Create a route point of type air.
|
||||
local FromRTBRoutePoint = FromCoord:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
RTBSpeed,
|
||||
true
|
||||
)
|
||||
|
||||
--- Create a route point of type air.
|
||||
local ToRTBRoutePoint = ToAirbaseCoord:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
ToTargetSpeed,
|
||||
RTBSpeed,
|
||||
true
|
||||
)
|
||||
|
||||
self:F( { Angle = ToAirbaseAngle, ToTargetSpeed = ToTargetSpeed } )
|
||||
self:T2( { self.MinSpeed, self.MaxSpeed, ToTargetSpeed } )
|
||||
|
||||
EngageRoute[#EngageRoute+1] = ToRTBRoutePoint
|
||||
EngageRoute[#EngageRoute+1] = FromRTBRoutePoint
|
||||
EngageRoute[#EngageRoute+1] = ToRTBRoutePoint
|
||||
|
||||
local Tasks = {}
|
||||
Tasks[#Tasks+1] = AIGroup:TaskFunction( "AI_AIR.RTBRoute", self )
|
||||
|
||||
EngageRoute[#EngageRoute].task = AIGroup:TaskCombo( Tasks )
|
||||
|
||||
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 )
|
||||
AIGroup:Route( EngageRoute, self.TaskDelay )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- @param #AI_A2A self
|
||||
--- @param #AI_AIR self
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
function AI_A2A:onafterHome( AIGroup, From, Event, To )
|
||||
function AI_AIR:onafterHome( AIGroup, From, Event, To )
|
||||
self:F( { AIGroup, From, Event, To } )
|
||||
|
||||
self:E( "Group " .. self.Controllable:GetName() .. " ... Home! ( " .. self:GetState() .. " )" )
|
||||
self:I( "Group " .. self.Controllable:GetName() .. " ... Home! ( " .. self:GetState() .. " )" )
|
||||
|
||||
if AIGroup and AIGroup:IsAlive() then
|
||||
end
|
||||
@@ -610,22 +671,22 @@ end
|
||||
|
||||
|
||||
|
||||
--- @param #AI_A2A self
|
||||
--- @param #AI_AIR self
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
function AI_A2A:onafterHold( AIGroup, From, Event, To, HoldTime )
|
||||
function AI_AIR:onafterHold( AIGroup, From, Event, To, HoldTime )
|
||||
self:F( { AIGroup, From, Event, To } )
|
||||
|
||||
self:E( "Group " .. self.Controllable:GetName() .. " ... Holding! ( " .. self:GetState() .. " )" )
|
||||
self:I( "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 RTBTask = AIGroup:TaskFunction( "AI_AIR.RTBHold", self )
|
||||
|
||||
local OrbitHoldTask = AIGroup:TaskOrbitCircle( 4000, self.PatrolMinSpeed )
|
||||
|
||||
--AIGroup:SetState( AIGroup, "AI_A2A", self )
|
||||
--AIGroup:SetState( AIGroup, "AI_AIR", self )
|
||||
|
||||
AIGroup:SetTask( AIGroup:TaskCombo( { TimedOrbitTask, RTBTask, OrbitHoldTask } ), 1 )
|
||||
end
|
||||
@@ -633,98 +694,112 @@ function AI_A2A:onafterHold( AIGroup, From, Event, To, HoldTime )
|
||||
end
|
||||
|
||||
--- @param Wrapper.Group#GROUP AIGroup
|
||||
function AI_A2A.Resume( AIGroup, Fsm )
|
||||
function AI_AIR.Resume( AIGroup, Fsm )
|
||||
|
||||
AIGroup:F( { "AI_A2A.Resume:", AIGroup:GetName() } )
|
||||
AIGroup:I( { "AI_AIR.Resume:", AIGroup:GetName() } )
|
||||
if AIGroup:IsAlive() then
|
||||
Fsm:__RTB( 0.5 )
|
||||
Fsm:__RTB( Fsm.TaskDelay )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- @param #AI_A2A self
|
||||
--- @param #AI_AIR self
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
function AI_A2A:onafterRefuel( AIGroup, From, Event, To )
|
||||
function AI_AIR: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
|
||||
|
||||
-- Get tanker group.
|
||||
local Tanker = GROUP:FindByName( self.TankerName )
|
||||
if Tanker:IsAlive() and Tanker:IsAirPlane() then
|
||||
|
||||
if Tanker and Tanker:IsAlive() and Tanker:IsAirPlane() then
|
||||
|
||||
self:I( "Group " .. self.Controllable:GetName() .. " ... Refuelling! State=" .. self:GetState() .. ", Refuelling tanker " .. self.TankerName )
|
||||
|
||||
local RefuelRoute = {}
|
||||
|
||||
--- Calculate the target route point.
|
||||
|
||||
local CurrentCoord = AIGroup:GetCoordinate()
|
||||
local FromRefuelCoord = 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
|
||||
)
|
||||
local FromRefuelRoutePoint = FromRefuelCoord:WaypointAir(self.PatrolAltType, POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, ToRefuelSpeed, true)
|
||||
|
||||
--- Create a route point of type air. NOT used!
|
||||
local ToRefuelRoutePoint = Tanker:GetCoordinate():WaypointAir(self.PatrolAltType, POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, ToRefuelSpeed, true)
|
||||
|
||||
self:F( { ToRefuelSpeed = ToRefuelSpeed } )
|
||||
|
||||
RefuelRoute[#RefuelRoute+1] = ToRefuelRoutePoint
|
||||
RefuelRoute[#RefuelRoute+1] = FromRefuelRoutePoint
|
||||
RefuelRoute[#RefuelRoute+1] = ToRefuelRoutePoint
|
||||
|
||||
AIGroup:OptionROEHoldFire()
|
||||
AIGroup:OptionROTEvadeFire()
|
||||
|
||||
-- Get Class name for .Resume function
|
||||
local classname=self:GetClassName()
|
||||
|
||||
-- AI_A2A_CAP can call this function but does not have a .Resume function. Try to fix.
|
||||
if classname=="AI_A2A_CAP" then
|
||||
classname="AI_AIR_PATROL"
|
||||
end
|
||||
|
||||
env.info("FF refueling classname="..classname)
|
||||
|
||||
local Tasks = {}
|
||||
Tasks[#Tasks+1] = AIGroup:TaskRefueling()
|
||||
Tasks[#Tasks+1] = AIGroup:TaskFunction( self:GetClassName() .. ".Resume", self )
|
||||
Tasks[#Tasks+1] = AIGroup:TaskFunction( classname .. ".Resume", self )
|
||||
RefuelRoute[#RefuelRoute].task = AIGroup:TaskCombo( Tasks )
|
||||
|
||||
AIGroup:Route( RefuelRoute, 0.5 )
|
||||
AIGroup:Route( RefuelRoute, self.TaskDelay )
|
||||
|
||||
else
|
||||
|
||||
-- No tanker defined ==> RTB!
|
||||
self:RTB()
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- @param #AI_A2A self
|
||||
function AI_A2A:onafterDead()
|
||||
--- @param #AI_AIR self
|
||||
function AI_AIR:onafterDead()
|
||||
self:SetStatusOff()
|
||||
end
|
||||
|
||||
|
||||
--- @param #AI_A2A self
|
||||
--- @param #AI_AIR self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function AI_A2A:OnCrash( EventData )
|
||||
function AI_AIR: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 )
|
||||
self:__Crash( self.TaskDelay, EventData )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- @param #AI_A2A self
|
||||
--- @param #AI_AIR self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function AI_A2A:OnEjection( EventData )
|
||||
function AI_AIR:OnEjection( EventData )
|
||||
|
||||
if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then
|
||||
self:__Eject( 1, EventData )
|
||||
self:__Eject( self.TaskDelay, EventData )
|
||||
end
|
||||
end
|
||||
|
||||
--- @param #AI_A2A self
|
||||
--- @param #AI_AIR self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function AI_A2A:OnPilotDead( EventData )
|
||||
function AI_AIR:OnPilotDead( EventData )
|
||||
|
||||
if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then
|
||||
self:__PilotDead( 1, EventData )
|
||||
self:__PilotDead( self.TaskDelay, EventData )
|
||||
end
|
||||
end
|
||||
3248
Moose Development/Moose/AI/AI_Air_Dispatcher.lua
Normal file
3248
Moose Development/Moose/AI/AI_Air_Dispatcher.lua
Normal file
File diff suppressed because it is too large
Load Diff
597
Moose Development/Moose/AI/AI_Air_Engage.lua
Normal file
597
Moose Development/Moose/AI/AI_Air_Engage.lua
Normal file
@@ -0,0 +1,597 @@
|
||||
--- **AI** -- Models the process of air to ground engagement for airplanes and helicopters.
|
||||
--
|
||||
-- This is a class used in the @{AI_A2G_Dispatcher}.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_Air_Engage
|
||||
-- @image AI_Air_To_Ground_Engage.JPG
|
||||
|
||||
|
||||
|
||||
--- @type AI_AIR_ENGAGE
|
||||
-- @extends AI.AI_AIR#AI_AIR
|
||||
|
||||
|
||||
--- Implements the core functions to intercept intruders. Use the Engage trigger to intercept intruders.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- The AI_AIR_ENGAGE is assigned a @{Wrapper.Group} and this must be done before the AI_AIR_ENGAGE 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_AIR_ENGAGE constructor
|
||||
--
|
||||
-- * @{#AI_AIR_ENGAGE.New}(): Creates a new AI_AIR_ENGAGE object.
|
||||
--
|
||||
-- ## 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.AI_GCI#AI_AIR_ENGAGE.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.AI_Cap#AI_AIR_ENGAGE.SetEngageZone}() to define that Zone.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #AI_AIR_ENGAGE
|
||||
AI_AIR_ENGAGE = {
|
||||
ClassName = "AI_AIR_ENGAGE",
|
||||
}
|
||||
|
||||
|
||||
|
||||
--- Creates a new AI_AIR_ENGAGE object
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param AI.AI_Air#AI_AIR AI_Air The AI_AIR FSM.
|
||||
-- @param Wrapper.Group#GROUP AIGroup The AI group.
|
||||
-- @param DCS#Speed EngageMinSpeed (optional, default = 50% of max speed) The minimum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Speed EngageMaxSpeed (optional, default = 75% of max speed) The maximum speed of the @{Wrapper.Group} in km/h when engaging a target.
|
||||
-- @param DCS#Altitude EngageFloorAltitude (optional, default = 1000m ) The lowest altitude in meters where to execute the engagement.
|
||||
-- @param DCS#Altitude EngageCeilingAltitude (optional, default = 1500m ) The highest altitude in meters where to execute the engagement.
|
||||
-- @param DCS#AltitudeType EngageAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to "RADIO".
|
||||
-- @return #AI_AIR_ENGAGE
|
||||
function AI_AIR_ENGAGE:New( AI_Air, AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloorAltitude, EngageCeilingAltitude, EngageAltType )
|
||||
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, AI_Air ) -- #AI_AIR_ENGAGE
|
||||
|
||||
self.Accomplished = false
|
||||
self.Engaging = false
|
||||
|
||||
local SpeedMax = AIGroup:GetSpeedMax()
|
||||
|
||||
self.EngageMinSpeed = EngageMinSpeed or SpeedMax * 0.5
|
||||
self.EngageMaxSpeed = EngageMaxSpeed or SpeedMax * 0.75
|
||||
self.EngageFloorAltitude = EngageFloorAltitude or 1000
|
||||
self.EngageCeilingAltitude = EngageCeilingAltitude or 1500
|
||||
self.EngageAltType = EngageAltType or "RADIO"
|
||||
|
||||
self:AddTransition( { "Started", "Engaging", "Returning", "Airborne", "Patrolling" }, "EngageRoute", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR_ENGAGE.
|
||||
|
||||
--- OnBefore Transition Handler for Event EngageRoute.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] OnBeforeEngageRoute
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup 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 EngageRoute.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] OnAfterEngageRoute
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup 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 EngageRoute.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] EngageRoute
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
|
||||
--- Asynchronous Event Trigger for Event EngageRoute.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] __EngageRoute
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
--- OnLeave Transition Handler for State Engaging.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] OnLeaveEngaging
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup 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_AIR_ENGAGE] OnEnterEngaging
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup 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( { "Started", "Engaging", "Returning", "Airborne", "Patrolling" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR_ENGAGE.
|
||||
|
||||
--- OnBefore Transition Handler for Event Engage.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] OnBeforeEngage
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup 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_AIR_ENGAGE] OnAfterEngage
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup 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_AIR_ENGAGE] Engage
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
|
||||
--- Asynchronous Event Trigger for Event Engage.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] __Engage
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
--- OnLeave Transition Handler for State Engaging.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] OnLeaveEngaging
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup 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_AIR_ENGAGE] OnEnterEngaging
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup 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_AIR_ENGAGE.
|
||||
|
||||
--- OnBefore Transition Handler for Event Fired.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] OnBeforeFired
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup 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_AIR_ENGAGE] OnAfterFired
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup 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_AIR_ENGAGE] Fired
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
|
||||
--- Asynchronous Event Trigger for Event Fired.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] __Fired
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
self:AddTransition( "*", "Destroy", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR_ENGAGE.
|
||||
|
||||
--- OnBefore Transition Handler for Event Destroy.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] OnBeforeDestroy
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup 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_AIR_ENGAGE] OnAfterDestroy
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup 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_AIR_ENGAGE] Destroy
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
|
||||
--- Asynchronous Event Trigger for Event Destroy.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] __Destroy
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
|
||||
self:AddTransition( "Engaging", "Abort", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR_ENGAGE.
|
||||
|
||||
--- OnBefore Transition Handler for Event Abort.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] OnBeforeAbort
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup 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_AIR_ENGAGE] OnAfterAbort
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup 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_AIR_ENGAGE] Abort
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
|
||||
--- Asynchronous Event Trigger for Event Abort.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] __Abort
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
self:AddTransition( "Engaging", "Accomplish", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR_ENGAGE.
|
||||
|
||||
--- OnBefore Transition Handler for Event Accomplish.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] OnBeforeAccomplish
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup 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_AIR_ENGAGE] OnAfterAccomplish
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup 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_AIR_ENGAGE] Accomplish
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
|
||||
--- Asynchronous Event Trigger for Event Accomplish.
|
||||
-- @function [parent=#AI_AIR_ENGAGE] __Accomplish
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
self:AddTransition( { "Patrolling", "Engaging" }, "Refuel", "Refuelling" )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- onafter event handler for Start event.
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The AI group managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_AIR_ENGAGE:onafterStart( AIGroup, From, Event, To )
|
||||
|
||||
self:GetParent( self, AI_AIR_ENGAGE ).onafterStart( self, AIGroup, From, Event, To )
|
||||
|
||||
AIGroup:HandleEvent( EVENTS.Takeoff, nil, self )
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- onafter event handler for Engage event.
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The AI Group managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_AIR_ENGAGE:onafterEngage( AIGroup, From, Event, To )
|
||||
-- TODO: This function is overwritten below!
|
||||
self:HandleEvent( EVENTS.Dead )
|
||||
end
|
||||
|
||||
-- todo: need to fix this global function
|
||||
|
||||
|
||||
--- onbefore event handler for Engage event.
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup 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_AIR_ENGAGE:onbeforeEngage( AIGroup, From, Event, To )
|
||||
if self.Accomplished == true then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- onafter event handler for Abort event.
|
||||
-- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The AI Group managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_AIR_ENGAGE:onafterAbort( AIGroup, From, Event, To )
|
||||
AIGroup:ClearTasks()
|
||||
self:Return()
|
||||
end
|
||||
|
||||
|
||||
--- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup 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_AIR_ENGAGE:onafterAccomplish( AIGroup, From, Event, To )
|
||||
self.Accomplished = true
|
||||
--self:SetDetectionOff()
|
||||
end
|
||||
|
||||
--- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup 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_AIR_ENGAGE:onafterDestroy( AIGroup, From, Event, To, EventData )
|
||||
|
||||
if EventData.IniUnit then
|
||||
self.AttackUnits[EventData.IniUnit] = nil
|
||||
end
|
||||
end
|
||||
|
||||
--- @param #AI_AIR_ENGAGE self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function AI_AIR_ENGAGE:OnEventDead( EventData )
|
||||
self:F( { "EventDead", EventData } )
|
||||
|
||||
if EventData.IniDCSUnit then
|
||||
if self.AttackUnits and self.AttackUnits[EventData.IniUnit] then
|
||||
self:__Destroy( self.TaskDelay, EventData )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- @param Wrapper.Group#GROUP AIControllable
|
||||
function AI_AIR_ENGAGE.___EngageRoute( AIGroup, Fsm, AttackSetUnit )
|
||||
Fsm:I(string.format("AI_AIR_ENGAGE.___EngageRoute: %s", tostring(AIGroup:GetName())))
|
||||
|
||||
if AIGroup and AIGroup:IsAlive() then
|
||||
Fsm:__EngageRoute( Fsm.TaskDelay or 0.1, AttackSetUnit )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP DefenderGroup 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_AIR_ENGAGE:onafterEngageRoute( DefenderGroup, From, Event, To, AttackSetUnit )
|
||||
self:I( { DefenderGroup, From, Event, To, AttackSetUnit } )
|
||||
|
||||
local DefenderGroupName = DefenderGroup:GetName()
|
||||
|
||||
self.AttackSetUnit = AttackSetUnit -- Kept in memory in case of resume from refuel in air!
|
||||
|
||||
local AttackCount = AttackSetUnit:Count()
|
||||
|
||||
if AttackCount > 0 then
|
||||
|
||||
if DefenderGroup:IsAlive() then
|
||||
|
||||
local EngageAltitude = math.random( self.EngageFloorAltitude, self.EngageCeilingAltitude )
|
||||
local EngageSpeed = math.random( self.EngageMinSpeed, self.EngageMaxSpeed )
|
||||
|
||||
-- Determine the distance to the target.
|
||||
-- If it is less than 10km, then attack without a route.
|
||||
-- Otherwise perform a route attack.
|
||||
|
||||
local DefenderCoord = DefenderGroup:GetPointVec3()
|
||||
DefenderCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude.
|
||||
|
||||
local TargetCoord = AttackSetUnit:GetFirst():GetPointVec3()
|
||||
TargetCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude.
|
||||
|
||||
local TargetDistance = DefenderCoord:Get2DDistance( TargetCoord )
|
||||
local EngageDistance = ( DefenderGroup:IsHelicopter() and 5000 ) or ( DefenderGroup:IsAirPlane() and 10000 )
|
||||
|
||||
-- TODO: A factor of * 3 is way too close. This causes the AI not to engange until merged sometimes!
|
||||
if TargetDistance <= EngageDistance * 9 then
|
||||
|
||||
self:I(string.format("AI_AIR_ENGAGE onafterEngageRoute ==> __Engage - target distance = %.1f km", TargetDistance/1000))
|
||||
self:__Engage( 0.1, AttackSetUnit )
|
||||
|
||||
else
|
||||
|
||||
self:I(string.format("FF AI_AIR_ENGAGE onafterEngageRoute ==> Routing - target distance = %.1f km", TargetDistance/1000))
|
||||
|
||||
local EngageRoute = {}
|
||||
local AttackTasks = {}
|
||||
|
||||
--- Calculate the target route point.
|
||||
|
||||
local FromWP = DefenderCoord:WaypointAir(self.PatrolAltType or "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, EngageSpeed, true)
|
||||
|
||||
EngageRoute[#EngageRoute+1] = FromWP
|
||||
|
||||
self:SetTargetDistance( TargetCoord ) -- For RTB status check
|
||||
|
||||
local FromEngageAngle = DefenderCoord:GetAngleDegrees( DefenderCoord:GetDirectionVec3( TargetCoord ) )
|
||||
local ToCoord=DefenderCoord:Translate( EngageDistance, FromEngageAngle, true )
|
||||
|
||||
local ToWP = ToCoord:WaypointAir(self.PatrolAltType or "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, EngageSpeed, true)
|
||||
|
||||
EngageRoute[#EngageRoute+1] = ToWP
|
||||
|
||||
AttackTasks[#AttackTasks+1] = DefenderGroup:TaskFunction( "AI_AIR_ENGAGE.___EngageRoute", self, AttackSetUnit )
|
||||
EngageRoute[#EngageRoute].task = DefenderGroup:TaskCombo( AttackTasks )
|
||||
|
||||
DefenderGroup:OptionROEReturnFire()
|
||||
DefenderGroup:OptionROTEvadeFire()
|
||||
|
||||
DefenderGroup:Route( EngageRoute, self.TaskDelay or 0.1 )
|
||||
end
|
||||
|
||||
end
|
||||
else
|
||||
-- TODO: This will make an A2A Dispatcher CAP flight to return rather than going back to patrolling!
|
||||
self:I( DefenderGroupName .. ": No targets found -> Going RTB")
|
||||
self:Return()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- @param Wrapper.Group#GROUP AIControllable
|
||||
function AI_AIR_ENGAGE.___Engage( AIGroup, Fsm, AttackSetUnit )
|
||||
|
||||
Fsm:I(string.format("AI_AIR_ENGAGE.___Engage: %s", tostring(AIGroup:GetName())))
|
||||
|
||||
if AIGroup and AIGroup:IsAlive() then
|
||||
local delay=Fsm.TaskDelay or 0.1
|
||||
Fsm:__Engage(delay, AttackSetUnit)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- @param #AI_AIR_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP DefenderGroup 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_AIR_ENGAGE:onafterEngage( DefenderGroup, From, Event, To, AttackSetUnit )
|
||||
self:F( { DefenderGroup, From, Event, To, AttackSetUnit} )
|
||||
|
||||
local DefenderGroupName = DefenderGroup:GetName()
|
||||
|
||||
self.AttackSetUnit = AttackSetUnit -- Kept in memory in case of resume from refuel in air!
|
||||
|
||||
local AttackCount = AttackSetUnit:Count()
|
||||
self:I({AttackCount = AttackCount})
|
||||
|
||||
if AttackCount > 0 then
|
||||
|
||||
if DefenderGroup and DefenderGroup:IsAlive() then
|
||||
|
||||
local EngageAltitude = math.random( self.EngageFloorAltitude or 500, self.EngageCeilingAltitude or 1000 )
|
||||
local EngageSpeed = math.random( self.EngageMinSpeed, self.EngageMaxSpeed )
|
||||
|
||||
local DefenderCoord = DefenderGroup:GetPointVec3()
|
||||
DefenderCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude.
|
||||
|
||||
local TargetCoord = AttackSetUnit:GetFirst():GetPointVec3()
|
||||
TargetCoord:SetY( EngageAltitude ) -- Ground targets don't have an altitude.
|
||||
|
||||
local TargetDistance = DefenderCoord:Get2DDistance( TargetCoord )
|
||||
|
||||
local EngageDistance = ( DefenderGroup:IsHelicopter() and 5000 ) or ( DefenderGroup:IsAirPlane() and 10000 )
|
||||
|
||||
local EngageRoute = {}
|
||||
local AttackTasks = {}
|
||||
|
||||
local FromWP = DefenderCoord:WaypointAir(self.EngageAltType or "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, EngageSpeed, true)
|
||||
EngageRoute[#EngageRoute+1] = FromWP
|
||||
|
||||
self:SetTargetDistance( TargetCoord ) -- For RTB status check
|
||||
|
||||
local FromEngageAngle = DefenderCoord:GetAngleDegrees( DefenderCoord:GetDirectionVec3( TargetCoord ) )
|
||||
local ToCoord=DefenderCoord:Translate( EngageDistance, FromEngageAngle, true )
|
||||
|
||||
local ToWP = ToCoord:WaypointAir(self.EngageAltType or "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, EngageSpeed, true)
|
||||
EngageRoute[#EngageRoute+1] = ToWP
|
||||
|
||||
-- TODO: A factor of * 3 this way too low. This causes the AI NOT to engage until very close or even merged sometimes. Some A2A missiles have a much longer range! Needs more frequent updates of the task!
|
||||
if TargetDistance <= EngageDistance * 9 then
|
||||
|
||||
local AttackUnitTasks = self:CreateAttackUnitTasks( AttackSetUnit, DefenderGroup, EngageAltitude ) -- Polymorphic
|
||||
|
||||
if #AttackUnitTasks == 0 then
|
||||
self:I( DefenderGroupName .. ": No valid targets found -> Going RTB")
|
||||
self:Return()
|
||||
return
|
||||
else
|
||||
local text=string.format("%s: Engaging targets at distance %.2f NM", DefenderGroupName, UTILS.MetersToNM(TargetDistance))
|
||||
self:I(text)
|
||||
DefenderGroup:OptionROEOpenFire()
|
||||
DefenderGroup:OptionROTEvadeFire()
|
||||
DefenderGroup:OptionKeepWeaponsOnThreat()
|
||||
|
||||
AttackTasks[#AttackTasks+1] = DefenderGroup:TaskCombo( AttackUnitTasks )
|
||||
end
|
||||
end
|
||||
|
||||
AttackTasks[#AttackTasks+1] = DefenderGroup:TaskFunction( "AI_AIR_ENGAGE.___Engage", self, AttackSetUnit )
|
||||
EngageRoute[#EngageRoute].task = DefenderGroup:TaskCombo( AttackTasks )
|
||||
|
||||
DefenderGroup:Route( EngageRoute, self.TaskDelay or 0.1 )
|
||||
|
||||
end
|
||||
else
|
||||
-- TODO: This will make an A2A Dispatcher CAP flight to return rather than going back to patrolling!
|
||||
self:I( DefenderGroupName .. ": No targets found -> returning.")
|
||||
self:Return()
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
--- @param Wrapper.Group#GROUP AIEngage
|
||||
function AI_AIR_ENGAGE.Resume( AIEngage, Fsm )
|
||||
|
||||
AIEngage:F( { "Resume:", AIEngage:GetName() } )
|
||||
if AIEngage and AIEngage:IsAlive() then
|
||||
Fsm:__Reset( Fsm.TaskDelay or 0.1 )
|
||||
Fsm:__EngageRoute( Fsm.TaskDelay or 0.2, Fsm.AttackSetUnit )
|
||||
end
|
||||
|
||||
end
|
||||
398
Moose Development/Moose/AI/AI_Air_Patrol.lua
Normal file
398
Moose Development/Moose/AI/AI_Air_Patrol.lua
Normal file
@@ -0,0 +1,398 @@
|
||||
--- **AI** -- Models the process of A2G patrolling and engaging ground targets for airplanes and helicopters.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_Air_Patrol
|
||||
-- @image AI_Air_To_Ground_Patrol.JPG
|
||||
|
||||
--- @type AI_AIR_PATROL
|
||||
-- @extends AI.AI_Air#AI_AIR
|
||||
|
||||
|
||||
--- The AI_AIR_PATROL class implements the core functions to patrol a @{Zone} by an AI @{Wrapper.Group} or @{Wrapper.Group}
|
||||
-- and automatically engage any airborne enemies that are within a certain range or within a certain zone.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- The AI_AIR_PATROL is assigned a @{Wrapper.Group} and this must be done before the AI_AIR_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.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- 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_AIR_PATROL constructor
|
||||
--
|
||||
-- * @{#AI_AIR_PATROL.New}(): Creates a new AI_AIR_PATROL object.
|
||||
--
|
||||
-- ## 2. AI_AIR_PATROL is a FSM
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ### 2.1 AI_AIR_PATROL 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_AIR_PATROL Events
|
||||
--
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process.
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.PatrolRoute}**: Route the AI to a new random 3D point within the Patrol Zone.
|
||||
-- * **@{#AI_AIR_PATROL.Engage}**: Let the AI engage the bogeys.
|
||||
-- * **@{#AI_AIR_PATROL.Abort}**: Aborts the engagement and return patrolling in the patrol zone.
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base.
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets.
|
||||
-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets.
|
||||
-- * **@{#AI_AIR_PATROL.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}.
|
||||
-- * **@{#AI_AIR_PATROL.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.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.AI_CAP#AI_AIR_PATROL.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.AI_Cap#AI_AIR_PATROL.SetEngageZone}() to define that Zone.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #AI_AIR_PATROL
|
||||
AI_AIR_PATROL = {
|
||||
ClassName = "AI_AIR_PATROL",
|
||||
}
|
||||
|
||||
--- Creates a new AI_AIR_PATROL object
|
||||
-- @param #AI_AIR_PATROL self
|
||||
-- @param AI.AI_Air#AI_AIR AI_Air The AI_AIR FSM.
|
||||
-- @param Wrapper.Group#GROUP AIGroup The AI group.
|
||||
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed.
|
||||
-- @param DCS#Altitude PatrolFloorAltitude (optional, default = 1000m ) The lowest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Altitude PatrolCeilingAltitude (optional, default = 1500m ) The highest altitude in meters where to execute the patrol.
|
||||
-- @param DCS#Speed PatrolMinSpeed (optional, default = 50% of max speed) The minimum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#Speed PatrolMaxSpeed (optional, default = 75% of max speed) The maximum speed of the @{Wrapper.Group} in km/h.
|
||||
-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO.
|
||||
-- @return #AI_AIR_PATROL
|
||||
function AI_AIR_PATROL:New( AI_Air, AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
|
||||
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, AI_Air ) -- #AI_AIR_PATROL
|
||||
|
||||
local SpeedMax = AIGroup:GetSpeedMax()
|
||||
|
||||
self.PatrolZone = PatrolZone
|
||||
|
||||
self.PatrolFloorAltitude = PatrolFloorAltitude or 1000
|
||||
self.PatrolCeilingAltitude = PatrolCeilingAltitude or 1500
|
||||
self.PatrolMinSpeed = PatrolMinSpeed or SpeedMax * 0.5
|
||||
self.PatrolMaxSpeed = PatrolMaxSpeed or SpeedMax * 0.75
|
||||
|
||||
-- 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_AIR_PATROL] OnBeforePatrol
|
||||
-- @param #AI_AIR_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_AIR_PATROL] OnAfterPatrol
|
||||
-- @param #AI_AIR_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_AIR_PATROL] Patrol
|
||||
-- @param #AI_AIR_PATROL self
|
||||
|
||||
--- Asynchronous Event Trigger for Event Patrol.
|
||||
-- @function [parent=#AI_AIR_PATROL] __Patrol
|
||||
-- @param #AI_AIR_PATROL self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
--- OnLeave Transition Handler for State Patrolling.
|
||||
-- @function [parent=#AI_AIR_PATROL] OnLeavePatrolling
|
||||
-- @param #AI_AIR_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_AIR_PATROL] OnEnterPatrolling
|
||||
-- @param #AI_AIR_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", "PatrolRoute", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR_PATROL.
|
||||
|
||||
--- OnBefore Transition Handler for Event PatrolRoute.
|
||||
-- @function [parent=#AI_AIR_PATROL] OnBeforePatrolRoute
|
||||
-- @param #AI_AIR_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 PatrolRoute.
|
||||
-- @function [parent=#AI_AIR_PATROL] OnAfterPatrolRoute
|
||||
-- @param #AI_AIR_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 PatrolRoute.
|
||||
-- @function [parent=#AI_AIR_PATROL] PatrolRoute
|
||||
-- @param #AI_AIR_PATROL self
|
||||
|
||||
--- Asynchronous Event Trigger for Event PatrolRoute.
|
||||
-- @function [parent=#AI_AIR_PATROL] __PatrolRoute
|
||||
-- @param #AI_AIR_PATROL self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
|
||||
self:AddTransition( "*", "Reset", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR_PATROL.
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set the Engage Range when the AI will engage with airborne enemies.
|
||||
-- @param #AI_AIR_PATROL self
|
||||
-- @param #number EngageRange The Engage Range.
|
||||
-- @return #AI_AIR_PATROL self
|
||||
function AI_AIR_PATROL:SetEngageRange( EngageRange )
|
||||
self:F2()
|
||||
|
||||
if EngageRange then
|
||||
self.EngageRange = EngageRange
|
||||
else
|
||||
self.EngageRange = nil
|
||||
end
|
||||
end
|
||||
|
||||
--- Set race track parameters. CAP flights will perform race track patterns rather than randomly patrolling the zone.
|
||||
-- @param #AI_AIR_PATROL self
|
||||
-- @param #number LegMin Min Length of the race track leg in meters. Default 10,000 m.
|
||||
-- @param #number LegMax Max length of the race track leg in meters. Default 15,000 m.
|
||||
-- @param #number HeadingMin Min heading of the race track in degrees. Default 0 deg, i.e. from South to North.
|
||||
-- @param #number HeadingMax Max heading of the race track in degrees. Default 180 deg, i.e. from South to North.
|
||||
-- @param #number DurationMin (Optional) Min duration before switching the orbit position. Default is keep same orbit until RTB or engage.
|
||||
-- @param #number DurationMax (Optional) Max duration before switching the orbit position. Default is keep same orbit until RTB or engage.
|
||||
-- @param #table CapCoordinates Table of coordinates of first race track point. Second point is determined by leg length and heading.
|
||||
-- @return #AI_AIR_PATROL self
|
||||
function AI_AIR_PATROL:SetRaceTrackPattern(LegMin, LegMax, HeadingMin, HeadingMax, DurationMin, DurationMax, CapCoordinates)
|
||||
|
||||
self.racetrack=true
|
||||
self.racetracklegmin=LegMin or 10000
|
||||
self.racetracklegmax=LegMax or 15000
|
||||
self.racetrackheadingmin=HeadingMin or 0
|
||||
self.racetrackheadingmax=HeadingMax or 180
|
||||
self.racetrackdurationmin=DurationMin
|
||||
self.racetrackdurationmax=DurationMax
|
||||
|
||||
if self.racetrackdurationmax and not self.racetrackdurationmin then
|
||||
self.racetrackdurationmin=self.racetrackdurationmax
|
||||
end
|
||||
|
||||
self.racetrackcapcoordinates=CapCoordinates
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Defines a new patrol route using the @{Process_PatrolZone} parameters and settings.
|
||||
-- @param #AI_AIR_PATROL self
|
||||
-- @return #AI_AIR_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_AIR_PATROL:onafterPatrol( AIPatrol, From, Event, To )
|
||||
self:F2()
|
||||
|
||||
self:ClearTargetDistance()
|
||||
|
||||
self:__PatrolRoute( self.TaskDelay )
|
||||
|
||||
AIPatrol:OnReSpawn(
|
||||
function( PatrolGroup )
|
||||
self:__Reset( self.TaskDelay )
|
||||
self:__PatrolRoute( self.TaskDelay )
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
--- 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.
|
||||
-- @param Wrapper.Group#GROUP AIPatrol The AI group.
|
||||
-- @param #AI_AIR_PATROL Fsm The FSM.
|
||||
function AI_AIR_PATROL.___PatrolRoute( AIPatrol, Fsm )
|
||||
|
||||
AIPatrol:F( { "AI_AIR_PATROL.___PatrolRoute:", AIPatrol:GetName() } )
|
||||
|
||||
if AIPatrol and AIPatrol:IsAlive() then
|
||||
Fsm:PatrolRoute()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Defines a new patrol route using the @{Process_PatrolZone} parameters and settings.
|
||||
-- @param #AI_AIR_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_AIR_PATROL:onafterPatrolRoute( AIPatrol, From, Event, To )
|
||||
|
||||
self:F2()
|
||||
|
||||
-- When RTB, don't allow anymore the routing.
|
||||
if From == "RTB" then
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
if AIPatrol and AIPatrol:IsAlive() then
|
||||
|
||||
local PatrolRoute = {}
|
||||
|
||||
--- Calculate the target route point.
|
||||
|
||||
local CurrentCoord = AIPatrol:GetCoordinate()
|
||||
|
||||
local altitude= math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude )
|
||||
|
||||
local ToTargetCoord = self.PatrolZone:GetRandomPointVec2()
|
||||
ToTargetCoord:SetAlt( altitude )
|
||||
self:SetTargetDistance( ToTargetCoord ) -- For RTB status check
|
||||
|
||||
local ToTargetSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed )
|
||||
local speedkmh=ToTargetSpeed
|
||||
|
||||
local FromWP = CurrentCoord:WaypointAir(self.PatrolAltType or "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, ToTargetSpeed, true)
|
||||
PatrolRoute[#PatrolRoute+1] = FromWP
|
||||
|
||||
if self.racetrack then
|
||||
|
||||
-- Random heading.
|
||||
local heading = math.random(self.racetrackheadingmin, self.racetrackheadingmax)
|
||||
|
||||
-- Random leg length.
|
||||
local leg=math.random(self.racetracklegmin, self.racetracklegmax)
|
||||
|
||||
-- Random duration if any.
|
||||
local duration = self.racetrackdurationmin
|
||||
if self.racetrackdurationmax then
|
||||
duration=math.random(self.racetrackdurationmin, self.racetrackdurationmax)
|
||||
end
|
||||
|
||||
-- CAP coordinate.
|
||||
local c0=self.PatrolZone:GetRandomCoordinate()
|
||||
if self.racetrackcapcoordinates and #self.racetrackcapcoordinates>0 then
|
||||
c0=self.racetrackcapcoordinates[math.random(#self.racetrackcapcoordinates)]
|
||||
end
|
||||
|
||||
-- Race track points.
|
||||
local c1=c0:SetAltitude(altitude) --Core.Point#COORDINATE
|
||||
local c2=c1:Translate(leg, heading):SetAltitude(altitude)
|
||||
|
||||
self:SetTargetDistance(c0) -- For RTB status check
|
||||
|
||||
-- Debug:
|
||||
self:T(string.format("Patrol zone race track: v=%.1f knots, h=%.1f ft, heading=%03d, leg=%d m, t=%s sec", UTILS.KmphToKnots(speedkmh), UTILS.MetersToFeet(altitude), heading, leg, tostring(duration)))
|
||||
--c1:MarkToAll("Race track c1")
|
||||
--c2:MarkToAll("Race track c2")
|
||||
|
||||
-- Task to orbit.
|
||||
local taskOrbit=AIPatrol:TaskOrbit(c1, altitude, UTILS.KmphToMps(speedkmh), c2)
|
||||
|
||||
-- Task function to redo the patrol at other random position.
|
||||
local taskPatrol=AIPatrol:TaskFunction("AI_AIR_PATROL.___PatrolRoute", self)
|
||||
|
||||
-- Controlled task with task condition.
|
||||
local taskCond=AIPatrol:TaskCondition(nil, nil, nil, nil, duration, nil)
|
||||
local taskCont=AIPatrol:TaskControlled(taskOrbit, taskCond)
|
||||
|
||||
-- Second waypoint
|
||||
PatrolRoute[2]=c1:WaypointAirTurningPoint(self.PatrolAltType, speedkmh, {taskCont, taskPatrol}, "CAP Orbit")
|
||||
|
||||
else
|
||||
|
||||
--- Create a route point of type air.
|
||||
local ToWP = ToTargetCoord:WaypointAir(self.PatrolAltType, POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, ToTargetSpeed, true)
|
||||
PatrolRoute[#PatrolRoute+1] = ToWP
|
||||
|
||||
local Tasks = {}
|
||||
Tasks[#Tasks+1] = AIPatrol:TaskFunction("AI_AIR_PATROL.___PatrolRoute", self)
|
||||
PatrolRoute[#PatrolRoute].task = AIPatrol:TaskCombo( Tasks )
|
||||
|
||||
end
|
||||
|
||||
AIPatrol:OptionROEReturnFire()
|
||||
AIPatrol:OptionROTEvadeFire()
|
||||
|
||||
AIPatrol:Route( PatrolRoute, self.TaskDelay )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- @param Wrapper.Group#GROUP AIPatrol
|
||||
function AI_AIR_PATROL.Resume( AIPatrol, Fsm )
|
||||
|
||||
AIPatrol:F( { "AI_AIR_PATROL.Resume:", AIPatrol:GetName() } )
|
||||
if AIPatrol and AIPatrol:IsAlive() then
|
||||
Fsm:__Reset( Fsm.TaskDelay )
|
||||
Fsm:__PatrolRoute( Fsm.TaskDelay )
|
||||
end
|
||||
|
||||
end
|
||||
289
Moose Development/Moose/AI/AI_Air_Squadron.lua
Normal file
289
Moose Development/Moose/AI/AI_Air_Squadron.lua
Normal file
@@ -0,0 +1,289 @@
|
||||
--- **AI** - Models squadrons for airplanes and helicopters.
|
||||
--
|
||||
-- This is a class used in the @{AI_Air_Dispatcher} and derived dispatcher classes.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_Air_Squadron
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
|
||||
|
||||
--- @type AI_AIR_SQUADRON
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
|
||||
--- Implements the core functions modeling squadrons for airplanes and helicopters.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #AI_AIR_SQUADRON
|
||||
AI_AIR_SQUADRON = {
|
||||
ClassName = "AI_AIR_SQUADRON",
|
||||
}
|
||||
|
||||
|
||||
|
||||
--- Creates a new AI_AIR_SQUADRON object
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @return #AI_AIR_SQUADRON
|
||||
function AI_AIR_SQUADRON:New( SquadronName, AirbaseName, TemplatePrefixes, ResourceCount )
|
||||
|
||||
self:I( { Air_Squadron = { SquadronName, AirbaseName, TemplatePrefixes, ResourceCount } } )
|
||||
|
||||
local AI_Air_Squadron = BASE:New() -- #AI_AIR_SQUADRON
|
||||
|
||||
AI_Air_Squadron.Name = SquadronName
|
||||
AI_Air_Squadron.Airbase = AIRBASE:FindByName( AirbaseName )
|
||||
AI_Air_Squadron.AirbaseName = AI_Air_Squadron.Airbase:GetName()
|
||||
if not AI_Air_Squadron.Airbase then
|
||||
error( "Cannot find airbase with name:" .. AirbaseName )
|
||||
end
|
||||
|
||||
AI_Air_Squadron.Spawn = {}
|
||||
if type( TemplatePrefixes ) == "string" then
|
||||
local SpawnTemplate = TemplatePrefixes
|
||||
self.DefenderSpawns[SpawnTemplate] = self.DefenderSpawns[SpawnTemplate] or SPAWN:New( SpawnTemplate ) -- :InitCleanUp( 180 )
|
||||
AI_Air_Squadron.Spawn[1] = self.DefenderSpawns[SpawnTemplate]
|
||||
else
|
||||
for TemplateID, SpawnTemplate in pairs( TemplatePrefixes ) do
|
||||
self.DefenderSpawns[SpawnTemplate] = self.DefenderSpawns[SpawnTemplate] or SPAWN:New( SpawnTemplate ) -- :InitCleanUp( 180 )
|
||||
AI_Air_Squadron.Spawn[#AI_Air_Squadron.Spawn+1] = self.DefenderSpawns[SpawnTemplate]
|
||||
end
|
||||
end
|
||||
AI_Air_Squadron.ResourceCount = ResourceCount
|
||||
AI_Air_Squadron.TemplatePrefixes = TemplatePrefixes
|
||||
AI_Air_Squadron.Captured = false -- Not captured. This flag will be set to true, when the airbase where the squadron is located, is captured.
|
||||
|
||||
self:SetSquadronLanguage( SquadronName, "EN" ) -- Squadrons speak English by default.
|
||||
|
||||
return AI_Air_Squadron
|
||||
end
|
||||
|
||||
--- Set the Name of the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @param #string Name The Squadron Name.
|
||||
-- @return #AI_AIR_SQUADRON The Squadron.
|
||||
function AI_AIR_SQUADRON:SetName( Name )
|
||||
|
||||
self.Name = Name
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the Name of the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @return #string The Squadron Name.
|
||||
function AI_AIR_SQUADRON:GetName()
|
||||
|
||||
return self.Name
|
||||
end
|
||||
|
||||
--- Set the ResourceCount of the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @param #number ResourceCount The Squadron ResourceCount.
|
||||
-- @return #AI_AIR_SQUADRON The Squadron.
|
||||
function AI_AIR_SQUADRON:SetResourceCount( ResourceCount )
|
||||
|
||||
self.ResourceCount = ResourceCount
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the ResourceCount of the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @return #number The Squadron ResourceCount.
|
||||
function AI_AIR_SQUADRON:GetResourceCount()
|
||||
|
||||
return self.ResourceCount
|
||||
end
|
||||
|
||||
--- Add Resources to the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @param #number Resources The Resources to be added.
|
||||
-- @return #AI_AIR_SQUADRON The Squadron.
|
||||
function AI_AIR_SQUADRON:AddResources( Resources )
|
||||
|
||||
self.ResourceCount = self.ResourceCount + Resources
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Remove Resources to the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @param #number Resources The Resources to be removed.
|
||||
-- @return #AI_AIR_SQUADRON The Squadron.
|
||||
function AI_AIR_SQUADRON:RemoveResources( Resources )
|
||||
|
||||
self.ResourceCount = self.ResourceCount - Resources
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set the Overhead of the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @param #number Overhead The Squadron Overhead.
|
||||
-- @return #AI_AIR_SQUADRON The Squadron.
|
||||
function AI_AIR_SQUADRON:SetOverhead( Overhead )
|
||||
|
||||
self.Overhead = Overhead
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the Overhead of the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @return #number The Squadron Overhead.
|
||||
function AI_AIR_SQUADRON:GetOverhead()
|
||||
|
||||
return self.Overhead
|
||||
end
|
||||
|
||||
--- Set the Grouping of the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @param #number Grouping The Squadron Grouping.
|
||||
-- @return #AI_AIR_SQUADRON The Squadron.
|
||||
function AI_AIR_SQUADRON:SetGrouping( Grouping )
|
||||
|
||||
self.Grouping = Grouping
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the Grouping of the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @return #number The Squadron Grouping.
|
||||
function AI_AIR_SQUADRON:GetGrouping()
|
||||
|
||||
return self.Grouping
|
||||
end
|
||||
|
||||
--- Set the FuelThreshold of the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @param #number FuelThreshold The Squadron FuelThreshold.
|
||||
-- @return #AI_AIR_SQUADRON The Squadron.
|
||||
function AI_AIR_SQUADRON:SetFuelThreshold( FuelThreshold )
|
||||
|
||||
self.FuelThreshold = FuelThreshold
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the FuelThreshold of the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @return #number The Squadron FuelThreshold.
|
||||
function AI_AIR_SQUADRON:GetFuelThreshold()
|
||||
|
||||
return self.FuelThreshold
|
||||
end
|
||||
|
||||
--- Set the EngageProbability of the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @param #number EngageProbability The Squadron EngageProbability.
|
||||
-- @return #AI_AIR_SQUADRON The Squadron.
|
||||
function AI_AIR_SQUADRON:SetEngageProbability( EngageProbability )
|
||||
|
||||
self.EngageProbability = EngageProbability
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the EngageProbability of the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @return #number The Squadron EngageProbability.
|
||||
function AI_AIR_SQUADRON:GetEngageProbability()
|
||||
|
||||
return self.EngageProbability
|
||||
end
|
||||
|
||||
--- Set the Takeoff of the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @param #number Takeoff The Squadron Takeoff.
|
||||
-- @return #AI_AIR_SQUADRON The Squadron.
|
||||
function AI_AIR_SQUADRON:SetTakeoff( Takeoff )
|
||||
|
||||
self.Takeoff = Takeoff
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the Takeoff of the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @return #number The Squadron Takeoff.
|
||||
function AI_AIR_SQUADRON:GetTakeoff()
|
||||
|
||||
return self.Takeoff
|
||||
end
|
||||
|
||||
--- Set the Landing of the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @param #number Landing The Squadron Landing.
|
||||
-- @return #AI_AIR_SQUADRON The Squadron.
|
||||
function AI_AIR_SQUADRON:SetLanding( Landing )
|
||||
|
||||
self.Landing = Landing
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the Landing of the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @return #number The Squadron Landing.
|
||||
function AI_AIR_SQUADRON:GetLanding()
|
||||
|
||||
return self.Landing
|
||||
end
|
||||
|
||||
--- Set the TankerName of the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @param #string TankerName The Squadron Tanker Name.
|
||||
-- @return #AI_AIR_SQUADRON The Squadron.
|
||||
function AI_AIR_SQUADRON:SetTankerName( TankerName )
|
||||
|
||||
self.TankerName = TankerName
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the Tanker Name of the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @return #string The Squadron Tanker Name.
|
||||
function AI_AIR_SQUADRON:GetTankerName()
|
||||
|
||||
return self.TankerName
|
||||
end
|
||||
|
||||
|
||||
--- Set the Radio of the Squadron.
|
||||
-- @param #AI_AIR_SQUADRON self
|
||||
-- @param #number RadioFrequency The frequency of communication.
|
||||
-- @param #number RadioModulation The modulation of communication.
|
||||
-- @param #number RadioPower The power in Watts of communication.
|
||||
-- @param #string Language The language of the radio speech.
|
||||
-- @return #AI_AIR_SQUADRON The Squadron.
|
||||
function AI_AIR_SQUADRON:SetRadio( RadioFrequency, RadioModulation, RadioPower, Language )
|
||||
|
||||
self.RadioFrequency = RadioFrequency
|
||||
self.RadioModulation = RadioModulation or radio.modulation.AM
|
||||
self.RadioPower = RadioPower or 100
|
||||
|
||||
if self.RadioSpeech then
|
||||
self.RadioSpeech:Stop()
|
||||
end
|
||||
|
||||
self.RadioSpeech = nil
|
||||
|
||||
self.RadioSpeech = RADIOSPEECH:New( RadioFrequency, RadioModulation )
|
||||
self.RadioSpeech.power = RadioPower
|
||||
self.RadioSpeech:Start( 0.5 )
|
||||
|
||||
self.RadioSpeech:SetLanguage( Language )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
@@ -196,8 +196,12 @@ function AI_BALANCER:onenterDestroying( SetGroup, From, Event, To, ClientName, A
|
||||
SetGroup:Flush( self )
|
||||
end
|
||||
|
||||
--- @param #AI_BALANCER self
|
||||
--- RTB
|
||||
-- @param #AI_BALANCER self
|
||||
-- @param Core.Set#SET_GROUP SetGroup
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
function AI_BALANCER:onenterReturning( SetGroup, From, Event, To, AIGroup )
|
||||
|
||||
@@ -213,10 +217,13 @@ function AI_BALANCER:onenterReturning( SetGroup, From, Event, To, AIGroup )
|
||||
local PointVec2 = POINT_VEC2:New( AIGroup:GetVec2().x, AIGroup:GetVec2().y )
|
||||
local ClosestAirbase = self.ReturnAirbaseSet:FindNearestAirbaseFromPointVec2( PointVec2 )
|
||||
self:T( ClosestAirbase.AirbaseName )
|
||||
--[[
|
||||
AIGroup:MessageToRed( "Returning to " .. ClosestAirbase:GetName().. " ...", 30 )
|
||||
local RTBRoute = AIGroup:RouteReturnToAirbase( ClosestAirbase )
|
||||
AIGroupTemplate.route = RTBRoute
|
||||
AIGroup:Respawn( AIGroupTemplate )
|
||||
]]
|
||||
AIGroup:RouteRTB(ClosestAirbase)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -421,7 +421,7 @@ end
|
||||
-- @param #string To The To State string.
|
||||
function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To )
|
||||
|
||||
if Controllable:IsAlive() then
|
||||
if Controllable and Controllable:IsAlive() then
|
||||
|
||||
local EngageRoute = {}
|
||||
|
||||
|
||||
@@ -141,6 +141,17 @@ function AI_CARGO:New( Carrier, CargoSet )
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
--- On after Deployed event.
|
||||
-- @function [parent=#AI_CARGO] OnAfterDeployed
|
||||
-- @param #AI_CARGO self
|
||||
-- @param Wrapper.Group#GROUP Carrier
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
|
||||
-- @param #boolean Defend Defend for APCs.
|
||||
|
||||
|
||||
for _, CarrierUnit in pairs( Carrier:GetUnits() ) do
|
||||
local CarrierUnit = CarrierUnit -- Wrapper.Unit#UNIT
|
||||
@@ -213,8 +224,8 @@ function AI_CARGO:onbeforeLoad( Carrier, From, Event, To, PickupZone )
|
||||
|
||||
local Boarding = false
|
||||
|
||||
local LoadInterval = 5
|
||||
local LoadDelay = 0
|
||||
local LoadInterval = 2
|
||||
local LoadDelay = 1
|
||||
local Carrier_List = {}
|
||||
local Carrier_Weight = {}
|
||||
|
||||
@@ -256,15 +267,17 @@ function AI_CARGO:onbeforeLoad( Carrier, From, Event, To, PickupZone )
|
||||
self:F( { "In radius", CarrierUnit:GetName() } )
|
||||
|
||||
local CargoWeight = Cargo:GetWeight()
|
||||
local CarrierSpace=Carrier_Weight[CarrierUnit]
|
||||
|
||||
-- Only when there is space within the bay to load the next cargo item!
|
||||
if Carrier_Weight[CarrierUnit] > CargoWeight then --and CargoBayFreeVolume > CargoVolume then
|
||||
if CarrierSpace > CargoWeight then
|
||||
Carrier:RouteStop()
|
||||
--Cargo:Ungroup()
|
||||
Cargo:__Board( -LoadDelay, CarrierUnit )
|
||||
LoadDelay = LoadDelay + Cargo:GetCount() * LoadInterval
|
||||
self:__Board( LoadDelay, Cargo, CarrierUnit, PickupZone )
|
||||
|
||||
|
||||
LoadDelay = LoadDelay + Cargo:GetCount() * LoadInterval
|
||||
|
||||
-- So now this CarrierUnit has Cargo that is being loaded.
|
||||
-- This will be used further in the logic to follow and to check cargo status.
|
||||
self.Carrier_Cargo[Cargo] = CarrierUnit
|
||||
@@ -274,13 +287,88 @@ function AI_CARGO:onbeforeLoad( Carrier, From, Event, To, PickupZone )
|
||||
|
||||
-- Ok, we loaded a cargo, now we can stop the loop.
|
||||
break
|
||||
else
|
||||
self:T(string.format("WARNING: Cargo too heavy for carrier %s. Cargo=%.1f > %.1f free space", tostring(CarrierUnit:GetName()), CargoWeight, CarrierSpace))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if not Loaded == true then
|
||||
-- No loading happened, so we need to pickup something else.
|
||||
self.Relocating = false
|
||||
end
|
||||
end
|
||||
|
||||
return Boarding
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- On before Reload event.
|
||||
-- @param #AI_CARGO self
|
||||
-- @param Wrapper.Group#GROUP Carrier
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided.
|
||||
function AI_CARGO:onbeforeReload( Carrier, From, Event, To )
|
||||
self:F( { Carrier, From, Event, To } )
|
||||
|
||||
local Boarding = false
|
||||
|
||||
local LoadInterval = 2
|
||||
local LoadDelay = 1
|
||||
local Carrier_List = {}
|
||||
local Carrier_Weight = {}
|
||||
|
||||
if Carrier and Carrier:IsAlive() then
|
||||
for _, CarrierUnit in pairs( Carrier:GetUnits() ) do
|
||||
local CarrierUnit = CarrierUnit -- Wrapper.Unit#UNIT
|
||||
|
||||
Carrier_List[#Carrier_List+1] = CarrierUnit
|
||||
end
|
||||
|
||||
local Carrier_Count = #Carrier_List
|
||||
local Carrier_Index = 1
|
||||
|
||||
local Loaded = false
|
||||
|
||||
for Cargo, CarrierUnit in pairs( self.Carrier_Cargo ) do
|
||||
local Cargo = Cargo -- Cargo.Cargo#CARGO
|
||||
|
||||
self:F( { IsUnLoaded = Cargo:IsUnLoaded(), IsDeployed = Cargo:IsDeployed(), Cargo:GetName(), Carrier:GetName() } )
|
||||
|
||||
-- Try all Carriers, but start from the one according the Carrier_Index
|
||||
for Carrier_Loop = 1, #Carrier_List do
|
||||
|
||||
local CarrierUnit = Carrier_List[Carrier_Index] -- Wrapper.Unit#UNIT
|
||||
|
||||
-- This counters loop through the available Carriers.
|
||||
Carrier_Index = Carrier_Index + 1
|
||||
if Carrier_Index > Carrier_Count then
|
||||
Carrier_Index = 1
|
||||
end
|
||||
|
||||
if Cargo:IsUnLoaded() and not Cargo:IsDeployed() then
|
||||
Carrier:RouteStop()
|
||||
Cargo:__Board( -LoadDelay, CarrierUnit )
|
||||
self:__Board( LoadDelay, Cargo, CarrierUnit )
|
||||
|
||||
LoadDelay = LoadDelay + Cargo:GetCount() * LoadInterval
|
||||
|
||||
-- So now this CarrierUnit has Cargo that is being loaded.
|
||||
-- This will be used further in the logic to follow and to check cargo status.
|
||||
self.Carrier_Cargo[Cargo] = CarrierUnit
|
||||
Boarding = true
|
||||
Loaded = true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if not Loaded == true then
|
||||
@@ -307,7 +395,7 @@ function AI_CARGO:onafterBoard( Carrier, From, Event, To, Cargo, CarrierUnit, Pi
|
||||
|
||||
if Carrier and Carrier:IsAlive() then
|
||||
self:F({ IsLoaded = Cargo:IsLoaded(), Cargo:GetName(), Carrier:GetName() } )
|
||||
if not Cargo:IsLoaded() then
|
||||
if not Cargo:IsLoaded() and not Cargo:IsDestroyed() then
|
||||
self:__Board( -10, Cargo, CarrierUnit, PickupZone )
|
||||
return
|
||||
end
|
||||
@@ -359,7 +447,7 @@ function AI_CARGO:onafterPickedUp( Carrier, From, Event, To, PickupZone )
|
||||
Carrier:RouteResume()
|
||||
|
||||
local HasCargo = false
|
||||
if Carrier and Carrier :IsAlive() then
|
||||
if Carrier and Carrier:IsAlive() then
|
||||
for Cargo, CarrierUnit in pairs( self.Carrier_Cargo ) do
|
||||
HasCargo = true
|
||||
break
|
||||
@@ -368,6 +456,7 @@ function AI_CARGO:onafterPickedUp( Carrier, From, Event, To, PickupZone )
|
||||
|
||||
self.Relocating = false
|
||||
if HasCargo then
|
||||
self:F( "Transporting" )
|
||||
self.Transporting = true
|
||||
end
|
||||
|
||||
@@ -383,11 +472,11 @@ end
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
|
||||
function AI_CARGO:onafterUnload( Carrier, From, Event, To, DeployZone )
|
||||
self:F( { Carrier, From, Event, To, DeployZone } )
|
||||
function AI_CARGO:onafterUnload( Carrier, From, Event, To, DeployZone, Defend )
|
||||
self:F( { Carrier, From, Event, To, DeployZone, Defend = Defend } )
|
||||
|
||||
local UnboardInterval = 10
|
||||
local UnboardDelay = 10
|
||||
local UnboardInterval = 5
|
||||
local UnboardDelay = 5
|
||||
|
||||
if Carrier and Carrier:IsAlive() then
|
||||
for _, CarrierUnit in pairs( Carrier:GetUnits() ) do
|
||||
@@ -398,8 +487,10 @@ function AI_CARGO:onafterUnload( Carrier, From, Event, To, DeployZone )
|
||||
if Cargo:IsLoaded() then
|
||||
Cargo:__UnBoard( UnboardDelay )
|
||||
UnboardDelay = UnboardDelay + Cargo:GetCount() * UnboardInterval
|
||||
Cargo:SetDeployed( true )
|
||||
self:__Unboard( UnboardDelay, Cargo, CarrierUnit, DeployZone )
|
||||
self:__Unboard( UnboardDelay, Cargo, CarrierUnit, DeployZone, Defend )
|
||||
if not Defend == true then
|
||||
Cargo:SetDeployed( true )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -415,17 +506,17 @@ end
|
||||
-- @param #string To To state.
|
||||
-- @param #string Cargo.Cargo#CARGO Cargo Cargo object.
|
||||
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
|
||||
function AI_CARGO:onafterUnboard( Carrier, From, Event, To, Cargo, CarrierUnit, DeployZone )
|
||||
self:F( { Carrier, From, Event, To, Cargo:GetName() } )
|
||||
function AI_CARGO:onafterUnboard( Carrier, From, Event, To, Cargo, CarrierUnit, DeployZone, Defend )
|
||||
self:F( { Carrier, From, Event, To, Cargo:GetName(), DeployZone = DeployZone, Defend = Defend } )
|
||||
|
||||
if Carrier and Carrier:IsAlive() then
|
||||
if not Cargo:IsUnLoaded() then
|
||||
self:__Unboard( 10, Cargo, CarrierUnit, DeployZone )
|
||||
self:__Unboard( 10, Cargo, CarrierUnit, DeployZone, Defend )
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
self:Unloaded( Cargo, CarrierUnit, DeployZone )
|
||||
self:Unloaded( Cargo, CarrierUnit, DeployZone, Defend )
|
||||
|
||||
end
|
||||
|
||||
@@ -438,8 +529,8 @@ end
|
||||
-- @param #string Cargo.Cargo#CARGO Cargo Cargo object.
|
||||
-- @param #boolean Deployed Cargo is deployed.
|
||||
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
|
||||
function AI_CARGO:onafterUnloaded( Carrier, From, Event, To, Cargo, CarrierUnit, DeployZone )
|
||||
self:F( { Carrier, From, Event, To, Cargo:GetName(), DeployZone = DeployZone } )
|
||||
function AI_CARGO:onafterUnloaded( Carrier, From, Event, To, Cargo, CarrierUnit, DeployZone, Defend )
|
||||
self:F( { Carrier, From, Event, To, Cargo:GetName(), DeployZone = DeployZone, Defend = Defend } )
|
||||
|
||||
local AllUnloaded = true
|
||||
|
||||
@@ -465,7 +556,7 @@ function AI_CARGO:onafterUnloaded( Carrier, From, Event, To, Cargo, CarrierUnit,
|
||||
end
|
||||
|
||||
if AllUnloaded == true then
|
||||
self:__Deployed( 5, DeployZone )
|
||||
self:__Deployed( 5, DeployZone, Defend )
|
||||
end
|
||||
|
||||
end
|
||||
@@ -477,10 +568,16 @@ end
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
|
||||
function AI_CARGO:onafterDeployed( Carrier, From, Event, To, DeployZone )
|
||||
self:F( { Carrier, From, Event, To, DeployZone = DeployZone } )
|
||||
-- @param #boolean Defend Defend for APCs.
|
||||
function AI_CARGO:onafterDeployed( Carrier, From, Event, To, DeployZone, Defend )
|
||||
self:F( { Carrier, From, Event, To, DeployZone = DeployZone, Defend = Defend } )
|
||||
|
||||
if not Defend == true then
|
||||
self.Transporting = false
|
||||
else
|
||||
self:F( "Defending" )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -97,6 +97,7 @@ function AI_CARGO_APC:New( APC, CargoSet, CombatRadius )
|
||||
self:AddTransition( "*", "Follow", "Following" )
|
||||
self:AddTransition( "*", "Guard", "Unloaded" )
|
||||
self:AddTransition( "*", "Home", "*" )
|
||||
self:AddTransition( "*", "Reload", "Boarding" )
|
||||
|
||||
self:AddTransition( "*", "Destroyed", "Destroyed" )
|
||||
|
||||
@@ -118,7 +119,6 @@ function AI_CARGO_APC:SetCarrier( CargoCarrier )
|
||||
self.CargoCarrier:SetState( self.CargoCarrier, "AI_CARGO_APC", self )
|
||||
|
||||
CargoCarrier:HandleEvent( EVENTS.Dead )
|
||||
CargoCarrier:HandleEvent( EVENTS.Hit )
|
||||
|
||||
function CargoCarrier:OnEventDead( EventData )
|
||||
self:F({"dead"})
|
||||
@@ -132,18 +132,20 @@ function AI_CARGO_APC:SetCarrier( CargoCarrier )
|
||||
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 radius. Unload the CargoCarrier.
|
||||
AICargoTroops:Unload( false )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- CargoCarrier:HandleEvent( EVENTS.Hit )
|
||||
--
|
||||
-- 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 radius. 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()
|
||||
@@ -276,34 +278,36 @@ function AI_CARGO_APC:onafterMonitor( APC, From, Event, To )
|
||||
if self.CarrierCoordinate then
|
||||
if self:IsTransporting() == 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 radius. Load the CargoCarrier.
|
||||
self:Load()
|
||||
end
|
||||
else
|
||||
if self:Is( "Loaded" ) then
|
||||
-- There are enemies within combat radius. Unload the CargoCarrier.
|
||||
self:__Unload( 1 )
|
||||
else
|
||||
if self:Is( "Unloaded" ) or self:Is( "Loaded" ) then
|
||||
self.Zone:Scan( { Object.Category.UNIT } )
|
||||
if self.Zone:IsAllInZoneOfCoalition( self.Coalition ) then
|
||||
if self:Is( "Unloaded" ) then
|
||||
self:Follow()
|
||||
-- There are no enemies within combat radius. Reload the CargoCarrier.
|
||||
self:Reload()
|
||||
end
|
||||
self:F( "I am here" .. self:GetCurrentState() )
|
||||
if self:Is( "Following" ) then
|
||||
for Cargo, APCUnit in pairs( self.Carrier_Cargo ) do
|
||||
local Cargo = Cargo -- Cargo.Cargo#CARGO
|
||||
local APCUnit = APCUnit -- Wrapper.Unit#UNIT
|
||||
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
|
||||
else
|
||||
if self:Is( "Loaded" ) then
|
||||
-- There are enemies within combat radius. Unload the CargoCarrier.
|
||||
self:__Unload( 1, nil, true ) -- The 2nd parameter is true, which means that the unload is for defending the carrier, not to deploy!
|
||||
else
|
||||
if self:Is( "Unloaded" ) then
|
||||
--self:Follow()
|
||||
end
|
||||
self:F( "I am here" .. self:GetCurrentState() )
|
||||
if self:Is( "Following" ) then
|
||||
for Cargo, APCUnit in pairs( self.Carrier_Cargo ) do
|
||||
local Cargo = Cargo -- Cargo.Cargo#CARGO
|
||||
local APCUnit = APCUnit -- Wrapper.Unit#UNIT
|
||||
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
|
||||
@@ -442,6 +446,45 @@ function AI_CARGO_APC:onafterDeploy( APC, From, Event, To, Coordinate, Speed, He
|
||||
|
||||
end
|
||||
|
||||
--- On after Unloaded event.
|
||||
-- @param #AI_CARGO_APC self
|
||||
-- @param Wrapper.Group#GROUP Carrier
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param #string Cargo.Cargo#CARGO Cargo Cargo object.
|
||||
-- @param #boolean Deployed Cargo is deployed.
|
||||
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
|
||||
function AI_CARGO_APC:onafterUnloaded( Carrier, From, Event, To, Cargo, CarrierUnit, DeployZone, Defend )
|
||||
self:F( { Carrier, From, Event, To, DeployZone = DeployZone, Defend = Defend } )
|
||||
|
||||
|
||||
self:GetParent( self, AI_CARGO_APC ).onafterUnloaded( self, Carrier, From, Event, To, Cargo, CarrierUnit, DeployZone, Defend )
|
||||
|
||||
-- If Defend == true then we need to scan for possible enemies within combat zone and engage only ground forces.
|
||||
if Defend == true then
|
||||
self.Zone:Scan( { Object.Category.UNIT } )
|
||||
if not self.Zone:IsAllInZoneOfCoalition( self.Coalition ) then
|
||||
-- OK, enemies nearby, now find the enemies and attack them.
|
||||
local AttackUnits = self.Zone:GetScannedUnits() -- #list<DCS#Unit>
|
||||
local Move = {}
|
||||
local CargoGroup = Cargo.CargoObject -- Wrapper.Group#GROUP
|
||||
Move[#Move+1] = CargoGroup:GetCoordinate():WaypointGround( 70, "Custom" )
|
||||
for UnitId, AttackUnit in pairs( AttackUnits ) do
|
||||
local MooseUnit = UNIT:Find( AttackUnit )
|
||||
if MooseUnit:GetCoalition() ~= CargoGroup:GetCoalition() then
|
||||
Move[#Move+1] = MooseUnit:GetCoordinate():WaypointGround( 70, "Line abreast" )
|
||||
--MoveTo.Task = CargoGroup:TaskCombo( CargoGroup:TaskAttackUnit( MooseUnit, true ) )
|
||||
self:F( { MooseUnit = MooseUnit:GetName(), CargoGroup = CargoGroup:GetName() } )
|
||||
end
|
||||
end
|
||||
CargoGroup:RoutePush( Move, 0.1 )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- On after Deployed event.
|
||||
-- @param #AI_CARGO_APC self
|
||||
-- @param Wrapper.Group#GROUP Carrier
|
||||
@@ -449,12 +492,12 @@ end
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
|
||||
function AI_CARGO_APC:onafterDeployed( APC, From, Event, To, DeployZone )
|
||||
self:F( { APC, From, Event, To, DeployZone = DeployZone } )
|
||||
function AI_CARGO_APC:onafterDeployed( APC, From, Event, To, DeployZone, Defend )
|
||||
self:F( { APC, From, Event, To, DeployZone = DeployZone, Defend = Defend } )
|
||||
|
||||
self:__Guard( 0.1 )
|
||||
|
||||
self:GetParent( self, AI_CARGO_APC ).onafterDeployed( self, APC, From, Event, To, DeployZone )
|
||||
self:GetParent( self, AI_CARGO_APC ).onafterDeployed( self, APC, From, Event, To, DeployZone, Defend )
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -76,25 +76,31 @@ function AI_CARGO_AIRPLANE:New( Airplane, CargoSet )
|
||||
--- Pickup Handler OnAfter for AI_CARGO_AIRPLANE
|
||||
-- @function [parent=#AI_CARGO_AIRPLANE] OnAfterPickup
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Group#GROUP Airplane Cargo plane.
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Wrapper.Airbase#AIRBASE Airbase Airbase where troops are picked up.
|
||||
-- @param #number Speed in km/h for travelling to pickup base.
|
||||
-- @param Wrapper.Group#GROUP Airplane Cargo transport plane.
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Point#COORDINATE Coordinate The coordinate where to pickup stuff.
|
||||
-- @param #number Speed Speed in km/h for travelling to pickup base.
|
||||
-- @param #number Height Height in meters to move to the pickup coordinate.
|
||||
-- @param Core.Zone#ZONE_AIRBASE PickupZone The airbase zone where the cargo will be picked up.
|
||||
|
||||
--- Pickup Trigger for AI_CARGO_AIRPLANE
|
||||
-- @function [parent=#AI_CARGO_AIRPLANE] Pickup
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Airbase#AIRBASE Airbase Airbase where troops are picked up.
|
||||
-- @param #number Speed in km/h for travelling to pickup base.
|
||||
-- @param Core.Point#COORDINATE Coordinate The coordinate where to pickup stuff.
|
||||
-- @param #number Speed Speed in km/h for travelling to pickup base.
|
||||
-- @param #number Height Height in meters to move to the pickup coordinate.
|
||||
-- @param Core.Zone#ZONE_AIRBASE PickupZone The airbase zone where the cargo will be picked up.
|
||||
|
||||
--- Pickup Asynchronous Trigger for AI_CARGO_AIRPLANE
|
||||
-- @function [parent=#AI_CARGO_AIRPLANE] __Pickup
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param #number Delay Delay in seconds.
|
||||
-- @param Wrapper.Airbase#AIRBASE Airbase Airbase where troops are picked up.
|
||||
-- @param #number Speed in km/h for travelling to pickup base.
|
||||
-- @param Core.Point#COORDINATE Coordinate The coordinate where to pickup stuff.
|
||||
-- @param #number Speed Speed in km/h for travelling to pickup base.
|
||||
-- @param #number Height Height in meters to move to the pickup coordinate.
|
||||
-- @param Core.Zone#ZONE_AIRBASE PickupZone The airbase zone where the cargo will be picked up.
|
||||
|
||||
--- Deploy Handler OnBefore for AI_CARGO_AIRPLANE
|
||||
-- @function [parent=#AI_CARGO_AIRPLANE] OnBeforeDeploy
|
||||
@@ -111,24 +117,30 @@ function AI_CARGO_AIRPLANE:New( Airplane, CargoSet )
|
||||
-- @function [parent=#AI_CARGO_AIRPLANE] OnAfterDeploy
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Group#GROUP Airplane Cargo plane.
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Wrapper.Airbase#AIRBASE Airbase Destination airbase where troops are deployed.
|
||||
-- @param #number Speed Speed in km/h for travelling to deploy base.
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Point#COORDINATE Coordinate Coordinate where to deploy stuff.
|
||||
-- @param #number Speed Speed in km/h for travelling to the deploy base.
|
||||
-- @param #number Height Height in meters to move to the home coordinate.
|
||||
-- @param Core.Zone#ZONE_AIRBASE DeployZone The airbase zone where the cargo will be deployed.
|
||||
|
||||
--- Deploy Trigger for AI_CARGO_AIRPLANE
|
||||
-- @function [parent=#AI_CARGO_AIRPLANE] Deploy
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Airbase#AIRBASE Airbase Destination airbase where troops are deployed.
|
||||
-- @param #number Speed Speed in km/h for travelling to deploy base.
|
||||
-- @param Core.Point#COORDINATE Coordinate Coordinate where to deploy stuff.
|
||||
-- @param #number Speed Speed in km/h for travelling to the deploy base.
|
||||
-- @param #number Height Height in meters to move to the home coordinate.
|
||||
-- @param Core.Zone#ZONE_AIRBASE DeployZone The airbase zone where the cargo will be deployed.
|
||||
|
||||
--- Deploy Asynchronous Trigger for AI_CARGO_AIRPLANE
|
||||
-- @function [parent=#AI_CARGO_AIRPLANE] __Deploy
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param #number Delay Delay in seconds.
|
||||
-- @param Wrapper.Airbase#AIRBASE Airbase Destination airbase where troops are deployed.
|
||||
-- @param #number Speed Speed in km/h for travelling to deploy base.
|
||||
-- @param Core.Point#COORDINATE Coordinate Coordinate where to deploy stuff.
|
||||
-- @param #number Speed Speed in km/h for travelling to the deploy base.
|
||||
-- @param #number Height Height in meters to move to the home coordinate.
|
||||
-- @param Core.Zone#ZONE_AIRBASE DeployZone The airbase zone where the cargo will be deployed.
|
||||
|
||||
--- On after Loaded event, i.e. triggered when the cargo is inside the carrier.
|
||||
-- @function [parent=#AI_CARGO_AIRPLANE] OnAfterLoaded
|
||||
@@ -137,6 +149,16 @@ function AI_CARGO_AIRPLANE:New( Airplane, CargoSet )
|
||||
-- @param From
|
||||
-- @param Event
|
||||
-- @param To
|
||||
|
||||
|
||||
--- On after Deployed event.
|
||||
-- @function [parent=#AI_CARGO_AIRPLANE] OnAfterDeployed
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Group#GROUP Airplane Cargo plane.
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed.
|
||||
|
||||
-- Set carrier.
|
||||
self:SetCarrier( Airplane )
|
||||
@@ -259,15 +281,17 @@ end
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @param #number Speed in km/h for travelling to pickup base.
|
||||
-- @param Core.Point#COORDINATE Coordinate The coordinate where to pickup stuff.
|
||||
-- @param #number Speed Speed in km/h for travelling to pickup base.
|
||||
-- @param #number Height Height in meters to move to the pickup coordinate.
|
||||
-- @param Core.Zone#ZONE_AIRBASE (optional) PickupZone The zone where the cargo will be picked up.
|
||||
-- @param Core.Zone#ZONE_AIRBASE PickupZone The airbase zone where the cargo will be picked up.
|
||||
function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Coordinate, Speed, Height, PickupZone )
|
||||
|
||||
if Airplane and Airplane:IsAlive() then
|
||||
|
||||
self.PickupZone = PickupZone
|
||||
local airbasepickup=Coordinate:GetClosestAirbase()
|
||||
|
||||
self.PickupZone = PickupZone or ZONE_AIRBASE:New(airbasepickup:GetName())
|
||||
|
||||
-- Get closest airbase of current position.
|
||||
local ClosestAirbase, DistToAirbase=Airplane:GetCoordinate():GetClosestAirbase()
|
||||
@@ -280,11 +304,10 @@ function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Coordinate,
|
||||
end
|
||||
|
||||
-- Set pickup airbase.
|
||||
local Airbase = PickupZone:GetAirbase()
|
||||
local Airbase = self.PickupZone:GetAirbase()
|
||||
|
||||
-- Distance from closest to pickup airbase ==> we need to know if we are already at the pickup airbase.
|
||||
local Dist = Airbase:GetCoordinate():Get2DDistance(ClosestAirbase:GetCoordinate())
|
||||
--env.info("Distance closest to pickup airbase = "..Dist)
|
||||
|
||||
if Airplane:InAir() or Dist>500 then
|
||||
|
||||
@@ -305,7 +328,7 @@ function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Coordinate,
|
||||
|
||||
end
|
||||
|
||||
self:GetParent( self, AI_CARGO_AIRPLANE ).onafterPickup( self, Airplane, From, Event, To, Coordinate, Speed, Height, PickupZone )
|
||||
self:GetParent( self, AI_CARGO_AIRPLANE ).onafterPickup( self, Airplane, From, Event, To, Coordinate, Speed, Height, self.PickupZone )
|
||||
|
||||
end
|
||||
|
||||
@@ -318,15 +341,19 @@ end
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @param #number Speed in km/h for travelling to pickup base.
|
||||
-- @param Core.Point#COORDINATE Coordinate Coordinate where to deploy stuff.
|
||||
-- @param #number Speed Speed in km/h for travelling to the deploy base.
|
||||
-- @param #number Height Height in meters to move to the home coordinate.
|
||||
-- @param Core.Zone#ZONE_AIRBASE DeployZone The zone where the cargo will be deployed.
|
||||
-- @param Core.Zone#ZONE_AIRBASE DeployZone The airbase zone where the cargo will be deployed.
|
||||
function AI_CARGO_AIRPLANE:onafterDeploy( Airplane, From, Event, To, Coordinate, Speed, Height, DeployZone )
|
||||
|
||||
if Airplane and Airplane:IsAlive()~=nil then
|
||||
|
||||
local Airbase = DeployZone:GetAirbase()
|
||||
local Airbase = Coordinate:GetClosestAirbase()
|
||||
|
||||
if DeployZone then
|
||||
Airbase=DeployZone:GetAirbase()
|
||||
end
|
||||
|
||||
-- Activate uncontrolled airplane.
|
||||
if Airplane:IsAlive()==false then
|
||||
@@ -354,6 +381,7 @@ end
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Zone#ZONE_AIRBASE DeployZone The airbase zone where the cargo will be deployed.
|
||||
function AI_CARGO_AIRPLANE:onafterUnload( Airplane, From, Event, To, DeployZone )
|
||||
|
||||
local UnboardInterval = 10
|
||||
|
||||
@@ -894,275 +894,284 @@ end
|
||||
-- @param #AI_CARGO_DISPATCHER self
|
||||
function AI_CARGO_DISPATCHER:onafterMonitor()
|
||||
|
||||
self:F("Carriers")
|
||||
self.SetCarrier:Flush()
|
||||
|
||||
for CarrierGroupName, Carrier in pairs( self.SetCarrier:GetSet() ) do
|
||||
local Carrier = Carrier -- Wrapper.Group#GROUP
|
||||
local AI_Cargo = self.AI_Cargo[Carrier]
|
||||
if not AI_Cargo then
|
||||
|
||||
-- ok, so this Carrier does not have yet an AI_CARGO handling 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]
|
||||
if Carrier:IsAlive() ~= nil then
|
||||
local AI_Cargo = self.AI_Cargo[Carrier]
|
||||
if not AI_Cargo then
|
||||
|
||||
--- Pickup event handler OnAfter for AI_CARGO_DISPATCHER.
|
||||
-- Use this event handler to tailor the event when a CarrierGroup is routed towards a new pickup Coordinate and a specified Speed.
|
||||
-- You can use this event handler to post messages to players, or provide status updates etc.
|
||||
-- @function [parent=#AI_CARGO_DISPATCHER] OnAfterPickup
|
||||
-- @param #AI_CARGO_DISPATCHER self
|
||||
-- @param #string From A string that contains the "*from state name*" when the event was triggered.
|
||||
-- @param #string Event A string that contains the "*event name*" when the event was triggered.
|
||||
-- @param #string To A string that contains the "*to state name*" when the event was triggered.
|
||||
-- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits.
|
||||
-- @param Core.Point#COORDINATE Coordinate The coordinate of the pickup location.
|
||||
-- @param #number Speed The velocity in meters per second on which the CarrierGroup is routed towards the pickup Coordinate.
|
||||
-- @param #number Height Height in meters to move to the pickup coordinate.
|
||||
-- @param Core.Zone#ZONE_AIRBASE PickupZone (optional) The zone from where the cargo is picked up. Note that the zone is optional and may not be provided, but for AI_CARGO_DISPATCHER_AIRBASE there will always be a PickupZone, as the pickup location is an airbase zone.
|
||||
function AI_Cargo.OnAfterPickup( AI_Cargo, CarrierGroup, From, Event, To, Coordinate, Speed, Height, PickupZone )
|
||||
self:Pickup( CarrierGroup, Coordinate, Speed, Height, PickupZone )
|
||||
-- ok, so this Carrier does not have yet an AI_CARGO handling 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]
|
||||
|
||||
--- Pickup event handler OnAfter for AI_CARGO_DISPATCHER.
|
||||
-- Use this event handler to tailor the event when a CarrierGroup is routed towards a new pickup Coordinate and a specified Speed.
|
||||
-- You can use this event handler to post messages to players, or provide status updates etc.
|
||||
-- @function [parent=#AI_CARGO_DISPATCHER] OnAfterPickup
|
||||
-- @param #AI_CARGO_DISPATCHER self
|
||||
-- @param #string From A string that contains the "*from state name*" when the event was triggered.
|
||||
-- @param #string Event A string that contains the "*event name*" when the event was triggered.
|
||||
-- @param #string To A string that contains the "*to state name*" when the event was triggered.
|
||||
-- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits.
|
||||
-- @param Core.Point#COORDINATE Coordinate The coordinate of the pickup location.
|
||||
-- @param #number Speed The velocity in meters per second on which the CarrierGroup is routed towards the pickup Coordinate.
|
||||
-- @param #number Height Height in meters to move to the pickup coordinate.
|
||||
-- @param Core.Zone#ZONE_AIRBASE PickupZone (optional) The zone from where the cargo is picked up. Note that the zone is optional and may not be provided, but for AI_CARGO_DISPATCHER_AIRBASE there will always be a PickupZone, as the pickup location is an airbase zone.
|
||||
function AI_Cargo.OnAfterPickup( AI_Cargo, CarrierGroup, From, Event, To, Coordinate, Speed, Height, PickupZone )
|
||||
self:Pickup( CarrierGroup, Coordinate, Speed, Height, PickupZone )
|
||||
end
|
||||
|
||||
--- Load event handler OnAfter for AI_CARGO_DISPATCHER.
|
||||
-- Use this event handler to tailor the event when a CarrierGroup has initiated the loading or boarding of cargo within reporting or near range.
|
||||
-- You can use this event handler to post messages to players, or provide status updates etc.
|
||||
-- @function [parent=#AI_CARGO_DISPATCHER] OnAfterLoad
|
||||
-- @param #AI_CARGO_DISPATCHER self
|
||||
-- @param #string From A string that contains the "*from state name*" when the event was triggered.
|
||||
-- @param #string Event A string that contains the "*event name*" when the event was triggered.
|
||||
-- @param #string To A string that contains the "*to state name*" when the event was triggered.
|
||||
-- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits.
|
||||
-- @param Core.Zone#ZONE_AIRBASE PickupZone (optional) The zone from where the cargo is picked up. Note that the zone is optional and may not be provided, but for AI_CARGO_DISPATCHER_AIRBASE there will always be a PickupZone, as the pickup location is an airbase zone.
|
||||
|
||||
function AI_Cargo.OnAfterLoad( AI_Cargo, CarrierGroup, From, Event, To, PickupZone )
|
||||
self:Load( CarrierGroup, PickupZone )
|
||||
end
|
||||
|
||||
--- Loading event handler OnAfter for AI_CARGO_DISPATCHER.
|
||||
-- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup is in the process of loading or boarding of a cargo object.
|
||||
-- You can use this event handler to post messages to players, or provide status updates etc.
|
||||
-- Note that this event is triggered repeatedly until all cargo (units) have been boarded into the carrier.
|
||||
-- @function [parent=#AI_CARGO_DISPATCHER] OnAfterLoading
|
||||
-- @param #AI_CARGO_DISPATCHER self
|
||||
-- @param #string From A string that contains the "*from state name*" when the event was triggered.
|
||||
-- @param #string Event A string that contains the "*event name*" when the event was triggered.
|
||||
-- @param #string To A string that contains the "*to state name*" when the event was triggered.
|
||||
-- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits.
|
||||
-- @param Cargo.Cargo#CARGO Cargo The cargo object.
|
||||
-- @param Wrapper.Unit#UNIT CarrierUnit The carrier unit that is executing the cargo loading operation.
|
||||
-- @param Core.Zone#ZONE_AIRBASE PickupZone (optional) The zone from where the cargo is picked up. Note that the zone is optional and may not be provided, but for AI_CARGO_DISPATCHER_AIRBASE there will always be a PickupZone, as the pickup location is an airbase zone.
|
||||
|
||||
function AI_Cargo.OnAfterBoard( AI_Cargo, CarrierGroup, From, Event, To, Cargo, CarrierUnit, PickupZone )
|
||||
self:Loading( CarrierGroup, Cargo, CarrierUnit, PickupZone )
|
||||
end
|
||||
|
||||
--- Loaded event handler OnAfter for AI_CARGO_DISPATCHER.
|
||||
-- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup has loaded a cargo object.
|
||||
-- You can use this event handler to post messages to players, or provide status updates etc.
|
||||
-- Note that if more cargo objects were loading or boarding into the CarrierUnit, then this event can be triggered multiple times for each different Cargo/CarrierUnit.
|
||||
-- A CarrierUnit can be part of the larger CarrierGroup.
|
||||
-- @function [parent=#AI_CARGO_DISPATCHER] OnAfterLoaded
|
||||
-- @param #AI_CARGO_DISPATCHER self
|
||||
-- @param #string From A string that contains the "*from state name*" when the event was triggered.
|
||||
-- @param #string Event A string that contains the "*event name*" when the event was triggered.
|
||||
-- @param #string To A string that contains the "*to state name*" when the event was triggered.
|
||||
-- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits.
|
||||
-- @param Cargo.Cargo#CARGO Cargo The cargo object.
|
||||
-- @param Wrapper.Unit#UNIT CarrierUnit The carrier unit that is executing the cargo loading operation.
|
||||
-- @param Core.Zone#ZONE_AIRBASE PickupZone (optional) The zone from where the cargo is picked up. Note that the zone is optional and may not be provided, but for AI_CARGO_DISPATCHER_AIRBASE there will always be a PickupZone, as the pickup location is an airbase zone.
|
||||
|
||||
function AI_Cargo.OnAfterLoaded( AI_Cargo, CarrierGroup, From, Event, To, Cargo, CarrierUnit, PickupZone )
|
||||
self:Loaded( CarrierGroup, Cargo, CarrierUnit, PickupZone )
|
||||
end
|
||||
|
||||
--- PickedUp event handler OnAfter for AI_CARGO_DISPATCHER.
|
||||
-- Use this event handler to tailor the event when a carrier has picked up all cargo objects into the CarrierGroup.
|
||||
-- You can use this event handler to post messages to players, or provide status updates etc.
|
||||
-- @function [parent=#AI_CARGO_DISPATCHER] OnAfterPickedUp
|
||||
-- @param #AI_CARGO_DISPATCHER self
|
||||
-- @param #string From A string that contains the "*from state name*" when the event was triggered.
|
||||
-- @param #string Event A string that contains the "*event name*" when the event was triggered.
|
||||
-- @param #string To A string that contains the "*to state name*" when the event was triggered.
|
||||
-- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits.
|
||||
-- @param Core.Zone#ZONE_AIRBASE PickupZone (optional) The zone from where the cargo is picked up. Note that the zone is optional and may not be provided, but for AI_CARGO_DISPATCHER_AIRBASE there will always be a PickupZone, as the pickup location is an airbase zone.
|
||||
|
||||
function AI_Cargo.OnAfterPickedUp( AI_Cargo, CarrierGroup, From, Event, To, PickupZone )
|
||||
self:PickedUp( CarrierGroup, PickupZone )
|
||||
self:Transport( CarrierGroup )
|
||||
end
|
||||
|
||||
|
||||
--- Deploy event handler OnAfter for AI_CARGO_DISPATCHER.
|
||||
-- Use this event handler to tailor the event when a CarrierGroup is routed to a deploy coordinate, to Unload all cargo objects in each CarrierUnit.
|
||||
-- You can use this event handler to post messages to players, or provide status updates etc.
|
||||
-- @function [parent=#AI_CARGO_DISPATCHER] OnAfterDeploy
|
||||
-- @param #AI_CARGO_DISPATCHER self
|
||||
-- @param #string From A string that contains the "*from state name*" when the event was triggered.
|
||||
-- @param #string Event A string that contains the "*event name*" when the event was triggered.
|
||||
-- @param #string To A string that contains the "*to state name*" when the event was triggered.
|
||||
-- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits.
|
||||
-- @param Core.Point#COORDINATE Coordinate The deploy coordinate.
|
||||
-- @param #number Speed The velocity in meters per second on which the CarrierGroup is routed towards the deploy Coordinate.
|
||||
-- @param #number Height Height in meters to move to the deploy coordinate.
|
||||
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
|
||||
|
||||
function AI_Cargo.OnAfterDeploy( AI_Cargo, CarrierGroup, From, Event, To, Coordinate, Speed, Height, DeployZone )
|
||||
self:Deploy( CarrierGroup, Coordinate, Speed, Height, DeployZone )
|
||||
end
|
||||
|
||||
|
||||
--- Unload event handler OnAfter for AI_CARGO_DISPATCHER.
|
||||
-- Use this event handler to tailor the event when a CarrierGroup has initiated the unloading or unboarding of cargo.
|
||||
-- You can use this event handler to post messages to players, or provide status updates etc.
|
||||
-- @function [parent=#AI_CARGO_DISPATCHER] OnAfterUnload
|
||||
-- @param #AI_CARGO_DISPATCHER self
|
||||
-- @param #string From A string that contains the "*from state name*" when the event was triggered.
|
||||
-- @param #string Event A string that contains the "*event name*" when the event was triggered.
|
||||
-- @param #string To A string that contains the "*to state name*" when the event was triggered.
|
||||
-- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits.
|
||||
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
|
||||
|
||||
function AI_Cargo.OnAfterUnload( AI_Cargo, Carrier, From, Event, To, Cargo, CarrierUnit, DeployZone )
|
||||
self:Unloading( Carrier, Cargo, CarrierUnit, DeployZone )
|
||||
end
|
||||
|
||||
--- UnLoading event handler OnAfter for AI_CARGO_DISPATCHER.
|
||||
-- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup is in the process of unloading or unboarding of a cargo object.
|
||||
-- You can use this event handler to post messages to players, or provide status updates etc.
|
||||
-- Note that this event is triggered repeatedly until all cargo (units) have been unboarded from the CarrierUnit.
|
||||
-- @function [parent=#AI_CARGO_DISPATCHER] OnAfterUnloading
|
||||
-- @param #AI_CARGO_DISPATCHER self
|
||||
-- @param #string From A string that contains the "*from state name*" when the event was triggered.
|
||||
-- @param #string Event A string that contains the "*event name*" when the event was triggered.
|
||||
-- @param #string To A string that contains the "*to state name*" when the event was triggered.
|
||||
-- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits.
|
||||
-- @param Cargo.Cargo#CARGO Cargo The cargo object.
|
||||
-- @param Wrapper.Unit#UNIT CarrierUnit The carrier unit that is executing the cargo unloading operation.
|
||||
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
|
||||
|
||||
function AI_Cargo.OnAfterUnboard( AI_Cargo, CarrierGroup, From, Event, To, Cargo, CarrierUnit, DeployZone )
|
||||
self:Unloading( CarrierGroup, Cargo, CarrierUnit, DeployZone )
|
||||
end
|
||||
|
||||
|
||||
--- Unloaded event handler OnAfter for AI_CARGO_DISPATCHER.
|
||||
-- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup has unloaded a cargo object.
|
||||
-- You can use this event handler to post messages to players, or provide status updates etc.
|
||||
-- Note that if more cargo objects were unloading or unboarding from the CarrierUnit, then this event can be triggered multiple times for each different Cargo/CarrierUnit.
|
||||
-- A CarrierUnit can be part of the larger CarrierGroup.
|
||||
-- @function [parent=#AI_CARGO_DISPATCHER] OnAfterUnloaded
|
||||
-- @param #AI_CARGO_DISPATCHER self
|
||||
-- @param #string From A string that contains the "*from state name*" when the event was triggered.
|
||||
-- @param #string Event A string that contains the "*event name*" when the event was triggered.
|
||||
-- @param #string To A string that contains the "*to state name*" when the event was triggered.
|
||||
-- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits.
|
||||
-- @param Cargo.Cargo#CARGO Cargo The cargo object.
|
||||
-- @param Wrapper.Unit#UNIT CarrierUnit The carrier unit that is executing the cargo unloading operation.
|
||||
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
|
||||
|
||||
function AI_Cargo.OnAfterUnloaded( AI_Cargo, Carrier, From, Event, To, Cargo, CarrierUnit, DeployZone )
|
||||
self:Unloaded( Carrier, Cargo, CarrierUnit, DeployZone )
|
||||
end
|
||||
|
||||
--- Deployed event handler OnAfter for AI_CARGO_DISPATCHER.
|
||||
-- Use this event handler to tailor the event when a carrier has deployed all cargo objects from the CarrierGroup.
|
||||
-- You can use this event handler to post messages to players, or provide status updates etc.
|
||||
-- @function [parent=#AI_CARGO_DISPATCHER] OnAfterDeployed
|
||||
-- @param #AI_CARGO_DISPATCHER self
|
||||
-- @param #string From A string that contains the "*from state name*" when the event was triggered.
|
||||
-- @param #string Event A string that contains the "*event name*" when the event was triggered.
|
||||
-- @param #string To A string that contains the "*to state name*" when the event was triggered.
|
||||
-- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits.
|
||||
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
|
||||
|
||||
function AI_Cargo.OnAfterDeployed( AI_Cargo, Carrier, From, Event, To, DeployZone )
|
||||
self:Deployed( Carrier, DeployZone )
|
||||
end
|
||||
|
||||
--- Home event handler OnAfter for AI_CARGO_DISPATCHER.
|
||||
-- Use this event handler to tailor the event when a CarrierGroup is returning to the HomeZone, after it has deployed all cargo objects from the CarrierGroup.
|
||||
-- You can use this event handler to post messages to players, or provide status updates etc.
|
||||
-- If there is no HomeZone is specified, the CarrierGroup will stay at the current location after having deployed all cargo.
|
||||
-- @function [parent=#AI_CARGO_DISPATCHER] OnAfterHome
|
||||
-- @param #AI_CARGO_DISPATCHER self
|
||||
-- @param #string From A string that contains the "*from state name*" when the event was triggered.
|
||||
-- @param #string Event A string that contains the "*event name*" when the event was triggered.
|
||||
-- @param #string To A string that contains the "*to state name*" when the event was triggered.
|
||||
-- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits.
|
||||
-- @param Core.Point#COORDINATE Coordinate The home coordinate the Carrier will arrive and stop it's activities.
|
||||
-- @param #number Speed The velocity in meters per second on which the CarrierGroup is routed towards the home Coordinate.
|
||||
-- @param #number Height Height in meters to move to the home coordinate.
|
||||
-- @param Core.Zone#ZONE HomeZone The zone wherein the carrier will return when all cargo has been transported. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
|
||||
|
||||
function AI_Cargo.OnAfterHome( AI_Cargo, Carrier, From, Event, To, Coordinate, Speed, Height, HomeZone )
|
||||
self:Home( Carrier, Coordinate, Speed, Height, HomeZone )
|
||||
end
|
||||
end
|
||||
|
||||
--- Load event handler OnAfter for AI_CARGO_DISPATCHER.
|
||||
-- Use this event handler to tailor the event when a CarrierGroup has initiated the loading or boarding of cargo within reporting or near range.
|
||||
-- You can use this event handler to post messages to players, or provide status updates etc.
|
||||
-- @function [parent=#AI_CARGO_DISPATCHER] OnAfterLoad
|
||||
-- @param #AI_CARGO_DISPATCHER self
|
||||
-- @param #string From A string that contains the "*from state name*" when the event was triggered.
|
||||
-- @param #string Event A string that contains the "*event name*" when the event was triggered.
|
||||
-- @param #string To A string that contains the "*to state name*" when the event was triggered.
|
||||
-- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits.
|
||||
-- @param Core.Zone#ZONE_AIRBASE PickupZone (optional) The zone from where the cargo is picked up. Note that the zone is optional and may not be provided, but for AI_CARGO_DISPATCHER_AIRBASE there will always be a PickupZone, as the pickup location is an airbase zone.
|
||||
|
||||
function AI_Cargo.OnAfterLoad( AI_Cargo, CarrierGroup, From, Event, To, PickupZone )
|
||||
self:Load( CarrierGroup, PickupZone )
|
||||
end
|
||||
|
||||
--- Loading event handler OnAfter for AI_CARGO_DISPATCHER.
|
||||
-- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup is in the process of loading or boarding of a cargo object.
|
||||
-- You can use this event handler to post messages to players, or provide status updates etc.
|
||||
-- Note that this event is triggered repeatedly until all cargo (units) have been boarded into the carrier.
|
||||
-- @function [parent=#AI_CARGO_DISPATCHER] OnAfterLoading
|
||||
-- @param #AI_CARGO_DISPATCHER self
|
||||
-- @param #string From A string that contains the "*from state name*" when the event was triggered.
|
||||
-- @param #string Event A string that contains the "*event name*" when the event was triggered.
|
||||
-- @param #string To A string that contains the "*to state name*" when the event was triggered.
|
||||
-- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits.
|
||||
-- @param Cargo.Cargo#CARGO Cargo The cargo object.
|
||||
-- @param Wrapper.Unit#UNIT CarrierUnit The carrier unit that is executing the cargo loading operation.
|
||||
-- @param Core.Zone#ZONE_AIRBASE PickupZone (optional) The zone from where the cargo is picked up. Note that the zone is optional and may not be provided, but for AI_CARGO_DISPATCHER_AIRBASE there will always be a PickupZone, as the pickup location is an airbase zone.
|
||||
|
||||
function AI_Cargo.OnAfterBoard( AI_Cargo, CarrierGroup, From, Event, To, Cargo, CarrierUnit, PickupZone )
|
||||
self:Loading( CarrierGroup, Cargo, CarrierUnit, PickupZone )
|
||||
end
|
||||
|
||||
--- Loaded event handler OnAfter for AI_CARGO_DISPATCHER.
|
||||
-- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup has loaded a cargo object.
|
||||
-- You can use this event handler to post messages to players, or provide status updates etc.
|
||||
-- Note that if more cargo objects were loading or boarding into the CarrierUnit, then this event can be triggered multiple times for each different Cargo/CarrierUnit.
|
||||
-- A CarrierUnit can be part of the larger CarrierGroup.
|
||||
-- @function [parent=#AI_CARGO_DISPATCHER] OnAfterLoaded
|
||||
-- @param #AI_CARGO_DISPATCHER self
|
||||
-- @param #string From A string that contains the "*from state name*" when the event was triggered.
|
||||
-- @param #string Event A string that contains the "*event name*" when the event was triggered.
|
||||
-- @param #string To A string that contains the "*to state name*" when the event was triggered.
|
||||
-- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits.
|
||||
-- @param Cargo.Cargo#CARGO Cargo The cargo object.
|
||||
-- @param Wrapper.Unit#UNIT CarrierUnit The carrier unit that is executing the cargo loading operation.
|
||||
-- @param Core.Zone#ZONE_AIRBASE PickupZone (optional) The zone from where the cargo is picked up. Note that the zone is optional and may not be provided, but for AI_CARGO_DISPATCHER_AIRBASE there will always be a PickupZone, as the pickup location is an airbase zone.
|
||||
|
||||
function AI_Cargo.OnAfterLoaded( AI_Cargo, CarrierGroup, From, Event, To, Cargo, CarrierUnit, PickupZone )
|
||||
self:Loaded( CarrierGroup, Cargo, CarrierUnit, PickupZone )
|
||||
end
|
||||
|
||||
--- PickedUp event handler OnAfter for AI_CARGO_DISPATCHER.
|
||||
-- Use this event handler to tailor the event when a carrier has picked up all cargo objects into the CarrierGroup.
|
||||
-- You can use this event handler to post messages to players, or provide status updates etc.
|
||||
-- @function [parent=#AI_CARGO_DISPATCHER] OnAfterPickedUp
|
||||
-- @param #AI_CARGO_DISPATCHER self
|
||||
-- @param #string From A string that contains the "*from state name*" when the event was triggered.
|
||||
-- @param #string Event A string that contains the "*event name*" when the event was triggered.
|
||||
-- @param #string To A string that contains the "*to state name*" when the event was triggered.
|
||||
-- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits.
|
||||
-- @param Core.Zone#ZONE_AIRBASE PickupZone (optional) The zone from where the cargo is picked up. Note that the zone is optional and may not be provided, but for AI_CARGO_DISPATCHER_AIRBASE there will always be a PickupZone, as the pickup location is an airbase zone.
|
||||
|
||||
function AI_Cargo.OnAfterPickedUp( AI_Cargo, CarrierGroup, From, Event, To, PickupZone )
|
||||
self:PickedUp( CarrierGroup, PickupZone )
|
||||
self:Transport( CarrierGroup )
|
||||
end
|
||||
|
||||
|
||||
--- Deploy event handler OnAfter for AI_CARGO_DISPATCHER.
|
||||
-- Use this event handler to tailor the event when a CarrierGroup is routed to a deploy coordinate, to Unload all cargo objects in each CarrierUnit.
|
||||
-- You can use this event handler to post messages to players, or provide status updates etc.
|
||||
-- @function [parent=#AI_CARGO_DISPATCHER] OnAfterDeploy
|
||||
-- @param #AI_CARGO_DISPATCHER self
|
||||
-- @param #string From A string that contains the "*from state name*" when the event was triggered.
|
||||
-- @param #string Event A string that contains the "*event name*" when the event was triggered.
|
||||
-- @param #string To A string that contains the "*to state name*" when the event was triggered.
|
||||
-- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits.
|
||||
-- @param Core.Point#COORDINATE Coordinate The deploy coordinate.
|
||||
-- @param #number Speed The velocity in meters per second on which the CarrierGroup is routed towards the deploy Coordinate.
|
||||
-- @param #number Height Height in meters to move to the deploy coordinate.
|
||||
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
|
||||
|
||||
function AI_Cargo.OnAfterDeploy( AI_Cargo, CarrierGroup, From, Event, To, Coordinate, Speed, Height, DeployZone )
|
||||
self:Deploy( CarrierGroup, Coordinate, Speed, Height, DeployZone )
|
||||
end
|
||||
|
||||
|
||||
--- Unload event handler OnAfter for AI_CARGO_DISPATCHER.
|
||||
-- Use this event handler to tailor the event when a CarrierGroup has initiated the unloading or unboarding of cargo.
|
||||
-- You can use this event handler to post messages to players, or provide status updates etc.
|
||||
-- @function [parent=#AI_CARGO_DISPATCHER] OnAfterUnload
|
||||
-- @param #AI_CARGO_DISPATCHER self
|
||||
-- @param #string From A string that contains the "*from state name*" when the event was triggered.
|
||||
-- @param #string Event A string that contains the "*event name*" when the event was triggered.
|
||||
-- @param #string To A string that contains the "*to state name*" when the event was triggered.
|
||||
-- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits.
|
||||
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
|
||||
|
||||
function AI_Cargo.OnAfterUnload( AI_Cargo, Carrier, From, Event, To, Cargo, CarrierUnit, DeployZone )
|
||||
self:Unloading( Carrier, Cargo, CarrierUnit, DeployZone )
|
||||
end
|
||||
|
||||
--- UnLoading event handler OnAfter for AI_CARGO_DISPATCHER.
|
||||
-- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup is in the process of unloading or unboarding of a cargo object.
|
||||
-- You can use this event handler to post messages to players, or provide status updates etc.
|
||||
-- Note that this event is triggered repeatedly until all cargo (units) have been unboarded from the CarrierUnit.
|
||||
-- @function [parent=#AI_CARGO_DISPATCHER] OnAfterUnloading
|
||||
-- @param #AI_CARGO_DISPATCHER self
|
||||
-- @param #string From A string that contains the "*from state name*" when the event was triggered.
|
||||
-- @param #string Event A string that contains the "*event name*" when the event was triggered.
|
||||
-- @param #string To A string that contains the "*to state name*" when the event was triggered.
|
||||
-- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits.
|
||||
-- @param Cargo.Cargo#CARGO Cargo The cargo object.
|
||||
-- @param Wrapper.Unit#UNIT CarrierUnit The carrier unit that is executing the cargo unloading operation.
|
||||
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
|
||||
|
||||
function AI_Cargo.OnAfterUnboard( AI_Cargo, CarrierGroup, From, Event, To, Cargo, CarrierUnit, DeployZone )
|
||||
self:Unloading( CarrierGroup, Cargo, CarrierUnit, DeployZone )
|
||||
end
|
||||
|
||||
|
||||
--- Unloaded event handler OnAfter for AI_CARGO_DISPATCHER.
|
||||
-- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup has unloaded a cargo object.
|
||||
-- You can use this event handler to post messages to players, or provide status updates etc.
|
||||
-- Note that if more cargo objects were unloading or unboarding from the CarrierUnit, then this event can be triggered multiple times for each different Cargo/CarrierUnit.
|
||||
-- A CarrierUnit can be part of the larger CarrierGroup.
|
||||
-- @function [parent=#AI_CARGO_DISPATCHER] OnAfterUnloaded
|
||||
-- @param #AI_CARGO_DISPATCHER self
|
||||
-- @param #string From A string that contains the "*from state name*" when the event was triggered.
|
||||
-- @param #string Event A string that contains the "*event name*" when the event was triggered.
|
||||
-- @param #string To A string that contains the "*to state name*" when the event was triggered.
|
||||
-- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits.
|
||||
-- @param Cargo.Cargo#CARGO Cargo The cargo object.
|
||||
-- @param Wrapper.Unit#UNIT CarrierUnit The carrier unit that is executing the cargo unloading operation.
|
||||
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
|
||||
|
||||
function AI_Cargo.OnAfterUnloaded( AI_Cargo, Carrier, From, Event, To, Cargo, CarrierUnit, DeployZone )
|
||||
self:Unloaded( Carrier, Cargo, CarrierUnit, DeployZone )
|
||||
end
|
||||
|
||||
--- Deployed event handler OnAfter for AI_CARGO_DISPATCHER.
|
||||
-- Use this event handler to tailor the event when a carrier has deployed all cargo objects from the CarrierGroup.
|
||||
-- You can use this event handler to post messages to players, or provide status updates etc.
|
||||
-- @function [parent=#AI_CARGO_DISPATCHER] OnAfterDeployed
|
||||
-- @param #AI_CARGO_DISPATCHER self
|
||||
-- @param #string From A string that contains the "*from state name*" when the event was triggered.
|
||||
-- @param #string Event A string that contains the "*event name*" when the event was triggered.
|
||||
-- @param #string To A string that contains the "*to state name*" when the event was triggered.
|
||||
-- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits.
|
||||
-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
|
||||
|
||||
function AI_Cargo.OnAfterDeployed( AI_Cargo, Carrier, From, Event, To, DeployZone )
|
||||
self:Deployed( Carrier, DeployZone )
|
||||
end
|
||||
|
||||
--- Home event handler OnAfter for AI_CARGO_DISPATCHER.
|
||||
-- Use this event handler to tailor the event when a CarrierGroup is returning to the HomeZone, after it has deployed all cargo objects from the CarrierGroup.
|
||||
-- You can use this event handler to post messages to players, or provide status updates etc.
|
||||
-- If there is no HomeZone is specified, the CarrierGroup will stay at the current location after having deployed all cargo.
|
||||
-- @function [parent=#AI_CARGO_DISPATCHER] OnAfterHome
|
||||
-- @param #AI_CARGO_DISPATCHER self
|
||||
-- @param #string From A string that contains the "*from state name*" when the event was triggered.
|
||||
-- @param #string Event A string that contains the "*event name*" when the event was triggered.
|
||||
-- @param #string To A string that contains the "*to state name*" when the event was triggered.
|
||||
-- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits.
|
||||
-- @param Core.Point#COORDINATE Coordinate The home coordinate the Carrier will arrive and stop it's activities.
|
||||
-- @param #number Speed The velocity in meters per second on which the CarrierGroup is routed towards the home Coordinate.
|
||||
-- @param #number Height Height in meters to move to the home coordinate.
|
||||
-- @param Core.Zone#ZONE HomeZone The zone wherein the carrier will return when all cargo has been transported. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE.
|
||||
|
||||
function AI_Cargo.OnAfterHome( AI_Cargo, Carrier, From, Event, To, Coordinate, Speed, Height, HomeZone )
|
||||
self:Home( Carrier, Coordinate, Speed, Height, HomeZone )
|
||||
end
|
||||
end
|
||||
|
||||
-- The Pickup sequence ...
|
||||
-- Check if this Carrier need to go and Pickup something...
|
||||
-- So, if the cargo bay is not full yet with cargo to be loaded ...
|
||||
self:I( { Carrier = CarrierGroupName, IsRelocating = AI_Cargo:IsRelocating(), IsTransporting = AI_Cargo:IsTransporting() } )
|
||||
if AI_Cargo:IsRelocating() == false and AI_Cargo:IsTransporting() == false then
|
||||
-- ok, so there is a free Carrier
|
||||
-- now find the first cargo that is Unloaded
|
||||
|
||||
local PickupCargo = nil
|
||||
local PickupZone = nil
|
||||
|
||||
--self.SetCargo:Flush()
|
||||
for CargoName, Cargo in UTILS.spairs( self.SetCargo:GetSet(), function( t, a, b ) return t[a]:GetWeight() < t[b]:GetWeight() end ) do
|
||||
local Cargo = Cargo -- Cargo.Cargo#CARGO
|
||||
self:F( { Cargo = Cargo:GetName(), UnLoaded = Cargo:IsUnLoaded(), Deployed = Cargo:IsDeployed(), PickupCargo = self.PickupCargo[Carrier] ~= nil } )
|
||||
if Cargo:IsUnLoaded() == true and Cargo:IsDeployed() == false then
|
||||
local CargoCoordinate = Cargo:GetCoordinate()
|
||||
local CoordinateFree = true
|
||||
--self.PickupZoneSet:Flush()
|
||||
--PickupZone = self.PickupZoneSet:GetRandomZone()
|
||||
PickupZone = self.PickupZoneSet and self.PickupZoneSet:IsCoordinateInZone( CargoCoordinate )
|
||||
if not self.PickupZoneSet or PickupZone then
|
||||
for CarrierPickup, Coordinate in pairs( self.PickupCargo ) do
|
||||
if CarrierPickup:IsAlive() == true then
|
||||
if CargoCoordinate:Get2DDistance( Coordinate ) <= 25 then
|
||||
self:F( { "Coordinate not free for ", Cargo = Cargo:GetName(), Carrier:GetName(), PickupCargo = self.PickupCargo[Carrier] ~= nil } )
|
||||
CoordinateFree = false
|
||||
|
||||
-- The Pickup sequence ...
|
||||
-- Check if this Carrier need to go and Pickup something...
|
||||
-- So, if the cargo bay is not full yet with cargo to be loaded ...
|
||||
self:I( { Carrier = CarrierGroupName, IsRelocating = AI_Cargo:IsRelocating(), IsTransporting = AI_Cargo:IsTransporting() } )
|
||||
if AI_Cargo:IsRelocating() == false and AI_Cargo:IsTransporting() == false then
|
||||
-- ok, so there is a free Carrier
|
||||
-- now find the first cargo that is Unloaded
|
||||
|
||||
local PickupCargo = nil
|
||||
local PickupZone = nil
|
||||
|
||||
self.SetCargo:Flush()
|
||||
for CargoName, Cargo in UTILS.spairs( self.SetCargo:GetSet(), function( t, a, b ) return t[a]:GetWeight() < t[b]:GetWeight() end ) do
|
||||
local Cargo = Cargo -- Cargo.Cargo#CARGO
|
||||
self:F( { Cargo = Cargo:GetName(), UnLoaded = Cargo:IsUnLoaded(), Deployed = Cargo:IsDeployed(), PickupCargo = self.PickupCargo[Carrier] ~= nil } )
|
||||
if Cargo:IsUnLoaded() == true and Cargo:IsDeployed() == false then
|
||||
local CargoCoordinate = Cargo:GetCoordinate()
|
||||
local CoordinateFree = true
|
||||
--self.PickupZoneSet:Flush()
|
||||
--PickupZone = self.PickupZoneSet:GetRandomZone()
|
||||
PickupZone = self.PickupZoneSet and self.PickupZoneSet:IsCoordinateInZone( CargoCoordinate )
|
||||
if not self.PickupZoneSet or PickupZone then
|
||||
for CarrierPickup, Coordinate in pairs( self.PickupCargo ) do
|
||||
if CarrierPickup:IsAlive() == true then
|
||||
if CargoCoordinate:Get2DDistance( Coordinate ) <= 25 then
|
||||
self:F( { "Coordinate not free for ", Cargo = Cargo:GetName(), Carrier:GetName(), PickupCargo = self.PickupCargo[Carrier] ~= nil } )
|
||||
CoordinateFree = false
|
||||
break
|
||||
end
|
||||
else
|
||||
self.PickupCargo[CarrierPickup] = nil
|
||||
end
|
||||
end
|
||||
if CoordinateFree == true then
|
||||
-- Check if this cargo can be picked-up by at least one carrier unit of AI_Cargo.
|
||||
local LargestLoadCapacity = 0
|
||||
for _, Carrier in pairs( Carrier:GetUnits() ) do
|
||||
local LoadCapacity = Carrier:GetCargoBayFreeWeight()
|
||||
if LargestLoadCapacity < LoadCapacity then
|
||||
LargestLoadCapacity = LoadCapacity
|
||||
end
|
||||
end
|
||||
-- So if there is a carrier that has the required load capacity to load the total weight of the cargo, dispatch the carrier.
|
||||
-- Otherwise break and go to the next carrier.
|
||||
-- This will skip cargo which is too large to be able to be loaded by carriers
|
||||
-- and will secure an efficient dispatching scheme.
|
||||
if LargestLoadCapacity >= Cargo:GetWeight() then
|
||||
self.PickupCargo[Carrier] = CargoCoordinate
|
||||
PickupCargo = Cargo
|
||||
break
|
||||
else
|
||||
local text=string.format("WARNING: Cargo %s is too heavy to be loaded into transport. Cargo weight %.1f > %.1f load capacity of carrier %s.",
|
||||
tostring(Cargo:GetName()), Cargo:GetWeight(), LargestLoadCapacity, tostring(Carrier:GetName()))
|
||||
self:I(text)
|
||||
end
|
||||
else
|
||||
self.PickupCargo[CarrierPickup] = nil
|
||||
end
|
||||
end
|
||||
if CoordinateFree == true then
|
||||
-- Check if this cargo can be picked-up by at least one carrier unit of AI_Cargo.
|
||||
local LargestLoadCapacity = 0
|
||||
for _, Carrier in pairs( Carrier:GetUnits() ) do
|
||||
local LoadCapacity = Carrier:GetCargoBayFreeWeight()
|
||||
if LargestLoadCapacity < LoadCapacity then
|
||||
LargestLoadCapacity = LoadCapacity
|
||||
end
|
||||
end
|
||||
-- So if there is aa carrier that has the required load capacity to load the total weight of the cargo, dispatch the carrier.
|
||||
-- Otherwise break and go to the next carrier.
|
||||
-- This will skip cargo which is too large to be able to be loaded by carriers
|
||||
-- and will secure an efficient dispatching scheme.
|
||||
if LargestLoadCapacity >= Cargo:GetWeight() then
|
||||
self.PickupCargo[Carrier] = CargoCoordinate
|
||||
PickupCargo = Cargo
|
||||
break
|
||||
end
|
||||
end
|
||||
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 ), math.random( self.PickupMinHeight, self.PickupMaxHeight ), PickupZone )
|
||||
break
|
||||
else
|
||||
if self.HomeZone then
|
||||
if not self.CarrierHome[Carrier] then
|
||||
self.CarrierHome[Carrier] = true
|
||||
AI_Cargo:Home( self.HomeZone:GetRandomPointVec2(), math.random( self.PickupMinSpeed, self.PickupMaxSpeed ), math.random( self.PickupMinHeight, self.PickupMaxHeight ), self.HomeZone )
|
||||
|
||||
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 ), math.random( self.PickupMinHeight, self.PickupMaxHeight ), PickupZone )
|
||||
break
|
||||
else
|
||||
if self.HomeZone then
|
||||
if not self.CarrierHome[Carrier] then
|
||||
self.CarrierHome[Carrier] = true
|
||||
AI_Cargo:Home( self.HomeZone:GetRandomPointVec2(), math.random( self.PickupMinSpeed, self.PickupMaxSpeed ), math.random( self.PickupMinHeight, self.PickupMaxHeight ), self.HomeZone )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
2182
Moose Development/Moose/AI/AI_Escort.lua
Normal file
2182
Moose Development/Moose/AI/AI_Escort.lua
Normal file
File diff suppressed because it is too large
Load Diff
185
Moose Development/Moose/AI/AI_Escort_Dispatcher.lua
Normal file
185
Moose Development/Moose/AI/AI_Escort_Dispatcher.lua
Normal file
@@ -0,0 +1,185 @@
|
||||
--- **AI** - Models the automatic assignment of AI escorts to player flights.
|
||||
--
|
||||
-- ## Features:
|
||||
-- --
|
||||
-- * Provides the facilities to trigger escorts when players join flight slots.
|
||||
-- *
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_Escort_Dispatcher
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
|
||||
--- @type AI_ESCORT_DISPATCHER
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
|
||||
--- Models the automatic assignment of AI escorts to player flights.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #AI_ESCORT_DISPATCHER
|
||||
AI_ESCORT_DISPATCHER = {
|
||||
ClassName = "AI_ESCORT_DISPATCHER",
|
||||
}
|
||||
|
||||
--- @field #list
|
||||
AI_ESCORT_DISPATCHER.AI_Escorts = {}
|
||||
|
||||
|
||||
--- Creates a new AI_ESCORT_DISPATCHER object.
|
||||
-- @param #AI_ESCORT_DISPATCHER self
|
||||
-- @param Core.Set#SET_GROUP CarrierSet The set of @{Wrapper.Group#GROUP} objects of carriers for which escorts are spawned in.
|
||||
-- @param Core.Spawn#SPAWN EscortSpawn The spawn object that will spawn in the Escorts.
|
||||
-- @param Wrapper.Airbase#AIRBASE EscortAirbase The airbase where the escorts are spawned.
|
||||
-- @param #string EscortName Name of the escort, which will also be the name of the escort menu.
|
||||
-- @param #string EscortBriefing A text showing the briefing to the player. Note that if no EscortBriefing is provided, the default briefing will be shown.
|
||||
-- @return #AI_ESCORT_DISPATCHER
|
||||
-- @usage
|
||||
--
|
||||
-- -- Create a new escort when a player joins an SU-25T plane.
|
||||
-- Create a carrier set, which contains the player slots that can be joined by the players, for which escorts will be defined.
|
||||
-- local Red_SU25T_CarrierSet = SET_GROUP:New():FilterPrefixes( "Red A2G Player Su-25T" ):FilterStart()
|
||||
--
|
||||
-- -- Create a spawn object that will spawn in the escorts, once the player has joined the player slot.
|
||||
-- local Red_SU25T_EscortSpawn = SPAWN:NewWithAlias( "Red A2G Su-25 Escort", "Red AI A2G SU-25 Escort" ):InitLimit( 10, 10 )
|
||||
--
|
||||
-- -- Create an airbase object, where the escorts will be spawned.
|
||||
-- local Red_SU25T_Airbase = AIRBASE:FindByName( AIRBASE.Caucasus.Maykop_Khanskaya )
|
||||
--
|
||||
-- -- Park the airplanes at the airbase, visible before start.
|
||||
-- Red_SU25T_EscortSpawn:ParkAtAirbase( Red_SU25T_Airbase, AIRBASE.TerminalType.OpenMedOrBig )
|
||||
--
|
||||
-- -- New create the escort dispatcher, using the carrier set, the escort spawn object at the escort airbase.
|
||||
-- -- Provide a name of the escort, which will be also the name appearing on the radio menu for the group.
|
||||
-- -- And a briefing to appear when the player joins the player slot.
|
||||
-- Red_SU25T_EscortDispatcher = AI_ESCORT_DISPATCHER:New( Red_SU25T_CarrierSet, Red_SU25T_EscortSpawn, Red_SU25T_Airbase, "Escort Su-25", "You Su-25T is escorted by one Su-25. Use the radio menu to control the escorts." )
|
||||
--
|
||||
-- -- The dispatcher needs to be started using the :Start() method.
|
||||
-- Red_SU25T_EscortDispatcher:Start()
|
||||
function AI_ESCORT_DISPATCHER:New( CarrierSet, EscortSpawn, EscortAirbase, EscortName, EscortBriefing )
|
||||
|
||||
local self = BASE:Inherit( self, FSM:New() ) -- #AI_ESCORT_DISPATCHER
|
||||
|
||||
self.CarrierSet = CarrierSet
|
||||
self.EscortSpawn = EscortSpawn
|
||||
self.EscortAirbase = EscortAirbase
|
||||
self.EscortName = EscortName
|
||||
self.EscortBriefing = EscortBriefing
|
||||
|
||||
self:SetStartState( "Idle" )
|
||||
|
||||
self:AddTransition( "Monitoring", "Monitor", "Monitoring" )
|
||||
|
||||
self:AddTransition( "Idle", "Start", "Monitoring" )
|
||||
self:AddTransition( "Monitoring", "Stop", "Idle" )
|
||||
|
||||
-- Put a Dead event handler on CarrierSet, to ensure that when a carrier is destroyed, that all internal parameters are reset.
|
||||
function self.CarrierSet.OnAfterRemoved( CarrierSet, From, Event, To, CarrierName, Carrier )
|
||||
self:F( { Carrier = Carrier:GetName() } )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function AI_ESCORT_DISPATCHER:onafterStart( From, Event, To )
|
||||
|
||||
self:HandleEvent( EVENTS.Birth )
|
||||
|
||||
self:HandleEvent( EVENTS.PlayerLeaveUnit, self.OnEventExit )
|
||||
self:HandleEvent( EVENTS.Crash, self.OnEventExit )
|
||||
self:HandleEvent( EVENTS.Dead, self.OnEventExit )
|
||||
|
||||
end
|
||||
|
||||
--- @param #AI_ESCORT_DISPATCHER self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function AI_ESCORT_DISPATCHER:OnEventExit( EventData )
|
||||
|
||||
local PlayerGroupName = EventData.IniGroupName
|
||||
local PlayerGroup = EventData.IniGroup
|
||||
local PlayerUnit = EventData.IniUnit
|
||||
|
||||
self:I({EscortAirbase= self.EscortAirbase } )
|
||||
self:I({PlayerGroupName = PlayerGroupName } )
|
||||
self:I({PlayerGroup = PlayerGroup})
|
||||
self:I({FirstGroup = self.CarrierSet:GetFirst()})
|
||||
self:I({FindGroup = self.CarrierSet:FindGroup( PlayerGroupName )})
|
||||
|
||||
if self.CarrierSet:FindGroup( PlayerGroupName ) then
|
||||
if self.AI_Escorts[PlayerGroupName] then
|
||||
self.AI_Escorts[PlayerGroupName]:Stop()
|
||||
self.AI_Escorts[PlayerGroupName] = nil
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- @param #AI_ESCORT_DISPATCHER self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function AI_ESCORT_DISPATCHER:OnEventBirth( EventData )
|
||||
|
||||
local PlayerGroupName = EventData.IniGroupName
|
||||
local PlayerGroup = EventData.IniGroup
|
||||
local PlayerUnit = EventData.IniUnit
|
||||
|
||||
self:I({EscortAirbase= self.EscortAirbase } )
|
||||
self:I({PlayerGroupName = PlayerGroupName } )
|
||||
self:I({PlayerGroup = PlayerGroup})
|
||||
self:I({FirstGroup = self.CarrierSet:GetFirst()})
|
||||
self:I({FindGroup = self.CarrierSet:FindGroup( PlayerGroupName )})
|
||||
|
||||
if self.CarrierSet:FindGroup( PlayerGroupName ) then
|
||||
if not self.AI_Escorts[PlayerGroupName] then
|
||||
local LeaderUnit = PlayerUnit
|
||||
local EscortGroup = self.EscortSpawn:SpawnAtAirbase( self.EscortAirbase, SPAWN.Takeoff.Hot )
|
||||
self:I({EscortGroup = EscortGroup})
|
||||
|
||||
self:ScheduleOnce( 1,
|
||||
function( EscortGroup )
|
||||
local EscortSet = SET_GROUP:New()
|
||||
EscortSet:AddGroup( EscortGroup )
|
||||
self.AI_Escorts[PlayerGroupName] = AI_ESCORT:New( LeaderUnit, EscortSet, self.EscortName, self.EscortBriefing )
|
||||
self.AI_Escorts[PlayerGroupName]:FormationTrail( 0, 100, 0 )
|
||||
if EscortGroup:IsHelicopter() then
|
||||
self.AI_Escorts[PlayerGroupName]:MenusHelicopters()
|
||||
else
|
||||
self.AI_Escorts[PlayerGroupName]:MenusAirplanes()
|
||||
end
|
||||
self.AI_Escorts[PlayerGroupName]:__Start( 0.1 )
|
||||
end, EscortGroup
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Start Trigger for AI_ESCORT_DISPATCHER
|
||||
-- @function [parent=#AI_ESCORT_DISPATCHER] Start
|
||||
-- @param #AI_ESCORT_DISPATCHER self
|
||||
|
||||
--- Start Asynchronous Trigger for AI_ESCORT_DISPATCHER
|
||||
-- @function [parent=#AI_ESCORT_DISPATCHER] __Start
|
||||
-- @param #AI_ESCORT_DISPATCHER self
|
||||
-- @param #number Delay
|
||||
|
||||
--- Stop Trigger for AI_ESCORT_DISPATCHER
|
||||
-- @function [parent=#AI_ESCORT_DISPATCHER] Stop
|
||||
-- @param #AI_ESCORT_DISPATCHER self
|
||||
|
||||
--- Stop Asynchronous Trigger for AI_ESCORT_DISPATCHER
|
||||
-- @function [parent=#AI_ESCORT_DISPATCHER] __Stop
|
||||
-- @param #AI_ESCORT_DISPATCHER self
|
||||
-- @param #number Delay
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
146
Moose Development/Moose/AI/AI_Escort_Dispatcher_Request.lua
Normal file
146
Moose Development/Moose/AI/AI_Escort_Dispatcher_Request.lua
Normal file
@@ -0,0 +1,146 @@
|
||||
--- **AI** - Models the assignment of AI escorts to player flights upon request using the radio menu.
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
-- * Provides the facilities to trigger escorts when players join flight units.
|
||||
-- * Provide a menu for which escorts can be requested.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_ESCORT_DISPATCHER_REQUEST
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
|
||||
--- @type AI_ESCORT_DISPATCHER_REQUEST
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
|
||||
--- Models the assignment of AI escorts to player flights upon request using the radio menu.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #AI_ESCORT_DISPATCHER_REQUEST
|
||||
AI_ESCORT_DISPATCHER_REQUEST = {
|
||||
ClassName = "AI_ESCORT_DISPATCHER_REQUEST",
|
||||
}
|
||||
|
||||
--- @field #list
|
||||
AI_ESCORT_DISPATCHER_REQUEST.AI_Escorts = {}
|
||||
|
||||
|
||||
--- Creates a new AI_ESCORT_DISPATCHER_REQUEST object.
|
||||
-- @param #AI_ESCORT_DISPATCHER_REQUEST self
|
||||
-- @param Core.Set#SET_GROUP CarrierSet The set of @{Wrapper.Group#GROUP} objects of carriers for which escorts are requested.
|
||||
-- @param Core.Spawn#SPAWN EscortSpawn The spawn object that will spawn in the Escorts.
|
||||
-- @param Wrapper.Airbase#AIRBASE EscortAirbase The airbase where the escorts are spawned.
|
||||
-- @param #string EscortName Name of the escort, which will also be the name of the escort menu.
|
||||
-- @param #string EscortBriefing A text showing the briefing to the player. Note that if no EscortBriefing is provided, the default briefing will be shown.
|
||||
-- @return #AI_ESCORT_DISPATCHER_REQUEST
|
||||
function AI_ESCORT_DISPATCHER_REQUEST:New( CarrierSet, EscortSpawn, EscortAirbase, EscortName, EscortBriefing )
|
||||
|
||||
local self = BASE:Inherit( self, FSM:New() ) -- #AI_ESCORT_DISPATCHER_REQUEST
|
||||
|
||||
self.CarrierSet = CarrierSet
|
||||
self.EscortSpawn = EscortSpawn
|
||||
self.EscortAirbase = EscortAirbase
|
||||
self.EscortName = EscortName
|
||||
self.EscortBriefing = EscortBriefing
|
||||
|
||||
self:SetStartState( "Idle" )
|
||||
|
||||
self:AddTransition( "Monitoring", "Monitor", "Monitoring" )
|
||||
|
||||
self:AddTransition( "Idle", "Start", "Monitoring" )
|
||||
self:AddTransition( "Monitoring", "Stop", "Idle" )
|
||||
|
||||
-- Put a Dead event handler on CarrierSet, to ensure that when a carrier is destroyed, that all internal parameters are reset.
|
||||
function self.CarrierSet.OnAfterRemoved( CarrierSet, From, Event, To, CarrierName, Carrier )
|
||||
self:F( { Carrier = Carrier:GetName() } )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function AI_ESCORT_DISPATCHER_REQUEST:onafterStart( From, Event, To )
|
||||
|
||||
self:HandleEvent( EVENTS.Birth )
|
||||
|
||||
self:HandleEvent( EVENTS.PlayerLeaveUnit, self.OnEventExit )
|
||||
self:HandleEvent( EVENTS.Crash, self.OnEventExit )
|
||||
self:HandleEvent( EVENTS.Dead, self.OnEventExit )
|
||||
|
||||
end
|
||||
|
||||
--- @param #AI_ESCORT_DISPATCHER_REQUEST self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function AI_ESCORT_DISPATCHER_REQUEST:OnEventExit( EventData )
|
||||
|
||||
local PlayerGroupName = EventData.IniGroupName
|
||||
local PlayerGroup = EventData.IniGroup
|
||||
local PlayerUnit = EventData.IniUnit
|
||||
|
||||
if self.CarrierSet:FindGroup( PlayerGroupName ) then
|
||||
if self.AI_Escorts[PlayerGroupName] then
|
||||
self.AI_Escorts[PlayerGroupName]:Stop()
|
||||
self.AI_Escorts[PlayerGroupName] = nil
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- @param #AI_ESCORT_DISPATCHER_REQUEST self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function AI_ESCORT_DISPATCHER_REQUEST:OnEventBirth( EventData )
|
||||
|
||||
local PlayerGroupName = EventData.IniGroupName
|
||||
local PlayerGroup = EventData.IniGroup
|
||||
local PlayerUnit = EventData.IniUnit
|
||||
|
||||
if self.CarrierSet:FindGroup( PlayerGroupName ) then
|
||||
if not self.AI_Escorts[PlayerGroupName] then
|
||||
local LeaderUnit = PlayerUnit
|
||||
self:ScheduleOnce( 0.1,
|
||||
function()
|
||||
self.AI_Escorts[PlayerGroupName] = AI_ESCORT_REQUEST:New( LeaderUnit, self.EscortSpawn, self.EscortAirbase, self.EscortName, self.EscortBriefing )
|
||||
self.AI_Escorts[PlayerGroupName]:FormationTrail( 0, 100, 0 )
|
||||
if PlayerGroup:IsHelicopter() then
|
||||
self.AI_Escorts[PlayerGroupName]:MenusHelicopters()
|
||||
else
|
||||
self.AI_Escorts[PlayerGroupName]:MenusAirplanes()
|
||||
end
|
||||
self.AI_Escorts[PlayerGroupName]:__Start( 0.1 )
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Start Trigger for AI_ESCORT_DISPATCHER_REQUEST
|
||||
-- @function [parent=#AI_ESCORT_DISPATCHER_REQUEST] Start
|
||||
-- @param #AI_ESCORT_DISPATCHER_REQUEST self
|
||||
|
||||
--- Start Asynchronous Trigger for AI_ESCORT_DISPATCHER_REQUEST
|
||||
-- @function [parent=#AI_ESCORT_DISPATCHER_REQUEST] __Start
|
||||
-- @param #AI_ESCORT_DISPATCHER_REQUEST self
|
||||
-- @param #number Delay
|
||||
|
||||
--- Stop Trigger for AI_ESCORT_DISPATCHER_REQUEST
|
||||
-- @function [parent=#AI_ESCORT_DISPATCHER_REQUEST] Stop
|
||||
-- @param #AI_ESCORT_DISPATCHER_REQUEST self
|
||||
|
||||
--- Stop Asynchronous Trigger for AI_ESCORT_DISPATCHER_REQUEST
|
||||
-- @function [parent=#AI_ESCORT_DISPATCHER_REQUEST] __Stop
|
||||
-- @param #AI_ESCORT_DISPATCHER_REQUEST self
|
||||
-- @param #number Delay
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
316
Moose Development/Moose/AI/AI_Escort_Request.lua
Normal file
316
Moose Development/Moose/AI/AI_Escort_Request.lua
Normal file
@@ -0,0 +1,316 @@
|
||||
--- **Functional** -- Taking the lead of AI escorting your flight or of other AI, upon request using the menu.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
-- * Escort navigation commands.
|
||||
-- * Escort hold at position commands.
|
||||
-- * Escorts reporting detected targets.
|
||||
-- * Escorts scanning targets in advance.
|
||||
-- * Escorts attacking specific targets.
|
||||
-- * Request assistance from other groups for attack.
|
||||
-- * Manage rule of engagement of escorts.
|
||||
-- * Manage the allowed evasion techniques of escorts.
|
||||
-- * Make escort to execute a defined mission or path.
|
||||
-- * Escort tactical situation reporting.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Missions:
|
||||
--
|
||||
-- [ESC - Escorting](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/ESC%20-%20Escorting)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- Allows you to interact with escorting AI on your flight and take the lead.
|
||||
--
|
||||
-- Each escorting group can be commanded with a complete set of radio commands (radio menu in your flight, and then F10).
|
||||
--
|
||||
-- The radio commands will vary according the category of the group. The richest set of commands are with helicopters and airPlanes.
|
||||
-- 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.
|
||||
--
|
||||
-- Escorts detect targets using a built-in detection mechanism. The detected targets are reported at a specified time interval.
|
||||
-- Once targets are reported, each escort has these targets as menu options to command the attack of these targets.
|
||||
-- Targets are by default grouped per area of 5000 meters, but the kind of detection and the grouping range can be altered.
|
||||
--
|
||||
-- Different formations can be selected in the Flight menu: Trail, Stack, Left Line, Right Line, Left Wing, Right Wing, Central Wing and Boxed formations are available.
|
||||
-- The Flight menu also allows for a mass attack, where all of the escorts are commanded to attack a target.
|
||||
--
|
||||
-- Escorts can emit flares to reports their location. They can be commanded to hold at a location, which can be their current or the leader location.
|
||||
-- In this way, you can spread out the escorts over the battle field before a coordinated attack.
|
||||
--
|
||||
-- But basically, the escort class provides 4 modes of operation, and depending on the mode, you are either leading the flight, or following the flight.
|
||||
--
|
||||
-- ## Leading the flight
|
||||
--
|
||||
-- When leading the flight, you are expected to guide the escorts towards the target areas,
|
||||
-- and carefully coordinate the attack based on the threat levels reported, and the available weapons
|
||||
-- carried by the escorts. Ground ships or ground troops can execute A-assisted attacks, when they have long-range ground precision weapons for attack.
|
||||
--
|
||||
-- ## Following the flight
|
||||
--
|
||||
-- Escorts can be commanded to execute a specific mission path. In this mode, the escorts are in the lead.
|
||||
-- You as a player, are following the escorts, and are commanding them to progress the mission while
|
||||
-- ensuring that the escorts survive. You are joining the escorts in the battlefield. They will detect and report targets
|
||||
-- and you will ensure that the attacks are well coordinated, assigning the correct escort type for the detected target
|
||||
-- type. Once the attack is finished, the escort will resume the mission it was assigned.
|
||||
-- In other words, you can use the escorts for reconnaissance, and for guiding the attack.
|
||||
-- Imagine you as a mi-8 pilot, assigned to pickup cargo. Two ka-50s are guiding the way, and you are
|
||||
-- following. You are in control. The ka-50s detect targets, report them, and you command how the attack
|
||||
-- will commence and from where. You can control where the escorts are holding position and which targets
|
||||
-- are attacked first. You are in control how the ka-50s will follow their mission path.
|
||||
--
|
||||
-- Escorts can act as part of a AI A2G dispatcher offensive. In this way, You was a player are in control.
|
||||
-- The mission is defined by the A2G dispatcher, and you are responsible to join the flight and ensure that the
|
||||
-- attack is well coordinated.
|
||||
--
|
||||
-- It is with great proud that I present you this class, and I hope you will enjoy the functionality and the dynamism
|
||||
-- it brings in your DCS world simulations.
|
||||
--
|
||||
-- # RADIO MENUs that can be created:
|
||||
--
|
||||
-- Find a summary below of the current available commands:
|
||||
--
|
||||
-- ## Navigation ...:
|
||||
--
|
||||
-- Escort group navigation functions:
|
||||
--
|
||||
-- * **"Join-Up":** The escort group fill follow you in the assigned formation.
|
||||
-- * **"Flare":** Provides menu commands to let the escort group shoot a flare in the air in a color.
|
||||
-- * **"Smoke":** Provides menu commands to let the escort group smoke the air in a color. Note that smoking is only available for ground and naval troops.
|
||||
--
|
||||
-- ## Hold position ...:
|
||||
--
|
||||
-- Escort group navigation functions:
|
||||
--
|
||||
-- * **"At current location":** The escort group will hover above the ground at the position they were. The altitude can be specified as a parameter.
|
||||
-- * **"At my location":** The escort group will hover or orbit at the position where you are. The escort will fly to your location and hold position. The altitude can be specified as a parameter.
|
||||
--
|
||||
-- ## Report targets ...:
|
||||
--
|
||||
-- Report targets will make the escort group to report any target that it identifies within detection range. Any detected target can be attacked using the "Attack Targets" menu function. (see below).
|
||||
--
|
||||
-- * **"Report now":** Will report the current detected targets.
|
||||
-- * **"Report targets on":** Will make the escorts to report the detected targets and will fill the "Attack Targets" menu list.
|
||||
-- * **"Report targets off":** Will stop detecting targets.
|
||||
--
|
||||
-- ## Attack targets ...:
|
||||
--
|
||||
-- This menu item will list all detected targets within a 15km range. Depending on the level of detection (known/unknown) and visuality, the targets type will also be listed.
|
||||
-- This menu will be available in Flight menu or in each Escort menu.
|
||||
--
|
||||
-- ## Scan targets ...:
|
||||
--
|
||||
-- Menu items to pop-up the escort group for target scanning. After scanning, the escort group will resume with the mission or rejoin formation.
|
||||
--
|
||||
-- * **"Scan targets 30 seconds":** Scan 30 seconds for targets.
|
||||
-- * **"Scan targets 60 seconds":** Scan 60 seconds for targets.
|
||||
--
|
||||
-- ## Request assistance from ...:
|
||||
--
|
||||
-- This menu item will list all detected targets within a 15km range, similar as with the menu item **Attack Targets**.
|
||||
-- This menu item allows to request attack support from other ground based escorts supporting the current escort.
|
||||
-- eg. the function allows a player to request support from the Ship escort to attack a target identified by the Plane escort with its Tomahawk missiles.
|
||||
-- eg. the function allows a player to request support from other Planes escorting to bomb the unit with illumination missiles or bombs, so that the main plane escort can attack the area.
|
||||
--
|
||||
-- ## ROE ...:
|
||||
--
|
||||
-- Sets the Rules of Engagement (ROE) of the escort group when in flight.
|
||||
--
|
||||
-- * **"Hold Fire":** The escort group will hold fire.
|
||||
-- * **"Return Fire":** The escort group will return fire.
|
||||
-- * **"Open Fire":** The escort group will open fire on designated targets.
|
||||
-- * **"Weapon Free":** The escort group will engage with any target.
|
||||
--
|
||||
-- ## Evasion ...:
|
||||
--
|
||||
-- Will define the evasion techniques that the escort group will perform during flight or combat.
|
||||
--
|
||||
-- * **"Fight until death":** The escort group will have no reaction to threats.
|
||||
-- * **"Use flares, chaff and jammers":** The escort group will use passive defense using flares and jammers. No evasive manoeuvres are executed.
|
||||
-- * **"Evade enemy fire":** The rescort group will evade enemy fire before firing.
|
||||
-- * **"Go below radar and evade fire":** The escort group will perform evasive vertical manoeuvres.
|
||||
--
|
||||
-- ## Resume Mission ...:
|
||||
--
|
||||
-- Escort groups can have their own mission. This menu item will allow the escort group to resume their Mission from a given waypoint.
|
||||
-- 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.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Authors: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_Escort
|
||||
-- @image Escorting.JPG
|
||||
|
||||
|
||||
|
||||
--- @type AI_ESCORT_REQUEST
|
||||
-- @extends AI.AI_Escort#AI_ESCORT
|
||||
|
||||
--- AI_ESCORT_REQUEST class
|
||||
--
|
||||
-- # AI_ESCORT_REQUEST construction methods.
|
||||
--
|
||||
-- Create a new AI_ESCORT_REQUEST object with the @{#AI_ESCORT_REQUEST.New} method:
|
||||
--
|
||||
-- * @{#AI_ESCORT_REQUEST.New}: Creates a new AI_ESCORT_REQUEST object from a @{Wrapper.Group#GROUP} for a @{Wrapper.Client#CLIENT}, with an optional briefing text.
|
||||
--
|
||||
-- @usage
|
||||
-- -- Declare a new EscortPlanes object as follows:
|
||||
--
|
||||
-- -- First find the GROUP object and the CLIENT object.
|
||||
-- local EscortUnit = CLIENT:FindByName( "Unit Name" ) -- The Unit Name is the name of the unit flagged with the skill Client in the mission editor.
|
||||
-- local EscortGroup = GROUP:FindByName( "Group Name" ) -- The Group Name is the name of the group that will escort the Escort Client.
|
||||
--
|
||||
-- -- Now use these 2 objects to construct the new EscortPlanes object.
|
||||
-- EscortPlanes = AI_ESCORT_REQUEST:New( EscortUnit, EscortGroup, "Desert", "Welcome to the mission. You are escorted by a plane with code name 'Desert', which can be instructed through the F10 radio menu." )
|
||||
--
|
||||
-- @field #AI_ESCORT_REQUEST
|
||||
AI_ESCORT_REQUEST = {
|
||||
ClassName = "AI_ESCORT_REQUEST",
|
||||
}
|
||||
|
||||
--- AI_ESCORT_REQUEST.Mode class
|
||||
-- @type AI_ESCORT_REQUEST.MODE
|
||||
-- @field #number FOLLOW
|
||||
-- @field #number MISSION
|
||||
|
||||
--- MENUPARAM type
|
||||
-- @type MENUPARAM
|
||||
-- @field #AI_ESCORT_REQUEST ParamSelf
|
||||
-- @field #Distance ParamDistance
|
||||
-- @field #function ParamFunction
|
||||
-- @field #string ParamMessage
|
||||
|
||||
--- AI_ESCORT_REQUEST class constructor for an AI group
|
||||
-- @param #AI_ESCORT_REQUEST self
|
||||
-- @param Wrapper.Client#CLIENT EscortUnit The client escorted by the EscortGroup.
|
||||
-- @param Core.Spawn#SPAWN EscortSpawn The spawn object of AI, escorting the EscortUnit.
|
||||
-- @param Wrapper.Airbase#AIRBASE EscortAirbase The airbase where escorts will be spawned once requested.
|
||||
-- @param #string EscortName Name of the escort.
|
||||
-- @param #string EscortBriefing A text showing the AI_ESCORT_REQUEST briefing to the player. Note that if no EscortBriefing is provided, the default briefing will be shown.
|
||||
-- @return #AI_ESCORT_REQUEST
|
||||
-- @usage
|
||||
-- EscortSpawn = SPAWN:NewWithAlias( "Red A2G Escort Template", "Red A2G Escort AI" ):InitLimit( 10, 10 )
|
||||
-- EscortSpawn:ParkAtAirbase( AIRBASE:FindByName( AIRBASE.Caucasus.Sochi_Adler ), AIRBASE.TerminalType.OpenBig )
|
||||
--
|
||||
-- local EscortUnit = UNIT:FindByName( "Red A2G Pilot" )
|
||||
--
|
||||
-- Escort = AI_ESCORT_REQUEST:New( EscortUnit, EscortSpawn, AIRBASE:FindByName(AIRBASE.Caucasus.Sochi_Adler), "A2G", "Briefing" )
|
||||
-- Escort:FormationTrail( 50, 100, 100 )
|
||||
-- Escort:Menus()
|
||||
-- Escort:__Start( 5 )
|
||||
function AI_ESCORT_REQUEST:New( EscortUnit, EscortSpawn, EscortAirbase, EscortName, EscortBriefing )
|
||||
|
||||
local EscortGroupSet = SET_GROUP:New():FilterDeads():FilterCrashes()
|
||||
local self = BASE:Inherit( self, AI_ESCORT:New( EscortUnit, EscortGroupSet, EscortName, EscortBriefing ) ) -- #AI_ESCORT_REQUEST
|
||||
|
||||
self.EscortGroupSet = EscortGroupSet
|
||||
self.EscortSpawn = EscortSpawn
|
||||
self.EscortAirbase = EscortAirbase
|
||||
|
||||
self.LeaderGroup = self.PlayerUnit:GetGroup()
|
||||
|
||||
self.Detection = DETECTION_AREAS:New( self.EscortGroupSet, 5000 )
|
||||
self.Detection:__Start( 30 )
|
||||
|
||||
self.SpawnMode = self.__Enum.Mode.Mission
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- @param #AI_ESCORT_REQUEST self
|
||||
function AI_ESCORT_REQUEST:SpawnEscort()
|
||||
|
||||
local EscortGroup = self.EscortSpawn:SpawnAtAirbase( self.EscortAirbase, SPAWN.Takeoff.Hot )
|
||||
|
||||
self:ScheduleOnce( 0.1,
|
||||
function( EscortGroup )
|
||||
|
||||
EscortGroup:OptionROTVertical()
|
||||
EscortGroup:OptionROEHoldFire()
|
||||
|
||||
self.EscortGroupSet:AddGroup( EscortGroup )
|
||||
|
||||
local LeaderEscort = self.EscortGroupSet:GetFirst() -- Wrapper.Group#GROUP
|
||||
local Report = REPORT:New()
|
||||
Report:Add( "Joining Up " .. self.EscortGroupSet:GetUnitTypeNames():Text( ", " ) .. " from " .. LeaderEscort:GetCoordinate():ToString( self.EscortUnit ) )
|
||||
LeaderEscort:MessageTypeToGroup( Report:Text(), MESSAGE.Type.Information, self.PlayerUnit )
|
||||
|
||||
self:SetFlightModeFormation( EscortGroup )
|
||||
self:FormationTrail()
|
||||
|
||||
self:_InitFlightMenus()
|
||||
self:_InitEscortMenus( EscortGroup )
|
||||
self:_InitEscortRoute( EscortGroup )
|
||||
|
||||
--- @param #AI_ESCORT self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function EscortGroup:OnEventDeadOrCrash( EventData )
|
||||
self:F( { "EventDead", EventData } )
|
||||
self.EscortMenu:Remove()
|
||||
end
|
||||
|
||||
EscortGroup:HandleEvent( EVENTS.Dead, EscortGroup.OnEventDeadOrCrash )
|
||||
EscortGroup:HandleEvent( EVENTS.Crash, EscortGroup.OnEventDeadOrCrash )
|
||||
|
||||
end, EscortGroup
|
||||
)
|
||||
|
||||
end
|
||||
|
||||
--- @param #AI_ESCORT_REQUEST self
|
||||
-- @param Core.Set#SET_GROUP EscortGroupSet
|
||||
function AI_ESCORT_REQUEST:onafterStart( EscortGroupSet )
|
||||
|
||||
self:F()
|
||||
|
||||
if not self.MenuRequestEscort then
|
||||
self.MainMenu = MENU_GROUP:New( self.PlayerGroup, self.EscortName )
|
||||
self.MenuRequestEscort = MENU_GROUP_COMMAND:New( self.LeaderGroup, "Request new escort ", self.MainMenu,
|
||||
function()
|
||||
self:SpawnEscort()
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
self:GetParent( self ).onafterStart( self, EscortGroupSet )
|
||||
|
||||
self:HandleEvent( EVENTS.Dead, self.OnEventDeadOrCrash )
|
||||
self:HandleEvent( EVENTS.Crash, self.OnEventDeadOrCrash )
|
||||
|
||||
end
|
||||
|
||||
--- @param #AI_ESCORT_REQUEST self
|
||||
-- @param Core.Set#SET_GROUP EscortGroupSet
|
||||
function AI_ESCORT_REQUEST:onafterStop( EscortGroupSet )
|
||||
|
||||
self:F()
|
||||
|
||||
EscortGroupSet:ForEachGroup(
|
||||
--- @param Core.Group#GROUP EscortGroup
|
||||
function( EscortGroup )
|
||||
EscortGroup:WayPointInitialize()
|
||||
|
||||
EscortGroup:OptionROTVertical()
|
||||
EscortGroup:OptionROEOpenFire()
|
||||
end
|
||||
)
|
||||
|
||||
self.Detection:Stop()
|
||||
|
||||
self.MainMenu:Remove()
|
||||
|
||||
end
|
||||
|
||||
--- Set the spawn mode to be mission execution.
|
||||
-- @param #AI_ESCORT_REQUEST self
|
||||
function AI_ESCORT_REQUEST:SetEscortSpawnMission()
|
||||
|
||||
self.SpawnMode = self.__Enum.Mode.Mission
|
||||
|
||||
end
|
||||
@@ -36,6 +36,7 @@
|
||||
-- @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 #number dtFollow Time step between position updates.
|
||||
|
||||
|
||||
--- Build large formations, make AI follow a @{Wrapper.Client#CLIENT} (player) leader or a @{Wrapper.Unit#UNIT} (AI) leader.
|
||||
@@ -106,12 +107,59 @@ AI_FORMATION = {
|
||||
FollowScheduler = nil,
|
||||
OptionROE = AI.Option.Air.val.ROE.OPEN_FIRE,
|
||||
OptionReactionOnThreat = AI.Option.Air.val.REACTION_ON_THREAT.ALLOW_ABORT_MISSION,
|
||||
dtFollow = 0.5,
|
||||
}
|
||||
|
||||
--- AI_FORMATION.Mode class
|
||||
-- @type AI_FORMATION.MODE
|
||||
-- @field #number FOLLOW
|
||||
-- @field #number MISSION
|
||||
AI_FORMATION.__Enum = {}
|
||||
|
||||
--- @type AI_FORMATION.__Enum.Formation
|
||||
-- @field #number None
|
||||
-- @field #number Line
|
||||
-- @field #number Trail
|
||||
-- @field #number Stack
|
||||
-- @field #number LeftLine
|
||||
-- @field #number RightLine
|
||||
-- @field #number LeftWing
|
||||
-- @field #number RightWing
|
||||
-- @field #number Vic
|
||||
-- @field #number Box
|
||||
AI_FORMATION.__Enum.Formation = {
|
||||
None = 0,
|
||||
Mission = 1,
|
||||
Line = 2,
|
||||
Trail = 3,
|
||||
Stack = 4,
|
||||
LeftLine = 5,
|
||||
RightLine = 6,
|
||||
LeftWing = 7,
|
||||
RightWing = 8,
|
||||
Vic = 9,
|
||||
Box = 10,
|
||||
}
|
||||
|
||||
--- @type AI_FORMATION.__Enum.Mode
|
||||
-- @field #number Mission
|
||||
-- @field #number Formation
|
||||
AI_FORMATION.__Enum.Mode = {
|
||||
Mission = "M",
|
||||
Formation = "F",
|
||||
Attack = "A",
|
||||
Reconnaissance = "R",
|
||||
}
|
||||
|
||||
--- @type AI_FORMATION.__Enum.ReportType
|
||||
-- @field #number All
|
||||
-- @field #number Airborne
|
||||
-- @field #number GroundRadar
|
||||
-- @field #number Ground
|
||||
AI_FORMATION.__Enum.ReportType = {
|
||||
Airborne = "*",
|
||||
Airborne = "A",
|
||||
GroundRadar = "R",
|
||||
Ground = "G",
|
||||
}
|
||||
|
||||
|
||||
|
||||
--- MENUPARAM type
|
||||
-- @type MENUPARAM
|
||||
@@ -125,6 +173,7 @@ AI_FORMATION = {
|
||||
-- @param Wrapper.Unit#UNIT FollowUnit The UNIT leading the FolllowGroupSet.
|
||||
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
|
||||
-- @param #string FollowName Name of the escort.
|
||||
-- @param #string FollowBriefing Briefing.
|
||||
-- @return #AI_FORMATION self
|
||||
function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefing ) --R2.1
|
||||
local self = BASE:Inherit( self, FSM_SET:New( FollowGroupSet ) )
|
||||
@@ -133,13 +182,20 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
self.FollowUnit = FollowUnit -- Wrapper.Unit#UNIT
|
||||
self.FollowGroupSet = FollowGroupSet -- Core.Set#SET_GROUP
|
||||
|
||||
self.FollowGroupSet:ForEachGroup(
|
||||
function( FollowGroup )
|
||||
self:E("Following")
|
||||
FollowGroup:SetState( self, "Mode", self.__Enum.Mode.Formation )
|
||||
end
|
||||
)
|
||||
|
||||
self:SetFlightRandomization( 2 )
|
||||
|
||||
self:SetStartState( "None" )
|
||||
|
||||
self:AddTransition( "*", "Stop", "Stopped" )
|
||||
|
||||
self:AddTransition( "None", "Start", "Following" )
|
||||
self:AddTransition( {"None", "Stopped"}, "Start", "Following" )
|
||||
|
||||
self:AddTransition( "*", "FormationLine", "*" )
|
||||
--- FormationLine Handler OnBefore for AI_FORMATION
|
||||
@@ -620,6 +676,16 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set time interval between updates of the formation.
|
||||
-- @param #AI_FORMATION self
|
||||
-- @param #number dt Time step in seconds between formation updates. Default is every 0.5 seconds.
|
||||
-- @return #AI_FORMATION
|
||||
function AI_FORMATION:SetFollowTimeInterval(dt) --R2.1
|
||||
self.dtFollow=dt or 0.5
|
||||
return self
|
||||
end
|
||||
|
||||
--- This function is for test, it will put on the frequency of the FollowScheduler a red smoke at the direction vector calculated for the escort to fly to.
|
||||
-- This allows to visualize where the escort is flying to.
|
||||
-- @param #AI_FORMATION self
|
||||
@@ -643,8 +709,15 @@ end
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
-- @return #AI_FORMATION
|
||||
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 } )
|
||||
function AI_FORMATION:onafterFormationLine( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace, Formation ) --R2.1
|
||||
self:F( { FollowGroupSet, From , Event ,To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace, Formation } )
|
||||
|
||||
XStart = XStart or self.XStart
|
||||
XSpace = XSpace or self.XSpace
|
||||
YStart = YStart or self.YStart
|
||||
YSpace = YSpace or self.YSpace
|
||||
ZStart = ZStart or self.ZStart
|
||||
ZSpace = ZSpace or self.ZSpace
|
||||
|
||||
FollowGroupSet:Flush( self )
|
||||
|
||||
@@ -662,6 +735,8 @@ function AI_FORMATION:onafterFormationLine( FollowGroupSet, From , Event , To, X
|
||||
local Vec3 = PointVec3:GetVec3()
|
||||
FollowGroup:SetState( self, "FormationVec3", Vec3 )
|
||||
i = i + 1
|
||||
|
||||
FollowGroup:SetState( FollowGroup, "Formation", Formation )
|
||||
end
|
||||
|
||||
return self
|
||||
@@ -680,7 +755,7 @@ end
|
||||
-- @return #AI_FORMATION
|
||||
function AI_FORMATION:onafterFormationTrail( FollowGroupSet, From , Event , To, XStart, XSpace, YStart ) --R2.1
|
||||
|
||||
self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,0,0,0)
|
||||
self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,0,0,0, self.__Enum.Formation.Trail )
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -699,7 +774,7 @@ end
|
||||
-- @return #AI_FORMATION
|
||||
function AI_FORMATION:onafterFormationStack( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace ) --R2.1
|
||||
|
||||
self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,YSpace,0,0)
|
||||
self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,YSpace,0,0, self.__Enum.Formation.Stack )
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -720,7 +795,7 @@ end
|
||||
-- @return #AI_FORMATION
|
||||
function AI_FORMATION:onafterFormationLeftLine( FollowGroupSet, From , Event , To, XStart, YStart, ZStart, ZSpace ) --R2.1
|
||||
|
||||
self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,0,YStart,0,ZStart,ZSpace)
|
||||
self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,0,YStart,0,-ZStart,-ZSpace, self.__Enum.Formation.LeftLine )
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -739,7 +814,7 @@ end
|
||||
-- @return #AI_FORMATION
|
||||
function AI_FORMATION:onafterFormationRightLine( FollowGroupSet, From , Event , To, XStart, YStart, ZStart, ZSpace ) --R2.1
|
||||
|
||||
self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,0,YStart,0,-ZStart,-ZSpace)
|
||||
self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,0,YStart,0,ZStart,ZSpace,self.__Enum.Formation.RightLine)
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -758,7 +833,7 @@ end
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
function AI_FORMATION:onafterFormationLeftWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, ZStart, ZSpace ) --R2.1
|
||||
|
||||
self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,0,ZStart,ZSpace)
|
||||
self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,0,-ZStart,-ZSpace,self.__Enum.Formation.LeftWing)
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -778,7 +853,7 @@ end
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
function AI_FORMATION:onafterFormationRightWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, ZStart, ZSpace ) --R2.1
|
||||
|
||||
self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,0,-ZStart,-ZSpace)
|
||||
self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,0,ZStart,ZSpace,self.__Enum.Formation.RightWing)
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -816,6 +891,7 @@ function AI_FORMATION:onafterFormationCenterWing( FollowGroupSet, From , Event ,
|
||||
local Vec3 = PointVec3:GetVec3()
|
||||
FollowGroup:SetState( self, "FormationVec3", Vec3 )
|
||||
i = i + 1
|
||||
FollowGroup:SetState( FollowGroup, "Formation", self.__Enum.Formation.Vic )
|
||||
end
|
||||
|
||||
return self
|
||||
@@ -875,6 +951,7 @@ function AI_FORMATION:onafterFormationBox( FollowGroupSet, From , Event , To, XS
|
||||
local Vec3 = PointVec3:GetVec3()
|
||||
FollowGroup:SetState( self, "FormationVec3", Vec3 )
|
||||
i = i + 1
|
||||
FollowGroup:SetState( FollowGroup, "Formation", self.__Enum.Formation.Box )
|
||||
end
|
||||
|
||||
return self
|
||||
@@ -893,17 +970,126 @@ function AI_FORMATION:SetFlightRandomization( FlightRandomization ) --R2.1
|
||||
end
|
||||
|
||||
|
||||
--- @param Follow#AI_FORMATION self
|
||||
function AI_FORMATION:onenterFollowing( FollowGroupSet ) --R2.1
|
||||
self:F( )
|
||||
--- Gets your escorts to flight mode.
|
||||
-- @param #AI_FORMATION self
|
||||
-- @param Wrapper.Group#GROUP FollowGroup FollowGroup.
|
||||
-- @return #AI_FORMATION
|
||||
function AI_FORMATION:GetFlightMode( FollowGroup )
|
||||
|
||||
if FollowGroup then
|
||||
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
|
||||
FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Mission )
|
||||
end
|
||||
|
||||
|
||||
return FollowGroup:GetState( FollowGroup, "Mode" )
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- This sets your escorts to fly a mission.
|
||||
-- @param #AI_FORMATION self
|
||||
-- @param Wrapper.Group#GROUP FollowGroup FollowGroup.
|
||||
-- @return #AI_FORMATION
|
||||
function AI_FORMATION:SetFlightModeMission( FollowGroup )
|
||||
|
||||
if FollowGroup then
|
||||
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
|
||||
FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Mission )
|
||||
else
|
||||
self.EscortGroupSet:ForSomeGroupAlive(
|
||||
--- @param Core.Group#GROUP EscortGroup
|
||||
function( FollowGroup )
|
||||
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
|
||||
FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Mission )
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- This sets your escorts to execute an attack.
|
||||
-- @param #AI_FORMATION self
|
||||
-- @param Wrapper.Group#GROUP FollowGroup FollowGroup.
|
||||
-- @return #AI_FORMATION
|
||||
function AI_FORMATION:SetFlightModeAttack( FollowGroup )
|
||||
|
||||
if FollowGroup then
|
||||
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
|
||||
FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Attack )
|
||||
else
|
||||
self.EscortGroupSet:ForSomeGroupAlive(
|
||||
--- @param Core.Group#GROUP EscortGroup
|
||||
function( FollowGroup )
|
||||
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
|
||||
FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Attack )
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- This sets your escorts to fly in a formation.
|
||||
-- @param #AI_FORMATION self
|
||||
-- @param Wrapper.Group#GROUP FollowGroup FollowGroup.
|
||||
-- @return #AI_FORMATION
|
||||
function AI_FORMATION:SetFlightModeFormation( FollowGroup )
|
||||
|
||||
if FollowGroup then
|
||||
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
|
||||
FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Formation )
|
||||
else
|
||||
self.EscortGroupSet:ForSomeGroupAlive(
|
||||
--- @param Core.Group#GROUP EscortGroup
|
||||
function( FollowGroup )
|
||||
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
|
||||
FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Formation )
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
--- Stop function. Formation will not be updated any more.
|
||||
-- @param #AI_FORMATION self
|
||||
-- @param Core.Set#SET_GROUP FollowGroupSet The following set of groups.
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @pram #string To The to state.
|
||||
function AI_FORMATION:onafterStop(FollowGroupSet, From, Event, To) --R2.1
|
||||
self:E("Stopping formation.")
|
||||
end
|
||||
|
||||
--- Follow event fuction. Check if coming from state "stopped". If so the transition is rejected.
|
||||
-- @param #AI_FORMATION self
|
||||
-- @param Core.Set#SET_GROUP FollowGroupSet The following set of groups.
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @pram #string To The to state.
|
||||
function AI_FORMATION:onbeforeFollow( FollowGroupSet, From, Event, To ) --R2.1
|
||||
if From=="Stopped" then
|
||||
return false -- Deny transition.
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- @param #AI_FORMATION self
|
||||
function AI_FORMATION:onenterFollowing( FollowGroupSet ) --R2.1
|
||||
|
||||
self:T( { self.FollowUnit.UnitName, self.FollowUnit:IsAlive() } )
|
||||
if self.FollowUnit:IsAlive() then
|
||||
|
||||
local ClientUnit = self.FollowUnit
|
||||
|
||||
self:T( {ClientUnit.UnitName } )
|
||||
|
||||
local CT1, CT2, CV1, CV2
|
||||
CT1 = ClientUnit:GetState( self, "CT1" )
|
||||
|
||||
@@ -920,120 +1106,139 @@ function AI_FORMATION:onenterFollowing( FollowGroupSet ) --R2.1
|
||||
ClientUnit:SetState( self, "CV1", CV2 )
|
||||
end
|
||||
|
||||
FollowGroupSet:ForEachGroup(
|
||||
FollowGroupSet:ForEachGroupAlive(
|
||||
--- @param Wrapper.Group#GROUP FollowGroup
|
||||
-- @param Wrapper.Unit#UNIT ClientUnit
|
||||
function( FollowGroup, Formation, ClientUnit, CT1, CV1, CT2, CV2 )
|
||||
|
||||
if FollowGroup:GetState( FollowGroup, "Mode" ) == self.__Enum.Mode.Formation then
|
||||
|
||||
FollowGroup:OptionROTEvadeFire()
|
||||
FollowGroup:OptionROEReturnFire()
|
||||
self:T({Mode=FollowGroup:GetState( FollowGroup, "Mode" )})
|
||||
|
||||
local GroupUnit = FollowGroup:GetUnit( 1 )
|
||||
local FollowFormation = FollowGroup:GetState( self, "FormationVec3" )
|
||||
if FollowFormation then
|
||||
local FollowDistance = FollowFormation.x
|
||||
|
||||
local GT1 = GroupUnit:GetState( self, "GT1" )
|
||||
|
||||
if CT1 == nil or CT1 == 0 or GT1 == nil or GT1 == 0 then
|
||||
GroupUnit:SetState( self, "GV1", GroupUnit:GetPointVec3() )
|
||||
GroupUnit:SetState( self, "GT1", timer.getTime() )
|
||||
else
|
||||
local CD = ( ( CV2.x - CV1.x )^2 + ( CV2.y - CV1.y )^2 + ( CV2.z - CV1.z )^2 ) ^ 0.5
|
||||
local CT = CT2 - CT1
|
||||
|
||||
local CS = ( 3600 / CT ) * ( CD / 1000 ) / 3.6
|
||||
|
||||
local CDv = { x = CV2.x - CV1.x, y = CV2.y - CV1.y, z = CV2.z - CV1.z }
|
||||
local Ca = math.atan2( CDv.x, CDv.z )
|
||||
|
||||
FollowGroup:OptionROTEvadeFire()
|
||||
FollowGroup:OptionROEReturnFire()
|
||||
|
||||
local GroupUnit = FollowGroup:GetUnit( 1 )
|
||||
local FollowFormation = FollowGroup:GetState( self, "FormationVec3" )
|
||||
if FollowFormation then
|
||||
local FollowDistance = FollowFormation.x
|
||||
|
||||
local GT1 = GroupUnit:GetState( self, "GT1" )
|
||||
local GT2 = timer.getTime()
|
||||
local GV1 = GroupUnit:GetState( self, "GV1" )
|
||||
local GV2 = GroupUnit:GetPointVec3()
|
||||
GV2:AddX( math.random( -Formation.FlightRandomization / 2, Formation.FlightRandomization / 2 ) )
|
||||
GV2:AddY( math.random( -Formation.FlightRandomization / 2, Formation.FlightRandomization / 2 ) )
|
||||
GV2:AddZ( math.random( -Formation.FlightRandomization / 2, Formation.FlightRandomization / 2 ) )
|
||||
GroupUnit:SetState( self, "GT1", GT2 )
|
||||
GroupUnit:SetState( self, "GV1", GV2 )
|
||||
|
||||
|
||||
local GD = ( ( GV2.x - GV1.x )^2 + ( GV2.y - GV1.y )^2 + ( GV2.z - GV1.z )^2 ) ^ 0.5
|
||||
local GT = GT2 - GT1
|
||||
|
||||
|
||||
if CT1 == nil or CT1 == 0 or GT1 == nil or GT1 == 0 then
|
||||
GroupUnit:SetState( self, "GV1", GroupUnit:GetPointVec3() )
|
||||
GroupUnit:SetState( self, "GT1", timer.getTime() )
|
||||
else
|
||||
local CD = ( ( CV2.x - CV1.x )^2 + ( CV2.y - CV1.y )^2 + ( CV2.z - CV1.z )^2 ) ^ 0.5
|
||||
local CT = CT2 - CT1
|
||||
|
||||
local CS = ( 3600 / CT ) * ( CD / 1000 ) / 3.6
|
||||
|
||||
local CDv = { x = CV2.x - CV1.x, y = CV2.y - CV1.y, z = CV2.z - CV1.z }
|
||||
local Ca = math.atan2( CDv.x, CDv.z )
|
||||
|
||||
local GT1 = GroupUnit:GetState( self, "GT1" )
|
||||
local GT2 = timer.getTime()
|
||||
local GV1 = GroupUnit:GetState( self, "GV1" )
|
||||
local GV2 = GroupUnit:GetPointVec3()
|
||||
GV2:AddX( math.random( -Formation.FlightRandomization / 2, Formation.FlightRandomization / 2 ) )
|
||||
GV2:AddY( math.random( -Formation.FlightRandomization / 2, Formation.FlightRandomization / 2 ) )
|
||||
GV2:AddZ( math.random( -Formation.FlightRandomization / 2, Formation.FlightRandomization / 2 ) )
|
||||
GroupUnit:SetState( self, "GT1", GT2 )
|
||||
GroupUnit:SetState( self, "GV1", GV2 )
|
||||
|
||||
|
||||
local GD = ( ( GV2.x - GV1.x )^2 + ( GV2.y - GV1.y )^2 + ( GV2.z - GV1.z )^2 ) ^ 0.5
|
||||
local GT = GT2 - GT1
|
||||
|
||||
|
||||
-- Calculate the distance
|
||||
local GDv = { x = GV2.x - CV1.x, y = GV2.y - CV1.y, z = GV2.z - CV1.z }
|
||||
local Alpha_T = math.atan2( GDv.x, GDv.z ) - math.atan2( CDv.x, CDv.z )
|
||||
local Alpha_R = ( Alpha_T < 0 ) and Alpha_T + 2 * math.pi or Alpha_T
|
||||
local Position = math.cos( Alpha_R )
|
||||
local GD = ( ( GDv.x )^2 + ( GDv.z )^2 ) ^ 0.5
|
||||
local Distance = GD * Position + - CS * 0.5
|
||||
|
||||
-- Calculate the group direction vector
|
||||
local GV = { x = GV2.x - CV2.x, y = GV2.y - CV2.y, z = GV2.z - CV2.z }
|
||||
|
||||
-- Calculate GH2, GH2 with the same height as CV2.
|
||||
local GH2 = { x = GV2.x, y = CV2.y + FollowFormation.y, z = GV2.z }
|
||||
|
||||
-- Calculate the angle of GV to the orthonormal plane
|
||||
local alpha = math.atan2( GV.x, GV.z )
|
||||
|
||||
local GVx = FollowFormation.z * math.cos( Ca ) + FollowFormation.x * math.sin( Ca )
|
||||
local GVz = FollowFormation.x * math.cos( Ca ) - FollowFormation.z * math.sin( Ca )
|
||||
|
||||
|
||||
-- Now we calculate the intersecting vector between the circle around CV2 with radius FollowDistance and GH2.
|
||||
-- From the GeoGebra model: CVI = (x(CV2) + FollowDistance cos(alpha), y(GH2) + FollowDistance sin(alpha), z(CV2))
|
||||
local Inclination = ( Distance + FollowFormation.x ) / 10
|
||||
if Inclination < -30 then
|
||||
Inclination = - 30
|
||||
end
|
||||
local CVI = { x = CV2.x + CS * 10 * math.sin(Ca),
|
||||
y = GH2.y + Inclination, -- + FollowFormation.y,
|
||||
y = GH2.y,
|
||||
z = CV2.z + CS * 10 * math.cos(Ca),
|
||||
}
|
||||
|
||||
-- Calculate the direction vector DV of the escort group. We use CVI as the base and CV2 as the direction.
|
||||
local DV = { x = CV2.x - CVI.x, y = CV2.y - CVI.y, z = CV2.z - CVI.z }
|
||||
|
||||
-- We now calculate the unary direction vector DVu, so that we can multiply DVu with the speed, which is expressed in meters / s.
|
||||
-- We need to calculate this vector to predict the point the escort group needs to fly to according its speed.
|
||||
-- The distance of the destination point should be far enough not to have the aircraft starting to swipe left to right...
|
||||
local DVu = { x = DV.x / FollowDistance, y = DV.y, z = DV.z / FollowDistance }
|
||||
|
||||
-- Now we can calculate the group destination vector GDV.
|
||||
local GDV = { x = CVI.x, y = CVI.y, z = CVI.z }
|
||||
|
||||
local ADDx = FollowFormation.x * math.cos(alpha) - FollowFormation.z * math.sin(alpha)
|
||||
local ADDz = FollowFormation.z * math.cos(alpha) + FollowFormation.x * math.sin(alpha)
|
||||
|
||||
local GDV_Formation = {
|
||||
x = GDV.x - GVx,
|
||||
y = GDV.y,
|
||||
z = GDV.z - GVz
|
||||
}
|
||||
|
||||
if self.SmokeDirectionVector == true then
|
||||
trigger.action.smoke( GDV, trigger.smokeColor.Green )
|
||||
trigger.action.smoke( GDV_Formation, trigger.smokeColor.White )
|
||||
end
|
||||
|
||||
|
||||
|
||||
local Time = 120
|
||||
|
||||
local Speed = - ( Distance + FollowFormation.x ) / Time
|
||||
|
||||
-- Calculate the distance
|
||||
local GDv = { x = GV2.x - CV1.x, y = GV2.y - CV1.y, z = GV2.z - CV1.z }
|
||||
local Alpha_T = math.atan2( GDv.x, GDv.z ) - math.atan2( CDv.x, CDv.z )
|
||||
local Alpha_R = ( Alpha_T < 0 ) and Alpha_T + 2 * math.pi or Alpha_T
|
||||
local Position = math.cos( Alpha_R )
|
||||
local GD = ( ( GDv.x )^2 + ( GDv.z )^2 ) ^ 0.5
|
||||
local Distance = GD * Position + - CS * 0.5
|
||||
|
||||
-- Calculate the group direction vector
|
||||
local GV = { x = GV2.x - CV2.x, y = GV2.y - CV2.y, z = GV2.z - CV2.z }
|
||||
|
||||
-- Calculate GH2, GH2 with the same height as CV2.
|
||||
local GH2 = { x = GV2.x, y = CV2.y + FollowFormation.y, z = GV2.z }
|
||||
|
||||
-- Calculate the angle of GV to the orthonormal plane
|
||||
local alpha = math.atan2( GV.x, GV.z )
|
||||
|
||||
local GVx = FollowFormation.z * math.cos( Ca ) + FollowFormation.x * math.sin( Ca )
|
||||
local GVz = FollowFormation.x * math.cos( Ca ) - FollowFormation.z * math.sin( Ca )
|
||||
if Distance > -10000 then
|
||||
Speed = - ( Distance + FollowFormation.x ) / 60
|
||||
end
|
||||
|
||||
if Distance > -2500 then
|
||||
Speed = - ( Distance + FollowFormation.x ) / 20
|
||||
end
|
||||
|
||||
local GS = Speed + CS
|
||||
|
||||
|
||||
-- Now we calculate the intersecting vector between the circle around CV2 with radius FollowDistance and GH2.
|
||||
-- From the GeoGebra model: CVI = (x(CV2) + FollowDistance cos(alpha), y(GH2) + FollowDistance sin(alpha), z(CV2))
|
||||
local CVI = { x = CV2.x + CS * 10 * math.sin(Ca),
|
||||
y = GH2.y - ( Distance + FollowFormation.x ) / 5, -- + FollowFormation.y,
|
||||
z = CV2.z + CS * 10 * math.cos(Ca),
|
||||
}
|
||||
|
||||
-- Calculate the direction vector DV of the escort group. We use CVI as the base and CV2 as the direction.
|
||||
local DV = { x = CV2.x - CVI.x, y = CV2.y - CVI.y, z = CV2.z - CVI.z }
|
||||
|
||||
-- We now calculate the unary direction vector DVu, so that we can multiply DVu with the speed, which is expressed in meters / s.
|
||||
-- We need to calculate this vector to predict the point the escort group needs to fly to according its speed.
|
||||
-- The distance of the destination point should be far enough not to have the aircraft starting to swipe left to right...
|
||||
local DVu = { x = DV.x / FollowDistance, y = DV.y, z = DV.z / FollowDistance }
|
||||
|
||||
-- Now we can calculate the group destination vector GDV.
|
||||
local GDV = { x = CVI.x, y = CVI.y, z = CVI.z }
|
||||
|
||||
local ADDx = FollowFormation.x * math.cos(alpha) - FollowFormation.z * math.sin(alpha)
|
||||
local ADDz = FollowFormation.z * math.cos(alpha) + FollowFormation.x * math.sin(alpha)
|
||||
|
||||
local GDV_Formation = {
|
||||
x = GDV.x - GVx,
|
||||
y = GDV.y,
|
||||
z = GDV.z - GVz
|
||||
}
|
||||
|
||||
if self.SmokeDirectionVector == true then
|
||||
trigger.action.smoke( GDV, trigger.smokeColor.Green )
|
||||
trigger.action.smoke( GDV_Formation, trigger.smokeColor.White )
|
||||
self:F( { Distance = Distance, Speed = Speed, CS = CS, GS = GS } )
|
||||
|
||||
|
||||
-- Now route the escort to the desired point with the desired speed.
|
||||
FollowGroup:RouteToVec3( GDV_Formation, GS ) -- DCS models speed in Mps (Miles per second)
|
||||
end
|
||||
|
||||
|
||||
|
||||
local Time = 60
|
||||
|
||||
local Speed = - ( Distance + FollowFormation.x ) / Time
|
||||
local GS = Speed + CS
|
||||
if Speed < 0 then
|
||||
Speed = 0
|
||||
end
|
||||
|
||||
-- Now route the escort to the desired point with the desired speed.
|
||||
FollowGroup:RouteToVec3( GDV_Formation, GS ) -- DCS models speed in Mps (Miles per second)
|
||||
end
|
||||
end
|
||||
end,
|
||||
self, ClientUnit, CT1, CV1, CT2, CV2
|
||||
)
|
||||
|
||||
self:__Follow( -0.5 )
|
||||
|
||||
self:__Follow( -self.dtFollow )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -178,8 +178,8 @@ function AI_PATROL_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltit
|
||||
self.PatrolMinSpeed = PatrolMinSpeed
|
||||
self.PatrolMaxSpeed = PatrolMaxSpeed
|
||||
|
||||
-- defafult PatrolAltType to "RADIO" if not specified
|
||||
self.PatrolAltType = PatrolAltType or "RADIO"
|
||||
-- defafult PatrolAltType to "BARO" if not specified
|
||||
self.PatrolAltType = PatrolAltType or "BARO"
|
||||
|
||||
self:SetRefreshTimeInterval( 30 )
|
||||
|
||||
@@ -636,7 +636,7 @@ function AI_PATROL_ZONE:onafterStart( Controllable, From, Event, To )
|
||||
|
||||
self.Controllable:OnReSpawn(
|
||||
function( PatrolGroup )
|
||||
self:E( "ReSpawn" )
|
||||
self:T( "ReSpawn" )
|
||||
self:__Reset( 1 )
|
||||
self:__Route( 5 )
|
||||
end
|
||||
@@ -667,21 +667,27 @@ function AI_PATROL_ZONE:onafterDetect( Controllable, From, Event, To )
|
||||
if TargetObject and TargetObject:isExist() and TargetObject.id_ < 50000000 then
|
||||
|
||||
local TargetUnit = UNIT:Find( TargetObject )
|
||||
local TargetUnitName = TargetUnit:GetName()
|
||||
|
||||
if self.DetectionZone then
|
||||
if TargetUnit:IsInZone( self.DetectionZone ) then
|
||||
self:T( {"Detected ", TargetUnit } )
|
||||
-- Check that target is alive due to issue https://github.com/FlightControl-Master/MOOSE/issues/1234
|
||||
if TargetUnit and TargetUnit:IsAlive() then
|
||||
|
||||
local TargetUnitName = TargetUnit:GetName()
|
||||
|
||||
if self.DetectionZone then
|
||||
if TargetUnit:IsInZone( self.DetectionZone ) then
|
||||
self:T( {"Detected ", TargetUnit } )
|
||||
if self.DetectedUnits[TargetUnit] == nil then
|
||||
self.DetectedUnits[TargetUnit] = true
|
||||
end
|
||||
Detected = true
|
||||
end
|
||||
else
|
||||
if self.DetectedUnits[TargetUnit] == nil then
|
||||
self.DetectedUnits[TargetUnit] = true
|
||||
end
|
||||
Detected = true
|
||||
Detected = true
|
||||
end
|
||||
else
|
||||
if self.DetectedUnits[TargetUnit] == nil then
|
||||
self.DetectedUnits[TargetUnit] = true
|
||||
end
|
||||
Detected = true
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -735,7 +741,7 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
|
||||
-- This will make the plane fly immediately to the patrol zone.
|
||||
|
||||
if self.Controllable:InAir() == false then
|
||||
self:E( "Not in the air, finding route path within PatrolZone" )
|
||||
self:T( "Not in the air, finding route path within PatrolZone" )
|
||||
local CurrentVec2 = self.Controllable:GetVec2()
|
||||
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1).
|
||||
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
|
||||
@@ -750,7 +756,7 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
|
||||
)
|
||||
PatrolRoute[#PatrolRoute+1] = CurrentRoutePoint
|
||||
else
|
||||
self:E( "In the air, finding route path within PatrolZone" )
|
||||
self:T( "In the air, finding route path within PatrolZone" )
|
||||
local CurrentVec2 = self.Controllable:GetVec2()
|
||||
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1).
|
||||
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
|
||||
@@ -825,7 +831,7 @@ function AI_PATROL_ZONE:onafterStatus()
|
||||
|
||||
local Fuel = self.Controllable:GetFuelMin()
|
||||
if Fuel < self.PatrolFuelThresholdPercentage then
|
||||
self:E( self.Controllable:GetName() .. " is out of fuel:" .. Fuel .. ", RTB!" )
|
||||
self:I( 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 )
|
||||
@@ -839,7 +845,7 @@ function AI_PATROL_ZONE:onafterStatus()
|
||||
-- TODO: Check GROUP damage function.
|
||||
local Damage = self.Controllable:GetLife()
|
||||
if Damage <= self.PatrolDamageThreshold then
|
||||
self:E( self.Controllable:GetName() .. " is damaged:" .. Damage .. ", RTB!" )
|
||||
self:I( self.Controllable:GetName() .. " is damaged:" .. Damage .. ", RTB!" )
|
||||
RTB = true
|
||||
end
|
||||
|
||||
@@ -900,7 +906,6 @@ end
|
||||
function AI_PATROL_ZONE: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
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
--- **Actions** - ACT_ACCOUNT_ classes **account for** (detect, count & report) various DCS events occuring on @{Wrapper.Unit}s.
|
||||
--
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module Actions.Account
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
do -- ACT_ACCOUNT
|
||||
|
||||
|
||||
--- # @{#ACT_ACCOUNT} FSM class, extends @{Core.Fsm#FSM_PROCESS}
|
||||
--
|
||||
-- ## ACT_ACCOUNT state machine:
|
||||
--
|
||||
--
|
||||
-- ## ACT_ACCOUNT state machine:
|
||||
--
|
||||
-- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur.
|
||||
-- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below.
|
||||
-- Each derived class follows exactly the same process, using the same events and following the same state transitions,
|
||||
-- Each derived class follows exactly the same process, using the same events and following the same state transitions,
|
||||
-- but will have **different implementation behaviour** upon each event or state transition.
|
||||
--
|
||||
-- ### ACT_ACCOUNT States
|
||||
--
|
||||
--
|
||||
-- ### ACT_ACCOUNT States
|
||||
--
|
||||
-- * **Asigned**: The player is assigned.
|
||||
-- * **Waiting**: Waiting for an event.
|
||||
-- * **Report**: Reporting.
|
||||
-- * **Account**: Account for an event.
|
||||
-- * **Accounted**: All events have been accounted for, end of the process.
|
||||
-- * **Failed**: Failed the process.
|
||||
--
|
||||
-- ### ACT_ACCOUNT Events
|
||||
--
|
||||
--
|
||||
-- ### ACT_ACCOUNT Events
|
||||
--
|
||||
-- * **Start**: Start the process.
|
||||
-- * **Wait**: Wait for an event.
|
||||
-- * **Report**: Report the status of the accounting.
|
||||
@@ -36,32 +36,32 @@ do -- ACT_ACCOUNT
|
||||
-- * **More**: More targets.
|
||||
-- * **NoMore (*)**: No more targets.
|
||||
-- * **Fail (*)**: The action process has failed.
|
||||
--
|
||||
--
|
||||
-- (*) End states of the process.
|
||||
--
|
||||
--
|
||||
-- ### ACT_ACCOUNT state transition methods:
|
||||
--
|
||||
--
|
||||
-- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state.
|
||||
-- There are 2 moments when state transition methods will be called by the state machine:
|
||||
--
|
||||
-- * **Before** the state transition.
|
||||
-- The state transition method needs to start with the name **OnBefore + the name of the state**.
|
||||
--
|
||||
-- * **Before** the state transition.
|
||||
-- The state transition method needs to start with the name **OnBefore + the name of the state**.
|
||||
-- If the state transition method returns false, then the processing of the state transition will not be done!
|
||||
-- If you want to change the behaviour of the AIControllable at this event, return false,
|
||||
-- If you want to change the behaviour of the AIControllable at this event, return false,
|
||||
-- but then you'll need to specify your own logic using the AIControllable!
|
||||
--
|
||||
-- * **After** the state transition.
|
||||
-- The state transition method needs to start with the name **OnAfter + the name of the state**.
|
||||
--
|
||||
-- * **After** the state transition.
|
||||
-- The state transition method needs to start with the name **OnAfter + the name of the state**.
|
||||
-- These state transition methods need to provide a return value, which is specified at the function description.
|
||||
--
|
||||
--
|
||||
-- @type ACT_ACCOUNT
|
||||
-- @field Core.Set#SET_UNIT TargetSetUnit
|
||||
-- @extends Core.Fsm#FSM_PROCESS
|
||||
ACT_ACCOUNT = {
|
||||
ACT_ACCOUNT = {
|
||||
ClassName = "ACT_ACCOUNT",
|
||||
TargetSetUnit = nil,
|
||||
}
|
||||
|
||||
|
||||
--- Creates a new DESTROY process.
|
||||
-- @param #ACT_ACCOUNT self
|
||||
-- @return #ACT_ACCOUNT
|
||||
@@ -69,7 +69,7 @@ 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" )
|
||||
@@ -79,16 +79,16 @@ do -- ACT_ACCOUNT
|
||||
self:AddTransition( { "Account", "AccountForPlayer", "AccountForOther" }, "More", "Wait" )
|
||||
self:AddTransition( { "Account", "AccountForPlayer", "AccountForOther" }, "NoMore", "Accounted" )
|
||||
self:AddTransition( "*", "Fail", "Failed" )
|
||||
|
||||
|
||||
self:AddEndState( "Failed" )
|
||||
|
||||
self:SetStartState( "Assigned" )
|
||||
|
||||
|
||||
self:SetStartState( "Assigned" )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Process Events
|
||||
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ACCOUNT self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
@@ -104,7 +104,7 @@ do -- ACT_ACCOUNT
|
||||
self:__Wait( 1 )
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ACCOUNT self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
@@ -112,17 +112,17 @@ do -- ACT_ACCOUNT
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ACCOUNT:onenterWaiting( ProcessUnit, From, Event, To )
|
||||
|
||||
|
||||
if self.DisplayCount >= self.DisplayInterval then
|
||||
self:Report()
|
||||
self.DisplayCount = 1
|
||||
else
|
||||
self.DisplayCount = self.DisplayCount + 1
|
||||
end
|
||||
|
||||
|
||||
return true -- Process always the event.
|
||||
end
|
||||
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ACCOUNT self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
@@ -130,30 +130,30 @@ do -- ACT_ACCOUNT
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ACCOUNT:onafterEvent( ProcessUnit, From, Event, To, Event )
|
||||
|
||||
|
||||
self:__NoMore( 1 )
|
||||
end
|
||||
|
||||
|
||||
end -- ACT_ACCOUNT
|
||||
|
||||
do -- ACT_ACCOUNT_DEADS
|
||||
|
||||
--- # @{#ACT_ACCOUNT_DEADS} FSM class, extends @{Core.Fsm.Account#ACT_ACCOUNT}
|
||||
--
|
||||
--
|
||||
-- The ACT_ACCOUNT_DEADS class accounts (detects, counts and reports) successful kills of DCS units.
|
||||
-- The process is given a @{Set} of units that will be tracked upon successful destruction.
|
||||
-- The process will end after each target has been successfully destroyed.
|
||||
-- Each successful dead will trigger an Account state transition that can be scored, modified or administered.
|
||||
--
|
||||
--
|
||||
--
|
||||
--
|
||||
-- ## ACT_ACCOUNT_DEADS constructor:
|
||||
--
|
||||
--
|
||||
-- * @{#ACT_ACCOUNT_DEADS.New}(): Creates a new ACT_ACCOUNT_DEADS object.
|
||||
--
|
||||
--
|
||||
-- @type ACT_ACCOUNT_DEADS
|
||||
-- @field Core.Set#SET_UNIT TargetSetUnit
|
||||
-- @extends #ACT_ACCOUNT
|
||||
ACT_ACCOUNT_DEADS = {
|
||||
ACT_ACCOUNT_DEADS = {
|
||||
ClassName = "ACT_ACCOUNT_DEADS",
|
||||
}
|
||||
|
||||
@@ -165,24 +165,24 @@ do -- ACT_ACCOUNT_DEADS
|
||||
function ACT_ACCOUNT_DEADS:New()
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, ACT_ACCOUNT:New() ) -- #ACT_ACCOUNT_DEADS
|
||||
|
||||
|
||||
self.DisplayInterval = 30
|
||||
self.DisplayCount = 30
|
||||
self.DisplayMessage = true
|
||||
self.DisplayTime = 10 -- 10 seconds is the default
|
||||
self.DisplayCategory = "HQ" -- Targets is the default display category
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
function ACT_ACCOUNT_DEADS:Init( FsmAccount )
|
||||
|
||||
self.Task = self:GetTask()
|
||||
|
||||
self.Task = self:GetTask()
|
||||
self.TaskName = self.Task:GetName()
|
||||
end
|
||||
|
||||
--- Process Events
|
||||
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ACCOUNT_DEADS self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
@@ -190,12 +190,12 @@ do -- ACT_ACCOUNT_DEADS
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ACCOUNT_DEADS:onenterReport( ProcessUnit, Task, From, Event, To )
|
||||
|
||||
|
||||
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.Unit#UNIT ProcessUnit
|
||||
@@ -206,7 +206,7 @@ do -- ACT_ACCOUNT_DEADS
|
||||
-- @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 } )
|
||||
|
||||
|
||||
if Task.TargetSetUnit:FindUnit( EventData.IniUnitName ) then
|
||||
local PlayerName = ProcessUnit:GetPlayerName()
|
||||
local PlayerHit = self.PlayerHits and self.PlayerHits[EventData.IniUnitName]
|
||||
@@ -228,14 +228,14 @@ do -- ACT_ACCOUNT_DEADS
|
||||
-- @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 )
|
||||
|
||||
@@ -256,10 +256,10 @@ do -- ACT_ACCOUNT_DEADS
|
||||
-- @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 )
|
||||
|
||||
@@ -270,9 +270,9 @@ do -- ACT_ACCOUNT_DEADS
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- DCS Events
|
||||
|
||||
|
||||
--- @param #ACT_ACCOUNT_DEADS self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function ACT_ACCOUNT_DEADS:OnEventHit( EventData )
|
||||
@@ -282,8 +282,8 @@ do -- ACT_ACCOUNT_DEADS
|
||||
self.PlayerHits = self.PlayerHits or {}
|
||||
self.PlayerHits[EventData.TgtDCSUnitName] = EventData.IniPlayerName
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- @param #ACT_ACCOUNT_DEADS self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function ACT_ACCOUNT_DEADS:onfuncEventDead( EventData )
|
||||
@@ -295,7 +295,7 @@ do -- ACT_ACCOUNT_DEADS
|
||||
end
|
||||
|
||||
--- DCS Events
|
||||
|
||||
|
||||
--- @param #ACT_ACCOUNT_DEADS self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function ACT_ACCOUNT_DEADS:onfuncEventCrash( EventData )
|
||||
|
||||
@@ -1,82 +1,82 @@
|
||||
--- (SP) (MP) (FSM) Accept or reject process for player (task) assignments.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- # @{#ACT_ASSIGN} FSM template class, extends @{Core.Fsm#FSM_PROCESS}
|
||||
--
|
||||
--
|
||||
-- ## ACT_ASSIGN state machine:
|
||||
--
|
||||
--
|
||||
-- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur.
|
||||
-- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below.
|
||||
-- Each derived class follows exactly the same process, using the same events and following the same state transitions,
|
||||
-- Each derived class follows exactly the same process, using the same events and following the same state transitions,
|
||||
-- but will have **different implementation behaviour** upon each event or state transition.
|
||||
--
|
||||
--
|
||||
-- ### ACT_ASSIGN **Events**:
|
||||
--
|
||||
--
|
||||
-- These are the events defined in this class:
|
||||
--
|
||||
--
|
||||
-- * **Start**: Start the tasking acceptance process.
|
||||
-- * **Assign**: Assign the task.
|
||||
-- * **Reject**: Reject the task..
|
||||
--
|
||||
--
|
||||
-- ### ACT_ASSIGN **Event methods**:
|
||||
--
|
||||
--
|
||||
-- Event methods are available (dynamically allocated by the state machine), that accomodate for state transitions occurring in the process.
|
||||
-- There are two types of event methods, which you can use to influence the normal mechanisms in the state machine:
|
||||
--
|
||||
--
|
||||
-- * **Immediate**: The event method has exactly the name of the event.
|
||||
-- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed.
|
||||
--
|
||||
-- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed.
|
||||
--
|
||||
-- ### ACT_ASSIGN **States**:
|
||||
--
|
||||
--
|
||||
-- * **UnAssigned**: The player has not accepted the task.
|
||||
-- * **Assigned (*)**: The player has accepted the task.
|
||||
-- * **Rejected (*)**: The player has not accepted the task.
|
||||
-- * **Waiting**: The process is awaiting player feedback.
|
||||
-- * **Failed (*)**: The process has failed.
|
||||
--
|
||||
--
|
||||
-- (*) End states of the process.
|
||||
--
|
||||
--
|
||||
-- ### ACT_ASSIGN state transition methods:
|
||||
--
|
||||
--
|
||||
-- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state.
|
||||
-- There are 2 moments when state transition methods will be called by the state machine:
|
||||
--
|
||||
-- * **Before** the state transition.
|
||||
-- The state transition method needs to start with the name **OnBefore + the name of the state**.
|
||||
--
|
||||
-- * **Before** the state transition.
|
||||
-- The state transition method needs to start with the name **OnBefore + the name of the state**.
|
||||
-- If the state transition method returns false, then the processing of the state transition will not be done!
|
||||
-- If you want to change the behaviour of the AIControllable at this event, return false,
|
||||
-- If you want to change the behaviour of the AIControllable at this event, return false,
|
||||
-- but then you'll need to specify your own logic using the AIControllable!
|
||||
--
|
||||
-- * **After** the state transition.
|
||||
-- The state transition method needs to start with the name **OnAfter + the name of the state**.
|
||||
--
|
||||
-- * **After** the state transition.
|
||||
-- The state transition method needs to start with the name **OnAfter + the name of the state**.
|
||||
-- These state transition methods need to provide a return value, which is specified at the function description.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- # 1) @{#ACT_ASSIGN_ACCEPT} class, extends @{Core.Fsm.Assign#ACT_ASSIGN}
|
||||
--
|
||||
--
|
||||
-- The ACT_ASSIGN_ACCEPT class accepts by default a task for a player. No player intervention is allowed to reject the task.
|
||||
--
|
||||
--
|
||||
-- ## 1.1) ACT_ASSIGN_ACCEPT constructor:
|
||||
--
|
||||
--
|
||||
-- * @{#ACT_ASSIGN_ACCEPT.New}(): Creates a new ACT_ASSIGN_ACCEPT object.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- # 2) @{#ACT_ASSIGN_MENU_ACCEPT} class, extends @{Core.Fsm.Assign#ACT_ASSIGN}
|
||||
--
|
||||
--
|
||||
-- The ACT_ASSIGN_MENU_ACCEPT class accepts a task when the player accepts the task through an added menu option.
|
||||
-- This assignment type is useful to conditionally allow the player to choose whether or not he would accept the task.
|
||||
-- The assignment type also allows to reject the task.
|
||||
--
|
||||
--
|
||||
-- ## 2.1) ACT_ASSIGN_MENU_ACCEPT constructor:
|
||||
-- -----------------------------------------
|
||||
--
|
||||
--
|
||||
-- * @{#ACT_ASSIGN_MENU_ACCEPT.New}(): Creates a new ACT_ASSIGN_MENU_ACCEPT object.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- @module Actions.Assign
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
@@ -89,11 +89,11 @@ do -- ACT_ASSIGN
|
||||
-- @field Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @field Core.Zone#ZONE_BASE TargetZone
|
||||
-- @extends Core.Fsm#FSM_PROCESS
|
||||
ACT_ASSIGN = {
|
||||
ACT_ASSIGN = {
|
||||
ClassName = "ACT_ASSIGN",
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
--- Creates a new task assignment state machine. The process will accept the task by default, no player intervention accepted.
|
||||
-- @param #ACT_ASSIGN self
|
||||
-- @return #ACT_ASSIGN The task acceptance process.
|
||||
@@ -106,16 +106,16 @@ do -- ACT_ASSIGN
|
||||
self:AddTransition( "Waiting", "Assign", "Assigned" )
|
||||
self:AddTransition( "Waiting", "Reject", "Rejected" )
|
||||
self:AddTransition( "*", "Fail", "Failed" )
|
||||
|
||||
|
||||
self:AddEndState( "Assigned" )
|
||||
self:AddEndState( "Rejected" )
|
||||
self:AddEndState( "Failed" )
|
||||
|
||||
self:SetStartState( "UnAssigned" )
|
||||
|
||||
|
||||
self:SetStartState( "UnAssigned" )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
end -- ACT_ASSIGN
|
||||
|
||||
|
||||
@@ -128,26 +128,26 @@ do -- ACT_ASSIGN_ACCEPT
|
||||
-- @field Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @field Core.Zone#ZONE_BASE TargetZone
|
||||
-- @extends #ACT_ASSIGN
|
||||
ACT_ASSIGN_ACCEPT = {
|
||||
ACT_ASSIGN_ACCEPT = {
|
||||
ClassName = "ACT_ASSIGN_ACCEPT",
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
--- Creates a new task assignment state machine. The process will accept the task by default, no player intervention accepted.
|
||||
-- @param #ACT_ASSIGN_ACCEPT self
|
||||
-- @param #string TaskBriefing
|
||||
function ACT_ASSIGN_ACCEPT:New( TaskBriefing )
|
||||
|
||||
|
||||
local self = BASE:Inherit( self, ACT_ASSIGN:New() ) -- #ACT_ASSIGN_ACCEPT
|
||||
|
||||
self.TaskBriefing = TaskBriefing
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function ACT_ASSIGN_ACCEPT:Init( FsmAssign )
|
||||
|
||||
self.TaskBriefing = FsmAssign.TaskBriefing
|
||||
|
||||
self.TaskBriefing = FsmAssign.TaskBriefing
|
||||
end
|
||||
|
||||
--- StateMachine callback function
|
||||
@@ -157,8 +157,8 @@ do -- ACT_ASSIGN_ACCEPT
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ASSIGN_ACCEPT:onafterStart( ProcessUnit, Task, From, Event, To )
|
||||
|
||||
self:__Assign( 1 )
|
||||
|
||||
self:__Assign( 1 )
|
||||
end
|
||||
|
||||
--- StateMachine callback function
|
||||
@@ -167,11 +167,11 @@ do -- ACT_ASSIGN_ACCEPT
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ASSIGN_ACCEPT:onenterAssigned( ProcessUnit, Task, From, Event, To )
|
||||
|
||||
function ACT_ASSIGN_ACCEPT:onenterAssigned( ProcessUnit, Task, From, Event, To, TaskGroup )
|
||||
|
||||
self.Task:Assign( ProcessUnit, ProcessUnit:GetPlayerName() )
|
||||
end
|
||||
|
||||
|
||||
end -- ACT_ASSIGN_ACCEPT
|
||||
|
||||
|
||||
@@ -183,7 +183,7 @@ do -- ACT_ASSIGN_MENU_ACCEPT
|
||||
-- @field Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @field Core.Zone#ZONE_BASE TargetZone
|
||||
-- @extends #ACT_ASSIGN
|
||||
ACT_ASSIGN_MENU_ACCEPT = {
|
||||
ACT_ASSIGN_MENU_ACCEPT = {
|
||||
ClassName = "ACT_ASSIGN_MENU_ACCEPT",
|
||||
}
|
||||
|
||||
@@ -197,7 +197,7 @@ do -- ACT_ASSIGN_MENU_ACCEPT
|
||||
local self = BASE:Inherit( self, ACT_ASSIGN:New() ) -- #ACT_ASSIGN_MENU_ACCEPT
|
||||
|
||||
self.TaskBriefing = TaskBriefing
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -207,12 +207,12 @@ do -- ACT_ASSIGN_MENU_ACCEPT
|
||||
-- @param #string TaskBriefing
|
||||
-- @return #ACT_ASSIGN_MENU_ACCEPT self
|
||||
function ACT_ASSIGN_MENU_ACCEPT:Init( TaskBriefing )
|
||||
|
||||
|
||||
self.TaskBriefing = TaskBriefing
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ASSIGN_MENU_ACCEPT self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
@@ -222,30 +222,30 @@ do -- ACT_ASSIGN_MENU_ACCEPT
|
||||
function ACT_ASSIGN_MENU_ACCEPT:onafterStart( ProcessUnit, Task, From, Event, To )
|
||||
|
||||
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 TaskGroup = 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:__Reject( 120, TaskGroup )
|
||||
end
|
||||
|
||||
|
||||
--- Menu function.
|
||||
-- @param #ACT_ASSIGN_MENU_ACCEPT self
|
||||
function ACT_ASSIGN_MENU_ACCEPT:MenuAssign( TaskGroup )
|
||||
|
||||
|
||||
self:__Assign( -1, TaskGroup )
|
||||
end
|
||||
|
||||
|
||||
--- Menu function.
|
||||
-- @param #ACT_ASSIGN_MENU_ACCEPT self
|
||||
function ACT_ASSIGN_MENU_ACCEPT:MenuReject( TaskGroup )
|
||||
|
||||
|
||||
self:__Reject( -1, TaskGroup )
|
||||
end
|
||||
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ASSIGN_MENU_ACCEPT self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
@@ -253,10 +253,10 @@ do -- ACT_ASSIGN_MENU_ACCEPT
|
||||
-- @param #string From
|
||||
-- @param #string 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.Unit#UNIT ProcessUnit
|
||||
@@ -265,7 +265,7 @@ do -- ACT_ASSIGN_MENU_ACCEPT
|
||||
-- @param #string 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
|
||||
@@ -279,7 +279,7 @@ do -- ACT_ASSIGN_MENU_ACCEPT
|
||||
-- @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
|
||||
|
||||
@@ -1,65 +1,65 @@
|
||||
--- (SP) (MP) (FSM) Route AI or players through waypoints or to zones.
|
||||
--
|
||||
--
|
||||
-- ## ACT_ASSIST state machine:
|
||||
--
|
||||
--
|
||||
-- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur.
|
||||
-- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below.
|
||||
-- Each derived class follows exactly the same process, using the same events and following the same state transitions,
|
||||
-- Each derived class follows exactly the same process, using the same events and following the same state transitions,
|
||||
-- but will have **different implementation behaviour** upon each event or state transition.
|
||||
--
|
||||
--
|
||||
-- ### ACT_ASSIST **Events**:
|
||||
--
|
||||
--
|
||||
-- These are the events defined in this class:
|
||||
--
|
||||
--
|
||||
-- * **Start**: The process is started.
|
||||
-- * **Next**: The process is smoking the targets in the given zone.
|
||||
--
|
||||
--
|
||||
-- ### ACT_ASSIST **Event methods**:
|
||||
--
|
||||
--
|
||||
-- Event methods are available (dynamically allocated by the state machine), that accomodate for state transitions occurring in the process.
|
||||
-- There are two types of event methods, which you can use to influence the normal mechanisms in the state machine:
|
||||
--
|
||||
--
|
||||
-- * **Immediate**: The event method has exactly the name of the event.
|
||||
-- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed.
|
||||
--
|
||||
-- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed.
|
||||
--
|
||||
-- ### ACT_ASSIST **States**:
|
||||
--
|
||||
--
|
||||
-- * **None**: The controllable did not receive route commands.
|
||||
-- * **AwaitSmoke (*)**: The process is awaiting to smoke the targets in the zone.
|
||||
-- * **Smoking (*)**: The process is smoking the targets in the zone.
|
||||
-- * **Failed (*)**: The process has failed.
|
||||
--
|
||||
--
|
||||
-- (*) End states of the process.
|
||||
--
|
||||
--
|
||||
-- ### ACT_ASSIST state transition methods:
|
||||
--
|
||||
--
|
||||
-- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state.
|
||||
-- There are 2 moments when state transition methods will be called by the state machine:
|
||||
--
|
||||
-- * **Before** the state transition.
|
||||
-- The state transition method needs to start with the name **OnBefore + the name of the state**.
|
||||
--
|
||||
-- * **Before** the state transition.
|
||||
-- The state transition method needs to start with the name **OnBefore + the name of the state**.
|
||||
-- If the state transition method returns false, then the processing of the state transition will not be done!
|
||||
-- If you want to change the behaviour of the AIControllable at this event, return false,
|
||||
-- If you want to change the behaviour of the AIControllable at this event, return false,
|
||||
-- but then you'll need to specify your own logic using the AIControllable!
|
||||
--
|
||||
-- * **After** the state transition.
|
||||
-- The state transition method needs to start with the name **OnAfter + the name of the state**.
|
||||
--
|
||||
-- * **After** the state transition.
|
||||
-- The state transition method needs to start with the name **OnAfter + the name of the state**.
|
||||
-- These state transition methods need to provide a return value, which is specified at the function description.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- # 1) @{#ACT_ASSIST_SMOKE_TARGETS_ZONE} class, extends @{Core.Fsm.Route#ACT_ASSIST}
|
||||
--
|
||||
--
|
||||
-- The ACT_ASSIST_SMOKE_TARGETS_ZONE class implements the core functions to smoke targets in a @{Zone}.
|
||||
-- The targets are smoked within a certain range around each target, simulating a realistic smoking behaviour.
|
||||
-- The targets are smoked within a certain range around each target, simulating a realistic smoking behaviour.
|
||||
-- At random intervals, a new target is smoked.
|
||||
--
|
||||
--
|
||||
-- # 1.1) ACT_ASSIST_SMOKE_TARGETS_ZONE constructor:
|
||||
--
|
||||
--
|
||||
-- * @{#ACT_ASSIST_SMOKE_TARGETS_ZONE.New}(): Creates a new ACT_ASSIST_SMOKE_TARGETS_ZONE object.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- @module Actions.Assist
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
@@ -69,7 +69,7 @@ do -- ACT_ASSIST
|
||||
--- ACT_ASSIST class
|
||||
-- @type ACT_ASSIST
|
||||
-- @extends Core.Fsm#FSM_PROCESS
|
||||
ACT_ASSIST = {
|
||||
ACT_ASSIST = {
|
||||
ClassName = "ACT_ASSIST",
|
||||
}
|
||||
|
||||
@@ -86,15 +86,15 @@ do -- ACT_ASSIST
|
||||
self:AddTransition( "Smoking", "Next", "AwaitSmoke" )
|
||||
self:AddTransition( "*", "Stop", "Success" )
|
||||
self:AddTransition( "*", "Fail", "Failed" )
|
||||
|
||||
|
||||
self:AddEndState( "Failed" )
|
||||
self:AddEndState( "Success" )
|
||||
|
||||
self:SetStartState( "None" )
|
||||
|
||||
self:SetStartState( "None" )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Task Events
|
||||
|
||||
--- StateMachine callback function
|
||||
@@ -104,17 +104,17 @@ do -- ACT_ASSIST
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ASSIST:onafterStart( ProcessUnit, From, Event, To )
|
||||
|
||||
|
||||
local ProcessGroup = ProcessUnit:GetGroup()
|
||||
local MissionMenu = self:GetMission():GetMenu( ProcessGroup )
|
||||
|
||||
|
||||
local function MenuSmoke( MenuParam )
|
||||
local self = MenuParam.self
|
||||
local SmokeColor = MenuParam.SmokeColor
|
||||
self.SmokeColor = SmokeColor
|
||||
self:__Next( 1 )
|
||||
end
|
||||
|
||||
|
||||
self.Menu = MENU_GROUP:New( ProcessGroup, "Target acquisition", MissionMenu )
|
||||
self.MenuSmokeBlue = MENU_GROUP_COMMAND:New( ProcessGroup, "Drop blue smoke on targets", self.Menu, MenuSmoke, { self = self, SmokeColor = SMOKECOLOR.Blue } )
|
||||
self.MenuSmokeGreen = MENU_GROUP_COMMAND:New( ProcessGroup, "Drop green smoke on targets", self.Menu, MenuSmoke, { self = self, SmokeColor = SMOKECOLOR.Green } )
|
||||
@@ -130,10 +130,10 @@ do -- ACT_ASSIST
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ASSIST:onafterStop( ProcessUnit, From, Event, To )
|
||||
|
||||
|
||||
self.Menu:Remove() -- When stopped, remove the menus
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
do -- ACT_ASSIST_SMOKE_TARGETS_ZONE
|
||||
@@ -143,17 +143,17 @@ do -- ACT_ASSIST_SMOKE_TARGETS_ZONE
|
||||
-- @field Core.Set#SET_UNIT TargetSetUnit
|
||||
-- @field Core.Zone#ZONE_BASE TargetZone
|
||||
-- @extends #ACT_ASSIST
|
||||
ACT_ASSIST_SMOKE_TARGETS_ZONE = {
|
||||
ACT_ASSIST_SMOKE_TARGETS_ZONE = {
|
||||
ClassName = "ACT_ASSIST_SMOKE_TARGETS_ZONE",
|
||||
}
|
||||
|
||||
|
||||
-- function ACT_ASSIST_SMOKE_TARGETS_ZONE:_Destructor()
|
||||
-- self:E("_Destructor")
|
||||
--
|
||||
--
|
||||
-- self.Menu:Remove()
|
||||
-- self:EventRemoveAll()
|
||||
-- end
|
||||
|
||||
|
||||
--- Creates a new target smoking 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_ASSIST_SMOKE_TARGETS_ZONE self
|
||||
-- @param Core.Set#SET_UNIT TargetSetUnit
|
||||
@@ -163,29 +163,29 @@ do -- ACT_ASSIST_SMOKE_TARGETS_ZONE
|
||||
|
||||
self.TargetSetUnit = TargetSetUnit
|
||||
self.TargetZone = TargetZone
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function ACT_ASSIST_SMOKE_TARGETS_ZONE:Init( FsmSmoke )
|
||||
|
||||
|
||||
self.TargetSetUnit = FsmSmoke.TargetSetUnit
|
||||
self.TargetZone = FsmSmoke.TargetZone
|
||||
end
|
||||
|
||||
|
||||
--- Creates a new target smoking 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_ASSIST_SMOKE_TARGETS_ZONE self
|
||||
-- @param Core.Set#SET_UNIT TargetSetUnit
|
||||
-- @param Core.Zone#ZONE_BASE TargetZone
|
||||
-- @return #ACT_ASSIST_SMOKE_TARGETS_ZONE self
|
||||
function ACT_ASSIST_SMOKE_TARGETS_ZONE:Init( TargetSetUnit, TargetZone )
|
||||
|
||||
|
||||
self.TargetSetUnit = TargetSetUnit
|
||||
self.TargetZone = TargetZone
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ASSIST_SMOKE_TARGETS_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
|
||||
@@ -193,7 +193,7 @@ do -- ACT_ASSIST_SMOKE_TARGETS_ZONE
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ASSIST_SMOKE_TARGETS_ZONE:onenterSmoking( ProcessUnit, From, Event, To )
|
||||
|
||||
|
||||
self.TargetSetUnit:ForEachUnit(
|
||||
--- @param Wrapper.Unit#UNIT SmokeUnit
|
||||
function( SmokeUnit )
|
||||
@@ -203,12 +203,12 @@ do -- ACT_ASSIST_SMOKE_TARGETS_ZONE
|
||||
if SmokeUnit:IsAlive() then
|
||||
SmokeUnit:Smoke( self.SmokeColor, 150 )
|
||||
end
|
||||
end, {}, math.random( 10, 60 )
|
||||
end, {}, math.random( 10, 60 )
|
||||
)
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
--- (SP) (MP) (FSM) Route AI or players through waypoints or to zones.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- # @{#ACT_ROUTE} FSM class, extends @{Core.Fsm#FSM_PROCESS}
|
||||
--
|
||||
--
|
||||
-- ## ACT_ROUTE state machine:
|
||||
--
|
||||
--
|
||||
-- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur.
|
||||
-- All derived classes from this class will start with the class name, followed by a \_. See the relevant derived class descriptions below.
|
||||
-- Each derived class follows exactly the same process, using the same events and following the same state transitions,
|
||||
-- Each derived class follows exactly the same process, using the same events and following the same state transitions,
|
||||
-- but will have **different implementation behaviour** upon each event or state transition.
|
||||
--
|
||||
--
|
||||
-- ### ACT_ROUTE **Events**:
|
||||
--
|
||||
--
|
||||
-- These are the events defined in this class:
|
||||
--
|
||||
--
|
||||
-- * **Start**: The process is started. The process will go into the Report state.
|
||||
-- * **Report**: The process is reporting to the player the route to be followed.
|
||||
-- * **Route**: The process is routing the controllable.
|
||||
@@ -22,56 +22,56 @@
|
||||
-- * **Arrive**: The controllable has arrived at a route point.
|
||||
-- * **More**: There are more route points that need to be followed. The process will go back into the Report state.
|
||||
-- * **NoMore**: There are no more route points that need to be followed. The process will go into the Success state.
|
||||
--
|
||||
--
|
||||
-- ### ACT_ROUTE **Event methods**:
|
||||
--
|
||||
--
|
||||
-- Event methods are available (dynamically allocated by the state machine), that accomodate for state transitions occurring in the process.
|
||||
-- There are two types of event methods, which you can use to influence the normal mechanisms in the state machine:
|
||||
--
|
||||
--
|
||||
-- * **Immediate**: The event method has exactly the name of the event.
|
||||
-- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed.
|
||||
--
|
||||
-- * **Delayed**: The event method starts with a __ + the name of the event. The first parameter of the event method is a number value, expressing the delay in seconds when the event will be executed.
|
||||
--
|
||||
-- ### ACT_ROUTE **States**:
|
||||
--
|
||||
--
|
||||
-- * **None**: The controllable did not receive route commands.
|
||||
-- * **Arrived (*)**: The controllable has arrived at a route point.
|
||||
-- * **Aborted (*)**: The controllable has aborted the route path.
|
||||
-- * **Routing**: The controllable is understay to the route point.
|
||||
-- * **Pausing**: The process is pausing the routing. AI air will go into hover, AI ground will stop moving. Players can fly around.
|
||||
-- * **Success (*)**: All route points were reached.
|
||||
-- * **Success (*)**: All route points were reached.
|
||||
-- * **Failed (*)**: The process has failed.
|
||||
--
|
||||
--
|
||||
-- (*) End states of the process.
|
||||
--
|
||||
--
|
||||
-- ### ACT_ROUTE state transition methods:
|
||||
--
|
||||
--
|
||||
-- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state.
|
||||
-- There are 2 moments when state transition methods will be called by the state machine:
|
||||
--
|
||||
-- * **Before** the state transition.
|
||||
-- The state transition method needs to start with the name **OnBefore + the name of the state**.
|
||||
--
|
||||
-- * **Before** the state transition.
|
||||
-- The state transition method needs to start with the name **OnBefore + the name of the state**.
|
||||
-- If the state transition method returns false, then the processing of the state transition will not be done!
|
||||
-- If you want to change the behaviour of the AIControllable at this event, return false,
|
||||
-- If you want to change the behaviour of the AIControllable at this event, return false,
|
||||
-- but then you'll need to specify your own logic using the AIControllable!
|
||||
--
|
||||
-- * **After** the state transition.
|
||||
-- The state transition method needs to start with the name **OnAfter + the name of the state**.
|
||||
--
|
||||
-- * **After** the state transition.
|
||||
-- The state transition method needs to start with the name **OnAfter + the name of the state**.
|
||||
-- These state transition methods need to provide a return value, which is specified at the function description.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- # 1) @{#ACT_ROUTE_ZONE} class, extends @{Core.Fsm.Route#ACT_ROUTE}
|
||||
--
|
||||
--
|
||||
-- The ACT_ROUTE_ZONE class implements the core functions to route an AIR @{Wrapper.Controllable} player @{Wrapper.Unit} to a @{Zone}.
|
||||
-- The player receives on perioding times messages with the coordinates of the route to follow.
|
||||
-- The player receives on perioding times messages with the coordinates of the route to follow.
|
||||
-- Upon arrival at the zone, a confirmation of arrival is sent, and the process will be ended.
|
||||
--
|
||||
--
|
||||
-- # 1.1) ACT_ROUTE_ZONE constructor:
|
||||
--
|
||||
--
|
||||
-- * @{#ACT_ROUTE_ZONE.New}(): Creates a new ACT_ROUTE_ZONE object.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- @module Actions.Route
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
@@ -85,11 +85,11 @@ do -- ACT_ROUTE
|
||||
-- @field Core.Zone#ZONE_BASE Zone
|
||||
-- @field Core.Point#COORDINATE Coordinate
|
||||
-- @extends Core.Fsm#FSM_PROCESS
|
||||
ACT_ROUTE = {
|
||||
ACT_ROUTE = {
|
||||
ClassName = "ACT_ROUTE",
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
--- Creates a new routing state machine. The process will route a CLIENT to a ZONE until the CLIENT is within that ZONE.
|
||||
-- @param #ACT_ROUTE self
|
||||
-- @return #ACT_ROUTE self
|
||||
@@ -97,7 +97,7 @@ do -- ACT_ROUTE
|
||||
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, FSM_PROCESS:New( "ACT_ROUTE" ) ) -- Core.Fsm#FSM_PROCESS
|
||||
|
||||
|
||||
self:AddTransition( "*", "Reset", "None" )
|
||||
self:AddTransition( "None", "Start", "Routing" )
|
||||
self:AddTransition( "*", "Report", "*" )
|
||||
@@ -109,23 +109,23 @@ do -- ACT_ROUTE
|
||||
self:AddTransition( "*", "Fail", "Failed" )
|
||||
self:AddTransition( "", "", "" )
|
||||
self:AddTransition( "", "", "" )
|
||||
|
||||
|
||||
self:AddEndState( "Arrived" )
|
||||
self:AddEndState( "Failed" )
|
||||
self:AddEndState( "Cancelled" )
|
||||
|
||||
|
||||
self:SetStartState( "None" )
|
||||
|
||||
self:SetRouteMode( "C" )
|
||||
|
||||
|
||||
self:SetRouteMode( "C" )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set a Cancel Menu item.
|
||||
-- @param #ACT_ROUTE self
|
||||
-- @return #ACT_ROUTE
|
||||
function ACT_ROUTE:SetMenuCancel( MenuGroup, MenuText, ParentMenu, MenuTime, MenuTag )
|
||||
|
||||
|
||||
self.CancelMenuGroupCommand = MENU_GROUP_COMMAND:New(
|
||||
MenuGroup,
|
||||
MenuText,
|
||||
@@ -135,47 +135,47 @@ do -- ACT_ROUTE
|
||||
):SetTime( MenuTime ):SetTag( MenuTag )
|
||||
|
||||
ParentMenu:SetTime( MenuTime )
|
||||
|
||||
|
||||
ParentMenu:Remove( MenuTime, MenuTag )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set the route mode.
|
||||
-- There are 2 route modes supported:
|
||||
--
|
||||
--
|
||||
-- * SetRouteMode( "B" ): Route mode is Bearing and Range.
|
||||
-- * SetRouteMode( "C" ): Route mode is LL or MGRS according coordinate system setup.
|
||||
--
|
||||
--
|
||||
-- @param #ACT_ROUTE self
|
||||
-- @return #ACT_ROUTE
|
||||
function ACT_ROUTE:SetRouteMode( RouteMode )
|
||||
|
||||
|
||||
self.RouteMode = RouteMode
|
||||
|
||||
return self
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- 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( Controllable )
|
||||
|
||||
|
||||
local RouteText = ""
|
||||
|
||||
local Coordinate = nil -- Core.Point#COORDINATE
|
||||
|
||||
|
||||
if self.Coordinate then
|
||||
Coordinate = self.Coordinate
|
||||
end
|
||||
|
||||
|
||||
if self.Zone then
|
||||
Coordinate = self.Zone:GetPointVec3( self.Altitude )
|
||||
Coordinate:SetHeading( self.Heading )
|
||||
end
|
||||
|
||||
|
||||
|
||||
local 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()
|
||||
@@ -209,7 +209,7 @@ do -- ACT_ROUTE
|
||||
return RouteText
|
||||
end
|
||||
|
||||
|
||||
|
||||
function ACT_ROUTE:MenuCancel()
|
||||
self:F("Cancelled")
|
||||
self.CancelMenuGroupCommand:Remove()
|
||||
@@ -225,11 +225,11 @@ do -- ACT_ROUTE
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ROUTE:onafterStart( ProcessUnit, From, Event, To )
|
||||
|
||||
|
||||
|
||||
self:__Route( 1 )
|
||||
end
|
||||
|
||||
|
||||
--- Check if the controllable has arrived.
|
||||
-- @param #ACT_ROUTE self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
@@ -237,7 +237,7 @@ do -- ACT_ROUTE
|
||||
function ACT_ROUTE:onfuncHasArrived( ProcessUnit )
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ROUTE self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
@@ -245,7 +245,7 @@ do -- ACT_ROUTE
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ROUTE:onbeforeRoute( ProcessUnit, From, Event, To )
|
||||
|
||||
|
||||
if ProcessUnit:IsAlive() then
|
||||
local HasArrived = self:onfuncHasArrived( ProcessUnit ) -- Polymorphic
|
||||
if self.DisplayCount >= self.DisplayInterval then
|
||||
@@ -257,18 +257,18 @@ do -- ACT_ROUTE
|
||||
else
|
||||
self.DisplayCount = self.DisplayCount + 1
|
||||
end
|
||||
|
||||
|
||||
if HasArrived then
|
||||
self:__Arrive( 1 )
|
||||
else
|
||||
self:__Route( 1 )
|
||||
end
|
||||
|
||||
|
||||
return HasArrived -- if false, then the event will not be executed...
|
||||
end
|
||||
|
||||
|
||||
return false
|
||||
|
||||
|
||||
end
|
||||
|
||||
end -- ACT_ROUTE
|
||||
@@ -280,12 +280,12 @@ do -- ACT_ROUTE_POINT
|
||||
-- @type ACT_ROUTE_POINT
|
||||
-- @field Tasking.Task#TASK TASK
|
||||
-- @extends #ACT_ROUTE
|
||||
ACT_ROUTE_POINT = {
|
||||
ACT_ROUTE_POINT = {
|
||||
ClassName = "ACT_ROUTE_POINT",
|
||||
}
|
||||
|
||||
|
||||
--- Creates a new routing state machine.
|
||||
--- Creates a new routing state machine.
|
||||
-- The task will route a controllable to a Coordinate until the controllable is within the Range.
|
||||
-- @param #ACT_ROUTE_POINT self
|
||||
-- @param Core.Point#COORDINATE The Coordinate to Target.
|
||||
@@ -296,29 +296,29 @@ do -- ACT_ROUTE_POINT
|
||||
|
||||
self.Coordinate = Coordinate
|
||||
self.Range = Range or 0
|
||||
|
||||
|
||||
self.DisplayInterval = 30
|
||||
self.DisplayCount = 30
|
||||
self.DisplayMessage = true
|
||||
self.DisplayTime = 10 -- 10 seconds is the default
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Creates a new routing state machine.
|
||||
|
||||
--- Creates a new routing state machine.
|
||||
-- The task will route a controllable to a Coordinate until the controllable is within the Range.
|
||||
-- @param #ACT_ROUTE_POINT self
|
||||
function ACT_ROUTE_POINT:Init( FsmRoute )
|
||||
|
||||
|
||||
self.Coordinate = FsmRoute.Coordinate
|
||||
self.Range = FsmRoute.Range or 0
|
||||
|
||||
|
||||
self.DisplayInterval = 30
|
||||
self.DisplayCount = 30
|
||||
self.DisplayMessage = true
|
||||
self.DisplayTime = 10 -- 10 seconds is the default
|
||||
self:SetStartState("None")
|
||||
end
|
||||
end
|
||||
|
||||
--- Set Coordinate
|
||||
-- @param #ACT_ROUTE_POINT self
|
||||
@@ -326,7 +326,7 @@ do -- ACT_ROUTE_POINT
|
||||
function ACT_ROUTE_POINT:SetCoordinate( Coordinate )
|
||||
self:F2( { Coordinate } )
|
||||
self.Coordinate = Coordinate
|
||||
end
|
||||
end
|
||||
|
||||
--- Get Coordinate
|
||||
-- @param #ACT_ROUTE_POINT self
|
||||
@@ -334,7 +334,7 @@ do -- ACT_ROUTE_POINT
|
||||
function ACT_ROUTE_POINT:GetCoordinate()
|
||||
self:F2( { self.Coordinate } )
|
||||
return self.Coordinate
|
||||
end
|
||||
end
|
||||
|
||||
--- Set Range around Coordinate
|
||||
-- @param #ACT_ROUTE_POINT self
|
||||
@@ -342,16 +342,16 @@ do -- ACT_ROUTE_POINT
|
||||
function ACT_ROUTE_POINT:SetRange( Range )
|
||||
self:F2( { Range } )
|
||||
self.Range = Range or 10000
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Get Range around Coordinate
|
||||
-- @param #ACT_ROUTE_POINT self
|
||||
-- @return #number The Range to consider the arrival. Default is 10000 meters.
|
||||
function ACT_ROUTE_POINT:GetRange()
|
||||
self:F2( { self.Range } )
|
||||
return self.Range
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Method override to check if the controllable has arrived.
|
||||
-- @param #ACT_ROUTE_POINT self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
@@ -360,7 +360,7 @@ do -- ACT_ROUTE_POINT
|
||||
|
||||
if ProcessUnit:IsAlive() then
|
||||
local Distance = self.Coordinate:Get2DDistance( ProcessUnit:GetCoordinate() )
|
||||
|
||||
|
||||
if Distance <= self.Range then
|
||||
local RouteText = "Task \"" .. self:GetTask():GetName() .. "\", you have arrived."
|
||||
self:GetCommandCenter():MessageTypeToGroup( RouteText, ProcessUnit:GetGroup(), MESSAGE.Type.Information )
|
||||
@@ -370,9 +370,9 @@ do -- ACT_ROUTE_POINT
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
--- Task Events
|
||||
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ROUTE_POINT self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
@@ -380,9 +380,9 @@ do -- ACT_ROUTE_POINT
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ROUTE_POINT:onafterReport( ProcessUnit, From, Event, To )
|
||||
|
||||
|
||||
local RouteText = "Task \"" .. self:GetTask():GetName() .. "\", " .. self:GetRouteText( ProcessUnit )
|
||||
|
||||
|
||||
self:GetCommandCenter():MessageTypeToGroup( RouteText, ProcessUnit:GetGroup(), MESSAGE.Type.Update )
|
||||
end
|
||||
|
||||
@@ -397,7 +397,7 @@ do -- ACT_ROUTE_ZONE
|
||||
-- @field Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @field Core.Zone#ZONE_BASE Zone
|
||||
-- @extends #ACT_ROUTE
|
||||
ACT_ROUTE_ZONE = {
|
||||
ACT_ROUTE_ZONE = {
|
||||
ClassName = "ACT_ROUTE_ZONE",
|
||||
}
|
||||
|
||||
@@ -409,25 +409,25 @@ do -- ACT_ROUTE_ZONE
|
||||
local self = BASE:Inherit( self, ACT_ROUTE:New() ) -- #ACT_ROUTE_ZONE
|
||||
|
||||
self.Zone = Zone
|
||||
|
||||
|
||||
self.DisplayInterval = 30
|
||||
self.DisplayCount = 30
|
||||
self.DisplayMessage = true
|
||||
self.DisplayTime = 10 -- 10 seconds is the default
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
function ACT_ROUTE_ZONE:Init( FsmRoute )
|
||||
|
||||
|
||||
self.Zone = FsmRoute.Zone
|
||||
|
||||
|
||||
self.DisplayInterval = 30
|
||||
self.DisplayCount = 30
|
||||
self.DisplayMessage = true
|
||||
self.DisplayTime = 10 -- 10 seconds is the default
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Set Zone
|
||||
-- @param #ACT_ROUTE_ZONE self
|
||||
-- @param Core.Zone#ZONE_BASE Zone The Zone object where to route to.
|
||||
@@ -437,14 +437,14 @@ do -- ACT_ROUTE_ZONE
|
||||
self.Zone = Zone
|
||||
self.Altitude = Altitude
|
||||
self.Heading = Heading
|
||||
end
|
||||
end
|
||||
|
||||
--- Get Zone
|
||||
-- @param #ACT_ROUTE_ZONE self
|
||||
-- @return Core.Zone#ZONE_BASE Zone The Zone object where to route to.
|
||||
function ACT_ROUTE_ZONE:GetZone()
|
||||
return self.Zone
|
||||
end
|
||||
return self.Zone
|
||||
end
|
||||
|
||||
--- Method override to check if the controllable has arrived.
|
||||
-- @param #ACT_ROUTE self
|
||||
@@ -459,9 +459,9 @@ do -- ACT_ROUTE_ZONE
|
||||
|
||||
return ProcessUnit:IsInZone( self.Zone )
|
||||
end
|
||||
|
||||
|
||||
--- Task Events
|
||||
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ROUTE_ZONE self
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
@@ -470,7 +470,7 @@ do -- ACT_ROUTE_ZONE
|
||||
-- @param #string To
|
||||
function ACT_ROUTE_ZONE:onafterReport( ProcessUnit, From, Event, To )
|
||||
self:F( { ProcessUnit = ProcessUnit } )
|
||||
|
||||
|
||||
local RouteText = "Task \"" .. self:GetTask():GetName() .. "\", " .. self:GetRouteText( ProcessUnit )
|
||||
self:GetCommandCenter():MessageTypeToGroup( RouteText, ProcessUnit:GetGroup(), MESSAGE.Type.Update )
|
||||
end
|
||||
|
||||
@@ -872,7 +872,7 @@ do -- CARGO
|
||||
--self:F( { CargoObjectVec2 = self.CargoObject:GetVec2() } )
|
||||
--self:F( { PointVec2 = PointVec2:GetVec2() } )
|
||||
local Distance = Coordinate:Get2DDistance( self.CargoObject:GetCoordinate() )
|
||||
--self:F( Distance )
|
||||
--self:F( { Distance = Distance, NearRadius = NearRadius or "nil" } )
|
||||
|
||||
if Distance <= NearRadius then
|
||||
--self:F( { PointVec2 = PointVec2, NearRadius = NearRadius, IsNear = true } )
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
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.
|
||||
-- @extends Cargo.Cargo#CARGO_REPORTABLE
|
||||
|
||||
--- Defines a cargo that is represented by a @{Wrapper.Group} object within the simulator.
|
||||
-- The cargo can be Loaded, UnLoaded, Boarded, UnBoarded to and from Carriers.
|
||||
@@ -51,7 +51,7 @@ do -- CARGO_GROUP
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @param Wrapper.Group#GROUP CargoGroup Group to be transported as cargo.
|
||||
-- @param #string Type Cargo type, e.g. "Infantry". This is the type used in SET_CARGO:New():FilterTypes("Infantry") to define the valid cargo groups of the set.
|
||||
-- @param #string Name Some user defined name of the cargo group.
|
||||
-- @param #string Name A user defined name of the cargo group. This name CAN be the same as the group object but can also have a different name. This name MUST be unique!
|
||||
-- @param #number LoadRadius (optional) Distance in meters until which a cargo is loaded into the carrier. Cargo outside this radius has to be routed by other means to within the radius to be loaded.
|
||||
-- @param #number NearRadius (optional) Once the units are within this radius of the carrier, they are actually loaded, i.e. disappear from the scene.
|
||||
-- @return #CARGO_GROUP Cargo group object.
|
||||
@@ -291,29 +291,29 @@ do -- CARGO_GROUP
|
||||
|
||||
end
|
||||
|
||||
--- Enter Boarding State.
|
||||
--- After Board Event.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Wrapper.Unit#UNIT CargoCarrier
|
||||
-- @param #number NearRadius If distance is smaller than this number, cargo is loaded into the carrier.
|
||||
function CARGO_GROUP:onenterBoarding( From, Event, To, CargoCarrier, NearRadius, ... )
|
||||
function CARGO_GROUP:onafterBoard( From, Event, To, CargoCarrier, NearRadius, ... )
|
||||
self:F( { CargoCarrier.UnitName, From, Event, To, NearRadius = NearRadius } )
|
||||
|
||||
NearRadius = NearRadius or self.NearRadius
|
||||
|
||||
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
|
||||
-- For each Cargo object within the CARGO_GROUPED, route each object to the CargoLoadPointVec2
|
||||
self.CargoSet:ForEach(
|
||||
function( Cargo, ... )
|
||||
self:F( { "Board Unit", Cargo:GetName( ), Cargo:IsDestroyed(), Cargo.CargoObject:IsAlive() } )
|
||||
local CargoGroup = Cargo.CargoObject --Wrapper.Group#GROUP
|
||||
CargoGroup:OptionAlarmStateGreen()
|
||||
Cargo:__Board( 1, CargoCarrier, NearRadius, ... )
|
||||
end, ...
|
||||
)
|
||||
|
||||
self:__Boarding( -1, CargoCarrier, NearRadius, ... )
|
||||
|
||||
end
|
||||
|
||||
@@ -323,13 +323,15 @@ do -- CARGO_GROUP
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Wrapper.Unit#UNIT CargoCarrier
|
||||
function CARGO_GROUP:onenterLoaded( From, Event, To, CargoCarrier, ... )
|
||||
function CARGO_GROUP:onafterLoad( 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 )
|
||||
if not Cargo:IsDestroyed() then
|
||||
Cargo:Load( CargoCarrier )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -360,7 +362,7 @@ do -- CARGO_GROUP
|
||||
--self:T( { Cargo:GetName(), Cargo.current } )
|
||||
|
||||
|
||||
if not Cargo:is( "Loaded" )
|
||||
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
|
||||
@@ -400,7 +402,7 @@ do -- CARGO_GROUP
|
||||
-- @param #string To
|
||||
-- @param Core.Point#POINT_VEC2 ToPointVec2
|
||||
-- @param #number NearRadius If distance is smaller than this number, cargo is loaded into the carrier.
|
||||
function CARGO_GROUP:onenterUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... )
|
||||
function CARGO_GROUP:onafterUnBoard( From, Event, To, ToPointVec2, NearRadius, ... )
|
||||
self:F( {From, Event, To, ToPointVec2, NearRadius } )
|
||||
|
||||
NearRadius = NearRadius or 25
|
||||
@@ -443,7 +445,7 @@ do -- CARGO_GROUP
|
||||
-- @param #string To
|
||||
-- @param Core.Point#POINT_VEC2 ToPointVec2
|
||||
-- @param #number NearRadius If distance is smaller than this number, cargo is loaded into the carrier.
|
||||
function CARGO_GROUP:onleaveUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... )
|
||||
function CARGO_GROUP:onafterUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... )
|
||||
--self:F( { From, Event, To, ToPointVec2, NearRadius } )
|
||||
|
||||
--local NearRadius = NearRadius or 25
|
||||
@@ -464,7 +466,7 @@ do -- CARGO_GROUP
|
||||
end
|
||||
|
||||
if UnBoarded then
|
||||
return true
|
||||
self:__UnLoad( 1, ToPointVec2, ... )
|
||||
else
|
||||
self:__UnBoarding( 1, ToPointVec2, NearRadius, ... )
|
||||
end
|
||||
@@ -474,30 +476,13 @@ do -- CARGO_GROUP
|
||||
|
||||
end
|
||||
|
||||
--- UnBoard Event.
|
||||
-- @param #CARGO_GROUP self
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Core.Point#POINT_VEC2 ToPointVec2
|
||||
-- @param #number NearRadius If distance is smaller than this number, cargo is loaded into the carrier.
|
||||
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 #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Core.Point#POINT_VEC2
|
||||
function CARGO_GROUP:onenterUnLoaded( From, Event, To, ToPointVec2, ... )
|
||||
function CARGO_GROUP:onafterUnLoad( From, Event, To, ToPointVec2, ... )
|
||||
--self:F( { From, Event, To, ToPointVec2 } )
|
||||
|
||||
if From == "Loaded" then
|
||||
@@ -507,7 +492,7 @@ do -- CARGO_GROUP
|
||||
function( Cargo )
|
||||
--Cargo:UnLoad( ToPointVec2 )
|
||||
local RandomVec2=ToPointVec2:GetRandomPointVec2InRadius(20, 10)
|
||||
Cargo:UnLoad( RandomVec2 )
|
||||
Cargo:UnBoard( RandomVec2 )
|
||||
end
|
||||
)
|
||||
|
||||
|
||||
@@ -231,48 +231,49 @@ do -- CARGO_UNIT
|
||||
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 NearRadius is given, then use the given NearRadius, otherwise calculate the NearRadius
|
||||
-- based upon the Carrier bounding radius, which is calculated from the bounding rectangle on the Y axis.
|
||||
local NearRadius = NearRadius or CargoCarrier:GetBoundingRadius() + 5
|
||||
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:F({Unit=self.CargoObject:GetName()})
|
||||
|
||||
-- A cargo unit can only be boarded if it is not dead
|
||||
|
||||
-- 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 NearRadius is given, then use the given NearRadius, otherwise calculate the NearRadius
|
||||
-- based upon the Carrier bounding radius, which is calculated from the bounding rectangle on the Y axis.
|
||||
local NearRadius = NearRadius or CargoCarrier:GetBoundingRadius() + 5
|
||||
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 = 0
|
||||
|
||||
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 Speed = 90
|
||||
local Angle = 180
|
||||
local Distance = 5
|
||||
local PointStartVec2 = self.CargoObject:GetPointVec2()
|
||||
|
||||
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( -5, CargoCarrier, NearRadius, ... )
|
||||
self.RunCount = 0
|
||||
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( -5, CargoCarrier, NearRadius, ... )
|
||||
self.RunCount = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -286,50 +287,53 @@ do -- CARGO_UNIT
|
||||
function CARGO_UNIT:onafterBoarding( From, Event, To, CargoCarrier, NearRadius, ... )
|
||||
self:F( { From, Event, To, CargoCarrier:GetName(), NearRadius = NearRadius } )
|
||||
|
||||
self:F( { IsAlive=self.CargoObject:IsAlive() } )
|
||||
|
||||
if CargoCarrier and CargoCarrier:IsAlive() and self.CargoObject and self.CargoObject:IsAlive() then
|
||||
if (CargoCarrier:IsAir() and not CargoCarrier:InAir()) or true then
|
||||
local NearRadius = NearRadius or CargoCarrier:GetBoundingRadius( NearRadius ) + 5
|
||||
if self:IsNear( CargoCarrier:GetPointVec2(), NearRadius ) then
|
||||
self:__Load( 1, CargoCarrier, ... )
|
||||
else
|
||||
if self:IsNear( CargoCarrier:GetPointVec2(), 20 ) then
|
||||
self:__Boarding( -1, CargoCarrier, NearRadius, ... )
|
||||
self.RunCount = self.RunCount + 1
|
||||
if CargoCarrier and CargoCarrier:IsAlive() then -- and self.CargoObject and self.CargoObject:IsAlive() then
|
||||
if (CargoCarrier:IsAir() and not CargoCarrier:InAir()) or true then
|
||||
local NearRadius = NearRadius or CargoCarrier:GetBoundingRadius( NearRadius ) + 5
|
||||
if self:IsNear( CargoCarrier:GetPointVec2(), NearRadius ) then
|
||||
self:__Load( -1, CargoCarrier, ... )
|
||||
else
|
||||
self:__Boarding( -5, CargoCarrier, NearRadius, ... )
|
||||
self.RunCount = self.RunCount + 5
|
||||
end
|
||||
if self.RunCount >= 40 then
|
||||
self.RunCount = 0
|
||||
local Speed = 90
|
||||
local Angle = 180
|
||||
local Distance = 5
|
||||
if self:IsNear( CargoCarrier:GetPointVec2(), 20 ) then
|
||||
self:__Boarding( -1, CargoCarrier, NearRadius, ... )
|
||||
self.RunCount = self.RunCount + 1
|
||||
else
|
||||
self:__Boarding( -2, CargoCarrier, NearRadius, ... )
|
||||
self.RunCount = self.RunCount + 2
|
||||
end
|
||||
if self.RunCount >= 40 then
|
||||
self.RunCount = 0
|
||||
local Speed = 90
|
||||
local Angle = 180
|
||||
local Distance = 0
|
||||
|
||||
--self:F({Unit=self.CargoObject:GetName()})
|
||||
|
||||
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 )
|
||||
|
||||
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 )
|
||||
-- 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.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
|
||||
@@ -337,26 +341,6 @@ do -- CARGO_UNIT
|
||||
end
|
||||
|
||||
|
||||
--- Enter Boarding State.
|
||||
-- @param #CARGO_UNIT self
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Wrapper.Unit#UNIT CargoCarrier
|
||||
-- @param #number NearRadius Default 25 m.
|
||||
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
|
||||
|
||||
if From == "UnLoaded" or From == "Boarding" then
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Loaded State.
|
||||
-- @param #CARGO_UNIT self
|
||||
-- @param #string Event
|
||||
@@ -368,10 +352,11 @@ do -- CARGO_UNIT
|
||||
|
||||
self.CargoCarrier = CargoCarrier
|
||||
|
||||
-- Only destroy the CargoObject is if there is a CargoObject (packages don't have CargoObjects).
|
||||
--self:F({Unit=self.CargoObject:GetName()})
|
||||
|
||||
-- Only destroy the CargoObject if there is a CargoObject (packages don't have CargoObjects).
|
||||
if self.CargoObject then
|
||||
self:T("Destroying")
|
||||
self.CargoObject:Destroy()
|
||||
self.CargoObject:Destroy( false )
|
||||
--self.CargoObject:ReSpawnAt( COORDINATE:NewFromVec2( {x=0,y=0} ), 0 )
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,22 @@
|
||||
--- **Core** -- BASE forms **the basis of the MOOSE framework**. Each class within the MOOSE framework derives from BASE.
|
||||
--- **Core** - The base class within the framework.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
-- * The construction and inheritance of MOOSE classes.
|
||||
-- * The class naming and numbering system.
|
||||
-- * The class hierarchy search system.
|
||||
-- * The tracing of information or objects during mission execution for debuggin purposes.
|
||||
-- * The subscription to DCS events for event handling in MOOSE objects.
|
||||
-- * Object inspection.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- All classes within the MOOSE framework are derived from the BASE class.
|
||||
-- Note: The BASE class is an abstract class and is not meant to be used directly.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions:
|
||||
@@ -23,24 +41,14 @@ local _ClassID = 0
|
||||
-- @field ClassID The ID number of the class.
|
||||
-- @field ClassNameAndID The name of the class concatenated with the ID number of the class.
|
||||
|
||||
--- All classes within the MOOSE framework are derived from the BASE class.
|
||||
--
|
||||
-- BASE provides facilities for :
|
||||
--
|
||||
-- * The construction and inheritance of MOOSE classes.
|
||||
-- * The class naming and numbering system.
|
||||
-- * The class hierarchy search system.
|
||||
-- * The tracing of information or objects during mission execution for debuggin purposes.
|
||||
-- * The subscription to DCS events for event handling in MOOSE objects.
|
||||
--
|
||||
-- Note: The BASE class is an abstract class and is not meant to be used directly.
|
||||
--
|
||||
-- ## 1.1) BASE constructor
|
||||
--- BASE class
|
||||
--
|
||||
-- # 1. BASE constructor.
|
||||
--
|
||||
-- Any class derived from BASE, will use the @{Core.Base#BASE.New} constructor embedded in the @{Core.Base#BASE.Inherit} method.
|
||||
-- See an example at the @{Core.Base#BASE.New} method how this is done.
|
||||
--
|
||||
-- ## 1.2) Trace information for debugging
|
||||
-- # 2. Trace information for debugging.
|
||||
--
|
||||
-- The BASE class contains trace methods to trace progress within a mission execution of a certain object.
|
||||
-- These trace methods are inherited by each MOOSE class interiting BASE, soeach object created from derived class from BASE can use the tracing methods to trace its execution.
|
||||
@@ -71,7 +79,7 @@ local _ClassID = 0
|
||||
--
|
||||
-- Below a more detailed explanation of the different method types for tracing.
|
||||
--
|
||||
-- ### 1.2.1) Tracing methods categories
|
||||
-- ## 2.1. Tracing methods categories.
|
||||
--
|
||||
-- There are basically 3 types of tracing methods available:
|
||||
--
|
||||
@@ -79,9 +87,9 @@ local _ClassID = 0
|
||||
-- * @{#BASE.T}: Used to trace further logic within a function giving optional variables or parameters. A T is indicated at column 44 in the DCS.log file.
|
||||
-- * @{#BASE.E}: Used to always trace information giving optional variables or parameters. An E is indicated at column 44 in the DCS.log file.
|
||||
--
|
||||
-- ### 1.2.2) Tracing levels
|
||||
-- ## 2.2 Tracing levels.
|
||||
--
|
||||
-- There are 3 tracing levels within MOOSE.
|
||||
-- There are 3 tracing levels within MOOSE.
|
||||
-- These tracing levels were defined to avoid bulks of tracing to be generated by lots of objects.
|
||||
--
|
||||
-- As such, the F and T methods have additional variants to trace level 2 and 3 respectively:
|
||||
@@ -91,7 +99,7 @@ local _ClassID = 0
|
||||
-- * @{#BASE.T2}: Trace further logic within a function giving optional variables or parameters with tracing level 2.
|
||||
-- * @{#BASE.T3}: Trace further logic within a function giving optional variables or parameters with tracing level 3.
|
||||
--
|
||||
-- ### 1.2.3) Trace activation.
|
||||
-- ## 2.3. Trace activation.
|
||||
--
|
||||
-- Tracing can be activated in several ways:
|
||||
--
|
||||
@@ -101,16 +109,17 @@ local _ClassID = 0
|
||||
-- * Activate only the tracing of a certain method of a certain class through the @{#BASE.TraceClassMethod}() method.
|
||||
-- * Activate only the tracing of a certain level through the @{#BASE.TraceLevel}() method.
|
||||
--
|
||||
-- ### 1.2.4) Check if tracing is on.
|
||||
-- ## 2.4. Check if tracing is on.
|
||||
--
|
||||
-- The method @{#BASE.IsTrace}() will validate if tracing is activated or not.
|
||||
--
|
||||
-- ## 1.3 DCS simulator Event Handling
|
||||
--
|
||||
-- # 3. DCS simulator Event Handling.
|
||||
--
|
||||
-- The BASE class provides methods to catch DCS Events. These are events that are triggered from within the DCS simulator,
|
||||
-- and handled through lua scripting. MOOSE provides an encapsulation to handle these events more efficiently.
|
||||
--
|
||||
-- ### 1.3.1 Subscribe / Unsubscribe to DCS Events
|
||||
-- ## 3.1. Subscribe / Unsubscribe to DCS Events.
|
||||
--
|
||||
-- At first, the mission designer will need to **Subscribe** to a specific DCS event for the class.
|
||||
-- So, when the DCS event occurs, the class will be notified of that event.
|
||||
@@ -119,7 +128,7 @@ local _ClassID = 0
|
||||
-- * @{#BASE.HandleEvent}(): Subscribe to a DCS Event.
|
||||
-- * @{#BASE.UnHandleEvent}(): Unsubscribe from a DCS Event.
|
||||
--
|
||||
-- ### 1.3.2 Event Handling of DCS Events
|
||||
-- ## 3.2. Event Handling of DCS Events.
|
||||
--
|
||||
-- Once the class is subscribed to the event, an **Event Handling** method on the object or class needs to be written that will be called
|
||||
-- when the DCS event occurs. The Event Handling method receives an @{Core.Event#EVENTDATA} structure, which contains a lot of information
|
||||
@@ -154,7 +163,7 @@ local _ClassID = 0
|
||||
--
|
||||
-- See the @{Event} module for more information about event handling.
|
||||
--
|
||||
-- ## 1.4) Class identification methods
|
||||
-- # 4. Class identification methods.
|
||||
--
|
||||
-- BASE provides methods to get more information of each object:
|
||||
--
|
||||
@@ -162,7 +171,7 @@ local _ClassID = 0
|
||||
-- * @{#BASE.GetClassName}(): Gets the name of the object, which is the name of the class the object was instantiated from.
|
||||
-- * @{#BASE.GetClassNameAndID}(): Gets the name and ID of the object.
|
||||
--
|
||||
-- ## 1.5) All objects derived from BASE can have "States"
|
||||
-- # 5. All objects derived from BASE can have "States".
|
||||
--
|
||||
-- A mechanism is in place in MOOSE, that allows to let the objects administer **states**.
|
||||
-- States are essentially properties of objects, which are identified by a **Key** and a **Value**.
|
||||
@@ -177,7 +186,7 @@ local _ClassID = 0
|
||||
-- Thus, if the state is to be set for the same object as the object for which the method is used, then provide the same
|
||||
-- object name to the method.
|
||||
--
|
||||
-- ## 1.10) Inheritance
|
||||
-- # 6. Inheritance.
|
||||
--
|
||||
-- The following methods are available to implement inheritance
|
||||
--
|
||||
@@ -186,8 +195,7 @@ local _ClassID = 0
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #BASE BASE
|
||||
--
|
||||
-- @field #BASE
|
||||
BASE = {
|
||||
ClassName = "BASE",
|
||||
ClassID = 0,
|
||||
@@ -290,7 +298,8 @@ end
|
||||
--
|
||||
--
|
||||
-- @param #BASE self
|
||||
-- @param #BASE Child is the Child class from which the Parent class needs to be retrieved.
|
||||
-- @param #BASE Child This is the Child class from which the Parent class needs to be retrieved.
|
||||
-- @param #BASE FromClass (Optional) The class from which to get the parent.
|
||||
-- @return #BASE
|
||||
function BASE:GetParent( Child, FromClass )
|
||||
|
||||
@@ -587,18 +596,85 @@ do -- Event Handling
|
||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||
|
||||
--- Occurs when any unit begins firing a weapon that has a high rate of fire. Most common with aircraft cannons (GAU-8), autocannons, and machine guns.
|
||||
-- initiator : The unit that is doing the shooing.
|
||||
-- initiator : The unit that is doing the shooting.
|
||||
-- target: The unit that is being targeted.
|
||||
-- @function [parent=#BASE] OnEventShootingStart
|
||||
-- @param #BASE self
|
||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||
|
||||
--- Occurs when any unit stops firing its weapon. Event will always correspond with a shooting start event.
|
||||
-- initiator : The unit that was doing the shooing.
|
||||
-- initiator : The unit that was doing the shooting.
|
||||
-- @function [parent=#BASE] OnEventShootingEnd
|
||||
-- @param #BASE self
|
||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||
|
||||
--- Occurs when a new mark was added.
|
||||
-- MarkID: ID of the mark.
|
||||
-- @function [parent=#BASE] OnEventMarkAdded
|
||||
-- @param #BASE self
|
||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||
|
||||
--- Occurs when a mark was removed.
|
||||
-- MarkID: ID of the mark.
|
||||
-- @function [parent=#BASE] OnEventMarkRemoved
|
||||
-- @param #BASE self
|
||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||
|
||||
--- Occurs when a mark text was changed.
|
||||
-- MarkID: ID of the mark.
|
||||
-- @function [parent=#BASE] OnEventMarkChange
|
||||
-- @param #BASE self
|
||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||
|
||||
|
||||
--- Unknown precisely what creates this event, likely tied into newer damage model. Will update this page when new information become available.
|
||||
--
|
||||
-- * initiator: The unit that had the failure.
|
||||
--
|
||||
-- @function [parent=#BASE] OnEventDetailedFailure
|
||||
-- @param #BASE self
|
||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||
|
||||
--- Occurs when any modification to the "Score" as seen on the debrief menu would occur.
|
||||
-- There is no information on what values the score was changed to. Event is likely similar to player_comment in this regard.
|
||||
-- @function [parent=#BASE] OnEventScore
|
||||
-- @param #BASE self
|
||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||
|
||||
--- Occurs on the death of a unit. Contains more and different information. Similar to unit_lost it will occur for aircraft before the aircraft crash event occurs.
|
||||
--
|
||||
-- * initiator: The unit that killed the target
|
||||
-- * target: Target Object
|
||||
-- * weapon: Weapon Object
|
||||
--
|
||||
-- @function [parent=#BASE] OnEventKill
|
||||
-- @param #BASE self
|
||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||
|
||||
--- Occurs when any modification to the "Score" as seen on the debrief menu would occur.
|
||||
-- There is no information on what values the score was changed to. Event is likely similar to player_comment in this regard.
|
||||
-- @function [parent=#BASE] OnEventScore
|
||||
-- @param #BASE self
|
||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||
|
||||
--- Occurs when the game thinks an object is destroyed.
|
||||
--
|
||||
-- * initiator: The unit that is was destroyed.
|
||||
--
|
||||
-- @function [parent=#BASE] OnEventUnitLost
|
||||
-- @param #BASE self
|
||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||
|
||||
--- Occurs shortly after the landing animation of an ejected pilot touching the ground and standing up. Event does not occur if the pilot lands in the water and sub combs to Davey Jones Locker.
|
||||
--
|
||||
-- * initiator: Static object representing the ejected pilot. Place : Aircraft that the pilot ejected from.
|
||||
-- * place: may not return as a valid object if the aircraft has crashed into the ground and no longer exists.
|
||||
-- * subplace: is always 0 for unknown reasons.
|
||||
--
|
||||
-- @function [parent=#BASE] OnEventLandingAfterEjection
|
||||
-- @param #BASE self
|
||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -738,9 +814,7 @@ do -- Scheduling
|
||||
if not self.Scheduler then
|
||||
self.Scheduler = SCHEDULER:New( self )
|
||||
end
|
||||
|
||||
self.Scheduler.SchedulerObject = self.Scheduler
|
||||
|
||||
|
||||
local ScheduleID = _SCHEDULEDISPATCHER:AddSchedule(
|
||||
self,
|
||||
SchedulerFunction,
|
||||
@@ -778,16 +852,15 @@ do -- Scheduling
|
||||
self.Scheduler = SCHEDULER:New( self )
|
||||
end
|
||||
|
||||
self.Scheduler.SchedulerObject = self.Scheduler
|
||||
|
||||
local ScheduleID = _SCHEDULEDISPATCHER:AddSchedule(
|
||||
local ScheduleID = self.Scheduler:Schedule(
|
||||
self,
|
||||
SchedulerFunction,
|
||||
{ ... },
|
||||
Start,
|
||||
Repeat,
|
||||
RandomizeFactor,
|
||||
Stop
|
||||
Stop,
|
||||
4
|
||||
)
|
||||
|
||||
self._.Schedules[#self._.Schedules+1] = ScheduleID
|
||||
@@ -801,8 +874,10 @@ do -- Scheduling
|
||||
function BASE:ScheduleStop( SchedulerFunction )
|
||||
|
||||
self:F3( { "ScheduleStop:" } )
|
||||
|
||||
_SCHEDULEDISPATCHER:Stop( self.Scheduler, self._.Schedules[SchedulerFunction] )
|
||||
|
||||
if self.Scheduler then
|
||||
_SCHEDULEDISPATCHER:Stop( self.Scheduler, self._.Schedules[SchedulerFunction] )
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -861,6 +936,26 @@ end
|
||||
-- Log a trace (only shown when trace is on)
|
||||
-- TODO: Make trace function using variable parameters.
|
||||
|
||||
--- Set trace on.
|
||||
-- @param #BASE self
|
||||
-- @usage
|
||||
-- -- Switch the tracing On
|
||||
-- BASE:TraceOn()
|
||||
function BASE:TraceOn()
|
||||
self:TraceOnOff( true )
|
||||
end
|
||||
|
||||
--- Set trace off.
|
||||
-- @param #BASE self
|
||||
-- @usage
|
||||
-- -- Switch the tracing Off
|
||||
-- BASE:TraceOff()
|
||||
function BASE:TraceOff()
|
||||
self:TraceOnOff( false )
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Set trace on or off
|
||||
-- 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.
|
||||
@@ -875,7 +970,13 @@ end
|
||||
-- -- Switch the tracing Off
|
||||
-- BASE:TraceOnOff( false )
|
||||
function BASE:TraceOnOff( TraceOnOff )
|
||||
_TraceOnOff = TraceOnOff
|
||||
if TraceOnOff==false then
|
||||
self:I( "Tracing in MOOSE is OFF" )
|
||||
_TraceOnOff = false
|
||||
else
|
||||
self:I( "Tracing in MOOSE is ON" )
|
||||
_TraceOnOff = true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -895,8 +996,8 @@ end
|
||||
-- @param #BASE self
|
||||
-- @param #number Level
|
||||
function BASE:TraceLevel( Level )
|
||||
_TraceLevel = Level
|
||||
self:E( "Tracing level " .. Level )
|
||||
_TraceLevel = Level or 1
|
||||
self:I( "Tracing level " .. _TraceLevel )
|
||||
end
|
||||
|
||||
--- Trace all methods in MOOSE
|
||||
@@ -904,12 +1005,16 @@ end
|
||||
-- @param #boolean TraceAll true = trace all methods in MOOSE.
|
||||
function BASE:TraceAll( TraceAll )
|
||||
|
||||
_TraceAll = TraceAll
|
||||
if TraceAll==false then
|
||||
_TraceAll=false
|
||||
else
|
||||
_TraceAll = true
|
||||
end
|
||||
|
||||
if _TraceAll then
|
||||
self:E( "Tracing all methods in MOOSE " )
|
||||
self:I( "Tracing all methods in MOOSE " )
|
||||
else
|
||||
self:E( "Switched off tracing all methods in MOOSE" )
|
||||
self:I( "Switched off tracing all methods in MOOSE" )
|
||||
end
|
||||
end
|
||||
|
||||
@@ -919,7 +1024,7 @@ end
|
||||
function BASE:TraceClass( Class )
|
||||
_TraceClass[Class] = true
|
||||
_TraceClassMethod[Class] = {}
|
||||
self:E( "Tracing class " .. Class )
|
||||
self:I( "Tracing class " .. Class )
|
||||
end
|
||||
|
||||
--- Set tracing for a specific method of class
|
||||
@@ -932,7 +1037,7 @@ function BASE:TraceClassMethod( Class, Method )
|
||||
_TraceClassMethod[Class].Method = {}
|
||||
end
|
||||
_TraceClassMethod[Class].Method[Method] = true
|
||||
self:E( "Tracing method " .. Method .. " of class " .. Class )
|
||||
self:I( "Tracing method " .. Method .. " of class " .. Class )
|
||||
end
|
||||
|
||||
--- Trace a function call. This function is private.
|
||||
@@ -959,7 +1064,7 @@ function BASE:_F( Arguments, DebugInfoCurrentParam, DebugInfoFromParam )
|
||||
if DebugInfoFrom then
|
||||
LineFrom = DebugInfoFrom.currentline
|
||||
end
|
||||
env.info( string.format( "%6d(%6d)/%1s:%20s%05d.%s(%s)" , LineCurrent, LineFrom, "F", self.ClassName, self.ClassID, Function, routines.utils.oneLineSerialize( Arguments ) ) )
|
||||
env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s(%s)" , LineCurrent, LineFrom, "F", self.ClassName, self.ClassID, Function, routines.utils.oneLineSerialize( Arguments ) ) )
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1034,7 +1139,7 @@ function BASE:_T( Arguments, DebugInfoCurrentParam, DebugInfoFromParam )
|
||||
if DebugInfoFrom then
|
||||
LineFrom = DebugInfoFrom.currentline
|
||||
end
|
||||
env.info( string.format( "%6d(%6d)/%1s:%20s%05d.%s" , LineCurrent, LineFrom, "T", self.ClassName, self.ClassID, routines.utils.oneLineSerialize( Arguments ) ) )
|
||||
env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s" , LineCurrent, LineFrom, "T", self.ClassName, self.ClassID, routines.utils.oneLineSerialize( Arguments ) ) )
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1105,9 +1210,9 @@ function BASE:E( Arguments )
|
||||
LineFrom = DebugInfoFrom.currentline
|
||||
end
|
||||
|
||||
env.info( string.format( "%6d(%6d)/%1s:%20s%05d.%s(%s)" , LineCurrent, LineFrom, "E", self.ClassName, self.ClassID, Function, routines.utils.oneLineSerialize( Arguments ) ) )
|
||||
env.info( string.format( "%6d(%6d)/%1s:%30s%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 ) ) )
|
||||
env.info( string.format( "%1s:%30s%05d(%s)" , "E", self.ClassName, self.ClassID, routines.utils.oneLineSerialize( Arguments ) ) )
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1133,9 +1238,9 @@ function BASE:I( Arguments )
|
||||
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 ) ) )
|
||||
env.info( string.format( "%6d(%6d)/%1s:%30s%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 ) ) )
|
||||
env.info( string.format( "%1s:%30s%05d(%s)" , "I", self.ClassName, self.ClassID, routines.utils.oneLineSerialize( Arguments ) ) )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,4 +1,25 @@
|
||||
--- **Core** -- DATABASE manages the database of mission objects.
|
||||
--- **Core** - Manages several databases containing templates, mission objects, and mission information.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
-- * During mission startup, scan the mission environment, and create / instantiate intelligently the different objects as defined within the mission.
|
||||
-- * Manage database of DCS Group templates (as modelled using the mission editor).
|
||||
-- - Group templates.
|
||||
-- - Unit templates.
|
||||
-- - Statics templates.
|
||||
-- * Manage database of @{Wrapper.Group#GROUP} objects alive in the mission.
|
||||
-- * Manage database of @{Wrapper.Unit#UNIT} objects alive in the mission.
|
||||
-- * Manage database of @{Wrapper.Static#STATIC} objects alive in the mission.
|
||||
-- * Manage database of players.
|
||||
-- * Manage database of client slots defined using the mission editor.
|
||||
-- * Manage database of airbases on the map, and from FARPs and ships as defined using the mission editor.
|
||||
-- * Manage database of countries.
|
||||
-- * Manage database of zone names.
|
||||
-- * Manage database of hits to units and statics.
|
||||
-- * Manage database of destroys of units and statics.
|
||||
-- * Manage database of @{Core.Zone#ZONE_BASE} objects.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -60,6 +81,7 @@ DATABASE = {
|
||||
HITS = {},
|
||||
DESTROYS = {},
|
||||
ZONES = {},
|
||||
ZONES_GOAL = {},
|
||||
}
|
||||
|
||||
local _DATABASECoalition =
|
||||
@@ -165,6 +187,7 @@ end
|
||||
function DATABASE:AddUnit( DCSUnitName )
|
||||
|
||||
if not self.UNITS[DCSUnitName] then
|
||||
self:T( { "Add UNIT:", DCSUnitName } )
|
||||
local UnitRegister = UNIT:Register( DCSUnitName )
|
||||
self.UNITS[DCSUnitName] = UNIT:Register( DCSUnitName )
|
||||
|
||||
@@ -224,12 +247,15 @@ end
|
||||
|
||||
--- Adds a Airbase based on the Airbase Name in the DATABASE.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string AirbaseName The name of the airbase
|
||||
-- @param #string AirbaseName The name of the airbase.
|
||||
-- @return Wrapper.Airbase#AIRBASE Airbase object.
|
||||
function DATABASE:AddAirbase( AirbaseName )
|
||||
|
||||
if not self.AIRBASES[AirbaseName] then
|
||||
self.AIRBASES[AirbaseName] = AIRBASE:Register( AirbaseName )
|
||||
end
|
||||
|
||||
return self.AIRBASES[AirbaseName]
|
||||
end
|
||||
|
||||
|
||||
@@ -283,16 +309,6 @@ do -- Zones
|
||||
|
||||
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
|
||||
|
||||
|
||||
--- Private method that registers new ZONE_BASE derived objects within the DATABASE Object.
|
||||
@@ -327,7 +343,39 @@ do -- Zones
|
||||
|
||||
end -- zone
|
||||
|
||||
do -- Zone_Goal
|
||||
|
||||
--- 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:FindZoneGoal( ZoneName )
|
||||
|
||||
local ZoneFound = self.ZONES_GOAL[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:AddZoneGoal( ZoneName, Zone )
|
||||
|
||||
if not self.ZONES_GOAL[ZoneName] then
|
||||
self.ZONES_GOAL[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:DeleteZoneGoal( ZoneName )
|
||||
|
||||
self.ZONES_GOAL[ZoneName] = nil
|
||||
end
|
||||
|
||||
end -- Zone_Goal
|
||||
do -- cargo
|
||||
|
||||
--- Adds a Cargo based on the Cargo Name in the DATABASE.
|
||||
@@ -464,7 +512,7 @@ end
|
||||
function DATABASE:AddGroup( GroupName )
|
||||
|
||||
if not self.GROUPS[GroupName] then
|
||||
self:I( { "Add GROUP:", GroupName } )
|
||||
self:T( { "Add GROUP:", GroupName } )
|
||||
self.GROUPS[GroupName] = GROUP:Register( GroupName )
|
||||
end
|
||||
|
||||
@@ -476,7 +524,7 @@ end
|
||||
function DATABASE:AddPlayer( UnitName, PlayerName )
|
||||
|
||||
if PlayerName then
|
||||
self:I( { "Add player for unit:", UnitName, PlayerName } )
|
||||
self:T( { "Add player for unit:", UnitName, PlayerName } )
|
||||
self.PLAYERS[PlayerName] = UnitName
|
||||
self.PLAYERUNITS[PlayerName] = self:FindUnit( UnitName )
|
||||
self.PLAYERSJOINED[PlayerName] = PlayerName
|
||||
@@ -488,7 +536,7 @@ end
|
||||
function DATABASE:DeletePlayer( UnitName, PlayerName )
|
||||
|
||||
if PlayerName then
|
||||
self:I( { "Clean player:", PlayerName } )
|
||||
self:T( { "Clean player:", PlayerName } )
|
||||
self.PLAYERS[PlayerName] = nil
|
||||
self.PLAYERUNITS[PlayerName] = nil
|
||||
end
|
||||
@@ -653,11 +701,11 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, Category
|
||||
UnitNames[#UnitNames+1] = self.Templates.Units[UnitTemplate.name].UnitName
|
||||
end
|
||||
|
||||
self:I( { Group = self.Templates.Groups[GroupTemplateName].GroupName,
|
||||
self:T( { 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
|
||||
Category = self.Templates.Groups[GroupTemplateName].CategoryID,
|
||||
Country = self.Templates.Groups[GroupTemplateName].CountryID,
|
||||
Units = UnitNames
|
||||
}
|
||||
)
|
||||
end
|
||||
@@ -802,9 +850,9 @@ function DATABASE:_RegisterGroupsAndUnits()
|
||||
end
|
||||
end
|
||||
|
||||
self:I("Groups:")
|
||||
self:T("Groups:")
|
||||
for GroupName, Group in pairs( self.GROUPS ) do
|
||||
self:I( { "Group:", GroupName } )
|
||||
self:T( { "Group:", GroupName } )
|
||||
end
|
||||
|
||||
return self
|
||||
@@ -816,7 +864,7 @@ end
|
||||
function DATABASE:_RegisterClients()
|
||||
|
||||
for ClientName, ClientTemplate in pairs( self.Templates.ClientsByName ) do
|
||||
self:I( { "Register Client:", ClientName } )
|
||||
self:T( { "Register Client:", ClientName } )
|
||||
self:AddClient( ClientName )
|
||||
end
|
||||
|
||||
@@ -834,7 +882,7 @@ function DATABASE:_RegisterStatics()
|
||||
if DCSStatic:isExist() then
|
||||
local DCSStaticName = DCSStatic:getName()
|
||||
|
||||
self:I( { "Register Static:", DCSStaticName } )
|
||||
self:T( { "Register Static:", DCSStaticName } )
|
||||
self:AddStatic( DCSStaticName )
|
||||
else
|
||||
self:E( { "Static does not exist: ", DCSStatic } )
|
||||
@@ -848,16 +896,29 @@ end
|
||||
--- @param #DATABASE self
|
||||
function DATABASE:_RegisterAirbases()
|
||||
|
||||
--[[
|
||||
local CoalitionsData = { AirbasesRed = coalition.getAirbases( coalition.side.RED ), AirbasesBlue = coalition.getAirbases( coalition.side.BLUE ), AirbasesNeutral = coalition.getAirbases( coalition.side.NEUTRAL ) }
|
||||
for CoalitionId, CoalitionData in pairs( CoalitionsData ) do
|
||||
for DCSAirbaseId, DCSAirbase in pairs( CoalitionData ) do
|
||||
|
||||
local DCSAirbaseName = DCSAirbase:getName()
|
||||
|
||||
self:I( { "Register Airbase:", DCSAirbaseName, DCSAirbase:getID() } )
|
||||
self:T( { "Register Airbase:", DCSAirbaseName, DCSAirbase:getID() } )
|
||||
self:AddAirbase( DCSAirbaseName )
|
||||
end
|
||||
end
|
||||
]]
|
||||
|
||||
for DCSAirbaseId, DCSAirbase in pairs(world.getAirbases()) do
|
||||
local DCSAirbaseName = DCSAirbase:getName()
|
||||
|
||||
-- This gives the incorrect value to be inserted into the airdromeID for DCS 2.5.6!
|
||||
local airbaseID=DCSAirbase:getID()
|
||||
|
||||
local airbase=self:AddAirbase( DCSAirbaseName )
|
||||
|
||||
self:I(string.format("Register Airbase: %s, getID=%d, GetID=%d (unique=%d)", DCSAirbaseName, DCSAirbase:getID(), airbase:GetID(), airbase:GetID(true)))
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -869,7 +930,7 @@ end
|
||||
-- @param #DATABASE self
|
||||
-- @param Core.Event#EVENTDATA Event
|
||||
function DATABASE:_EventOnBirth( Event )
|
||||
self:F2( { Event } )
|
||||
self:F( { Event } )
|
||||
|
||||
if Event.IniDCSUnit then
|
||||
if Event.IniObjectCategory == 3 then
|
||||
@@ -878,6 +939,12 @@ function DATABASE:_EventOnBirth( Event )
|
||||
if Event.IniObjectCategory == 1 then
|
||||
self:AddUnit( Event.IniDCSUnitName )
|
||||
self:AddGroup( Event.IniDCSGroupName )
|
||||
-- Add airbase if it was spawned later in the mission.
|
||||
local DCSAirbase = Airbase.getByName(Event.IniDCSUnitName)
|
||||
if DCSAirbase then
|
||||
self:I(string.format("Adding airbase %s", tostring(Event.IniDCSUnitName)))
|
||||
self:AddAirbase(Event.IniDCSUnitName)
|
||||
end
|
||||
end
|
||||
end
|
||||
if Event.IniObjectCategory == 1 then
|
||||
@@ -886,6 +953,7 @@ function DATABASE:_EventOnBirth( Event )
|
||||
local PlayerName = Event.IniUnit:GetPlayerName()
|
||||
if PlayerName then
|
||||
self:I( { "Player Joined:", PlayerName } )
|
||||
self:AddClient( Event.IniDCSUnitName )
|
||||
if not self.PLAYERS[PlayerName] then
|
||||
self:AddPlayer( Event.IniUnitName, PlayerName )
|
||||
end
|
||||
@@ -1006,7 +1074,8 @@ function DATABASE:ForEach( IteratorFunction, FinalizeFunction, arg, Set )
|
||||
return false
|
||||
end
|
||||
|
||||
local Scheduler = SCHEDULER:New( self, Schedule, {}, 0.001, 0.001, 0 )
|
||||
--local Scheduler = SCHEDULER:New( self, Schedule, {}, 0.001, 0.001, 0 )
|
||||
Schedule()
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -1145,7 +1214,7 @@ function DATABASE:OnEventNewZone( EventData )
|
||||
self:F2( { EventData } )
|
||||
|
||||
if EventData.Zone then
|
||||
self:AddZone( EventData.Zone )
|
||||
self:AddZone( EventData.Zone.ZoneName, EventData.Zone )
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1201,7 +1270,7 @@ function DATABASE:_RegisterTemplates()
|
||||
|
||||
local CoalitionSide = coalition.side[string.upper(CoalitionName)]
|
||||
if CoalitionName=="red" then
|
||||
CoalitionSide=coalition.side.NEUTRAL
|
||||
CoalitionSide=coalition.side.RED
|
||||
elseif CoalitionName=="blue" then
|
||||
CoalitionSide=coalition.side.BLUE
|
||||
else
|
||||
@@ -1309,7 +1378,7 @@ end
|
||||
-- What is he hitting?
|
||||
if Event.TgtCategory then
|
||||
|
||||
if Event.IniCoalition then -- A coalition object was hit, probably a static.
|
||||
if Event.WeaponCoalition 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]
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
--- **Core** -- EVENT models DCS **event dispatching** using a **publish-subscribe** model.
|
||||
--- **Core** - Models DCS event dispatching using a publish-subscribe model.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # 1) Event Handling Overview
|
||||
-- ## Features:
|
||||
--
|
||||
-- * Capture DCS events and dispatch them to the subscribed objects.
|
||||
-- * Generate DCS events to the subscribed objects from within the code.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # Event Handling Overview
|
||||
--
|
||||
-- 
|
||||
--
|
||||
@@ -14,7 +21,7 @@
|
||||
-- Objects can subscribe to different events. The Event dispatcher will publish the received DCS events to the subscribed MOOSE objects, in a specified order.
|
||||
-- In this way, the subscribed MOOSE objects are kept in sync with your evolving running mission.
|
||||
--
|
||||
-- ## 1.1) Event Dispatching
|
||||
-- ## 1. Event Dispatching
|
||||
--
|
||||
-- 
|
||||
--
|
||||
@@ -41,7 +48,7 @@
|
||||
--
|
||||
-- But for some DCS events, the publishing order is reversed. This is due to the fact that objects need to be **erased** instead of added.
|
||||
--
|
||||
-- ## 1.2) Event Handling
|
||||
-- # 2. Event Handling
|
||||
--
|
||||
-- 
|
||||
--
|
||||
@@ -53,7 +60,7 @@
|
||||
-- The BASE class provides methods to catch DCS Events. These are events that are triggered from within the DCS simulator,
|
||||
-- and handled through lua scripting. MOOSE provides an encapsulation to handle these events more efficiently.
|
||||
--
|
||||
-- ### 1.2.1 Subscribe / Unsubscribe to DCS Events
|
||||
-- ## 2.1. Subscribe to / Unsubscribe from DCS Events.
|
||||
--
|
||||
-- At first, the mission designer will need to **Subscribe** to a specific DCS event for the class.
|
||||
-- So, when the DCS event occurs, the class will be notified of that event.
|
||||
@@ -69,7 +76,7 @@
|
||||
-- So if a UNIT within the mission has the subscribed event for that object,
|
||||
-- then the object event handler will receive the event for that UNIT!
|
||||
--
|
||||
-- ### 1.3.2 Event Handling of DCS Events
|
||||
-- ## 2.2 Event Handling of DCS Events
|
||||
--
|
||||
-- Once the class is subscribed to the event, an **Event Handling** method on the object or class needs to be written that will be called
|
||||
-- when the DCS event occurs. The Event Handling method receives an @{Core.Event#EVENTDATA} structure, which contains a lot of information
|
||||
@@ -100,19 +107,19 @@
|
||||
-- self:SmokeBlue()
|
||||
-- end
|
||||
--
|
||||
-- ### 1.3.3 Event Handling methods that are automatically called upon subscribed DCS events
|
||||
-- ## 2.3 Event Handling methods that are automatically called upon subscribed DCS events.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- The following list outlines which EVENTS item in the structure corresponds to which Event Handling method.
|
||||
-- Always ensure that your event handling methods align with the events being subscribed to, or nothing will be executed.
|
||||
--
|
||||
-- # 2) EVENTS type
|
||||
-- # 3. EVENTS type
|
||||
--
|
||||
-- The EVENTS structure contains names for all the different DCS events that objects can subscribe to using the
|
||||
-- @{Core.Base#BASE.HandleEvent}() method.
|
||||
--
|
||||
-- # 3) EVENTDATA type
|
||||
-- # 4. EVENTDATA type
|
||||
--
|
||||
-- The @{Core.Event#EVENTDATA} structure contains all the fields that are populated with event information before
|
||||
-- an Event Handler method is being called by the event dispatcher.
|
||||
@@ -166,11 +173,12 @@
|
||||
-- @image Core_Event.JPG
|
||||
|
||||
|
||||
--- The EVENT structure
|
||||
--
|
||||
-- @type EVENT
|
||||
--- @type EVENT
|
||||
-- @field #EVENT.Events Events
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
--- The EVENT class
|
||||
-- @field #EVENT
|
||||
EVENT = {
|
||||
ClassName = "EVENT",
|
||||
ClassID = 0,
|
||||
@@ -181,7 +189,9 @@ world.event.S_EVENT_NEW_CARGO = world.event.S_EVENT_MAX + 1000
|
||||
world.event.S_EVENT_DELETE_CARGO = world.event.S_EVENT_MAX + 1001
|
||||
world.event.S_EVENT_NEW_ZONE = world.event.S_EVENT_MAX + 1002
|
||||
world.event.S_EVENT_DELETE_ZONE = world.event.S_EVENT_MAX + 1003
|
||||
world.event.S_EVENT_REMOVE_UNIT = world.event.S_EVENT_MAX + 1004
|
||||
world.event.S_EVENT_NEW_ZONE_GOAL = world.event.S_EVENT_MAX + 1004
|
||||
world.event.S_EVENT_DELETE_ZONE_GOAL = world.event.S_EVENT_MAX + 1005
|
||||
world.event.S_EVENT_REMOVE_UNIT = world.event.S_EVENT_MAX + 1006
|
||||
|
||||
|
||||
--- The different types of events supported by MOOSE.
|
||||
@@ -211,14 +221,24 @@ EVENTS = {
|
||||
PlayerComment = world.event.S_EVENT_PLAYER_COMMENT,
|
||||
ShootingStart = world.event.S_EVENT_SHOOTING_START,
|
||||
ShootingEnd = world.event.S_EVENT_SHOOTING_END,
|
||||
-- Added with DCS 2.5.1
|
||||
MarkAdded = world.event.S_EVENT_MARK_ADDED,
|
||||
MarkChange = world.event.S_EVENT_MARK_CHANGE,
|
||||
MarkRemoved = world.event.S_EVENT_MARK_REMOVED,
|
||||
-- Moose Events
|
||||
NewCargo = world.event.S_EVENT_NEW_CARGO,
|
||||
DeleteCargo = world.event.S_EVENT_DELETE_CARGO,
|
||||
NewZone = world.event.S_EVENT_NEW_ZONE,
|
||||
DeleteZone = world.event.S_EVENT_DELETE_ZONE,
|
||||
NewZoneGoal = world.event.S_EVENT_NEW_ZONE_GOAL,
|
||||
DeleteZoneGoal = world.event.S_EVENT_DELETE_ZONE_GOAL,
|
||||
RemoveUnit = world.event.S_EVENT_REMOVE_UNIT,
|
||||
-- Added with DCS 2.5.6
|
||||
DetailedFailure = world.event.S_EVENT_DETAILED_FAILURE or -1, --We set this to -1 for backward compatibility to DCS 2.5.5 and earlier
|
||||
Kill = world.event.S_EVENT_KILL or -1,
|
||||
Score = world.event.S_EVENT_SCORE or -1,
|
||||
UnitLost = world.event.S_EVENT_UNIT_LOST or -1,
|
||||
LandingAfterEjection = world.event.S_EVENT_LANDING_AFTER_EJECTION or -1,
|
||||
}
|
||||
|
||||
--- The Event structure
|
||||
@@ -264,10 +284,16 @@ EVENTS = {
|
||||
-- @field Wrapper.Airbase#AIRBASE Place The MOOSE airbase object.
|
||||
-- @field #string PlaceName The name of the airbase.
|
||||
--
|
||||
-- @field weapon The weapon used during the event.
|
||||
-- @field Weapon
|
||||
-- @field WeaponName
|
||||
-- @field WeaponTgtDCSUnit
|
||||
-- @field #table weapon The weapon used during the event.
|
||||
-- @field #table Weapon
|
||||
-- @field #string WeaponName Name of the weapon.
|
||||
-- @field DCS#Unit WeaponTgtDCSUnit Target DCS unit of the weapon.
|
||||
--
|
||||
-- @field Cargo.Cargo#CARGO Cargo The cargo object.
|
||||
-- @field #string CargoName The name of the cargo object.
|
||||
--
|
||||
-- @field Core.ZONE#ZONE Zone The zone object.
|
||||
-- @field #string ZoneName The name of the zone.
|
||||
|
||||
|
||||
|
||||
@@ -448,11 +474,47 @@ local _EVENTMETA = {
|
||||
Event = "OnEventDeleteZone",
|
||||
Text = "S_EVENT_DELETE_ZONE"
|
||||
},
|
||||
[EVENTS.NewZoneGoal] = {
|
||||
Order = 1,
|
||||
Event = "OnEventNewZoneGoal",
|
||||
Text = "S_EVENT_NEW_ZONE_GOAL"
|
||||
},
|
||||
[EVENTS.DeleteZoneGoal] = {
|
||||
Order = 1,
|
||||
Event = "OnEventDeleteZoneGoal",
|
||||
Text = "S_EVENT_DELETE_ZONE_GOAL"
|
||||
},
|
||||
[EVENTS.RemoveUnit] = {
|
||||
Order = -1,
|
||||
Event = "OnEventRemoveUnit",
|
||||
Text = "S_EVENT_REMOVE_UNIT"
|
||||
},
|
||||
-- Added with DCS 2.5.6
|
||||
[EVENTS.DetailedFailure] = {
|
||||
Order = 1,
|
||||
Event = "OnEventDetailedFailure",
|
||||
Text = "S_EVENT_DETAILED_FAILURE"
|
||||
},
|
||||
[EVENTS.Kill] = {
|
||||
Order = 1,
|
||||
Event = "OnEventKill",
|
||||
Text = "S_EVENT_KILL"
|
||||
},
|
||||
[EVENTS.Score] = {
|
||||
Order = 1,
|
||||
Event = "OnEventScore",
|
||||
Text = "S_EVENT_SCORE"
|
||||
},
|
||||
[EVENTS.UnitLost] = {
|
||||
Order = 1,
|
||||
Event = "OnEventUnitLost",
|
||||
Text = "S_EVENT_UNIT_LOST"
|
||||
},
|
||||
[EVENTS.LandingAfterEjection] = {
|
||||
Order = 1,
|
||||
Event = "OnEventLandingAfterEjection",
|
||||
Text = "S_EVENT_LANDING_AFTER_EJECTION"
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -460,6 +522,9 @@ local _EVENTMETA = {
|
||||
-- @type EVENT.Events
|
||||
-- @field #number IniUnit
|
||||
|
||||
--- Create new event handler.
|
||||
-- @param #EVENT self
|
||||
-- @return #EVENT self
|
||||
function EVENT:New()
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
self:F2()
|
||||
@@ -490,6 +555,8 @@ function EVENT:Init( EventID, EventClass )
|
||||
if not self.Events[EventID][EventPriority][EventClass] then
|
||||
self.Events[EventID][EventPriority][EventClass] = {}
|
||||
end
|
||||
|
||||
|
||||
return self.Events[EventID][EventPriority][EventClass]
|
||||
end
|
||||
|
||||
@@ -576,7 +643,7 @@ end
|
||||
-- @param EventID
|
||||
-- @return #EVENT
|
||||
function EVENT:OnEventGeneric( EventFunction, EventClass, EventID )
|
||||
self:F2( { EventID } )
|
||||
self:F2( { EventID, EventClass, EventFunction } )
|
||||
|
||||
local EventData = self:Init( EventID, EventClass )
|
||||
EventData.EventFunction = EventFunction
|
||||
@@ -786,6 +853,38 @@ do -- Event Creation
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
--- Creation of a New ZoneGoal Event.
|
||||
-- @param #EVENT self
|
||||
-- @param Core.Functional#ZONE_GOAL ZoneGoal The ZoneGoal created.
|
||||
function EVENT:CreateEventNewZoneGoal( ZoneGoal )
|
||||
self:F( { ZoneGoal } )
|
||||
|
||||
local Event = {
|
||||
id = EVENTS.NewZoneGoal,
|
||||
time = timer.getTime(),
|
||||
ZoneGoal = ZoneGoal,
|
||||
}
|
||||
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
|
||||
--- Creation of a ZoneGoal Deletion Event.
|
||||
-- @param #EVENT self
|
||||
-- @param Core.ZoneGoal#ZONE_GOAL ZoneGoal The ZoneGoal created.
|
||||
function EVENT:CreateEventDeleteZoneGoal( ZoneGoal )
|
||||
self:F( { ZoneGoal } )
|
||||
|
||||
local Event = {
|
||||
id = EVENTS.DeleteZoneGoal,
|
||||
time = timer.getTime(),
|
||||
ZoneGoal = ZoneGoal,
|
||||
}
|
||||
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
|
||||
--- Creation of a S_EVENT_PLAYER_ENTER_UNIT Event.
|
||||
-- @param #EVENT self
|
||||
-- @param Wrapper.Unit#UNIT PlayerUnit.
|
||||
@@ -818,260 +917,227 @@ function EVENT:onEvent( Event )
|
||||
end
|
||||
|
||||
|
||||
-- Get event meta data.
|
||||
local EventMeta = _EVENTMETA[Event.id]
|
||||
|
||||
--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
|
||||
Event.IniDCSUnit = Event.initiator
|
||||
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
||||
Event.IniUnitName = Event.IniDCSUnitName
|
||||
Event.IniDCSGroup = Event.IniDCSUnit:getGroup()
|
||||
Event.IniUnit = UNIT:FindByName( Event.IniDCSUnitName )
|
||||
if not Event.IniUnit then
|
||||
-- Unit can be a CLIENT. Most likely this will be the case ...
|
||||
Event.IniUnit = CLIENT:FindByName( Event.IniDCSUnitName, '', true )
|
||||
end
|
||||
Event.IniDCSGroupName = ""
|
||||
if Event.IniDCSGroup and Event.IniDCSGroup:isExist() then
|
||||
Event.IniDCSGroupName = Event.IniDCSGroup:getName()
|
||||
Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName )
|
||||
if Event.IniGroup then
|
||||
Event.IniGroupName = Event.IniDCSGroupName
|
||||
end
|
||||
end
|
||||
Event.IniPlayerName = Event.IniDCSUnit:getPlayerName()
|
||||
Event.IniCoalition = Event.IniDCSUnit:getCoalition()
|
||||
Event.IniTypeName = Event.IniDCSUnit:getTypeName()
|
||||
Event.IniCategory = Event.IniDCSUnit:getDesc().category
|
||||
end
|
||||
|
||||
if Event.IniObjectCategory == Object.Category.STATIC then
|
||||
Event.IniDCSUnit = Event.initiator
|
||||
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
||||
Event.IniUnitName = Event.IniDCSUnitName
|
||||
Event.IniUnit = STATIC:FindByName( Event.IniDCSUnitName, false )
|
||||
Event.IniCoalition = Event.IniDCSUnit:getCoalition()
|
||||
Event.IniCategory = Event.IniDCSUnit:getDesc().category
|
||||
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.initiator:isExist() and Event.IniDCSUnit:getTypeName() or "SCENERY" -- TODO: Bug fix for 2.1!
|
||||
end
|
||||
end
|
||||
|
||||
if Event.target then
|
||||
|
||||
Event.TgtObjectCategory = Event.target:getCategory()
|
||||
|
||||
if Event.TgtObjectCategory == Object.Category.UNIT then
|
||||
Event.TgtDCSUnit = Event.target
|
||||
Event.TgtDCSGroup = Event.TgtDCSUnit:getGroup()
|
||||
Event.TgtDCSUnitName = Event.TgtDCSUnit:getName()
|
||||
Event.TgtUnitName = Event.TgtDCSUnitName
|
||||
Event.TgtUnit = UNIT:FindByName( Event.TgtDCSUnitName )
|
||||
Event.TgtDCSGroupName = ""
|
||||
if Event.TgtDCSGroup and Event.TgtDCSGroup:isExist() then
|
||||
Event.TgtDCSGroupName = Event.TgtDCSGroup:getName()
|
||||
Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName )
|
||||
if Event.TgtGroup then
|
||||
Event.TgtGroupName = Event.TgtDCSGroupName
|
||||
end
|
||||
end
|
||||
Event.TgtPlayerName = Event.TgtDCSUnit:getPlayerName()
|
||||
Event.TgtCoalition = Event.TgtDCSUnit:getCoalition()
|
||||
Event.TgtCategory = Event.TgtDCSUnit:getDesc().category
|
||||
Event.TgtTypeName = Event.TgtDCSUnit:getTypeName()
|
||||
end
|
||||
|
||||
if Event.TgtObjectCategory == Object.Category.STATIC then
|
||||
Event.TgtDCSUnit = Event.target
|
||||
Event.TgtDCSUnitName = Event.TgtDCSUnit:getName()
|
||||
Event.TgtUnitName = 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()
|
||||
end
|
||||
|
||||
if Event.TgtObjectCategory == Object.Category.SCENERY then
|
||||
Event.TgtDCSUnit = Event.target
|
||||
Event.TgtDCSUnitName = Event.TgtDCSUnit:getName()
|
||||
Event.TgtUnitName = Event.TgtDCSUnitName
|
||||
Event.TgtUnit = SCENERY:Register( Event.TgtDCSUnitName, Event.target )
|
||||
Event.TgtCategory = Event.TgtDCSUnit:getDesc().category
|
||||
Event.TgtTypeName = Event.TgtDCSUnit:getTypeName()
|
||||
end
|
||||
end
|
||||
|
||||
if Event.weapon then
|
||||
Event.Weapon = Event.weapon
|
||||
Event.WeaponName = Event.Weapon:getTypeName()
|
||||
Event.WeaponUNIT = CLIENT:Find( Event.Weapon, '', true ) -- Sometimes, the weapon is a player unit!
|
||||
Event.WeaponPlayerName = Event.WeaponUNIT and Event.Weapon:getPlayerName()
|
||||
Event.WeaponCoalition = Event.WeaponUNIT and Event.Weapon:getCoalition()
|
||||
Event.WeaponCategory = Event.WeaponUNIT and Event.Weapon:getDesc().category
|
||||
Event.WeaponTypeName = Event.WeaponUNIT and Event.Weapon:getTypeName()
|
||||
--Event.WeaponTgtDCSUnit = Event.Weapon:getTarget()
|
||||
end
|
||||
|
||||
-- Place should be given for takeoff and landing events as well as base captured. It should be a DCS airbase.
|
||||
if Event.place then
|
||||
Event.Place=AIRBASE:Find(Event.place)
|
||||
Event.PlaceName=Event.Place:GetName()
|
||||
end
|
||||
|
||||
-- @FC: something like this should be added.
|
||||
--[[
|
||||
if Event.idx then
|
||||
Event.MarkID=Event.idx
|
||||
Event.MarkVec3=Event.pos
|
||||
Event.MarkCoordinate=COORDINATE:NewFromVec3(Event.pos)
|
||||
Event.MarkText=Event.text
|
||||
Event.MarkCoalition=Event.coalition
|
||||
Event.MarkGroupID = Event.groupID
|
||||
end
|
||||
]]
|
||||
|
||||
if Event.cargo then
|
||||
Event.Cargo = Event.cargo
|
||||
Event.CargoName = Event.cargo.Name
|
||||
end
|
||||
|
||||
if Event.zone then
|
||||
Event.Zone = Event.zone
|
||||
Event.ZoneName = Event.zone.ZoneName
|
||||
end
|
||||
|
||||
local PriorityOrder = EventMeta.Order
|
||||
local PriorityBegin = PriorityOrder == -1 and 5 or 1
|
||||
local PriorityEnd = PriorityOrder == -1 and 1 or 5
|
||||
|
||||
if Event.IniObjectCategory ~= Object.Category.STATIC then
|
||||
self:T( { EventMeta.Text, Event, Event.IniDCSUnitName, Event.TgtDCSUnitName, PriorityOrder } )
|
||||
end
|
||||
|
||||
for EventPriority = PriorityBegin, PriorityEnd, PriorityOrder do
|
||||
|
||||
if self.Events[Event.id][EventPriority] then
|
||||
|
||||
-- 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
|
||||
|
||||
Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName )
|
||||
Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName )
|
||||
|
||||
-- If the EventData is for a UNIT, the call directly the EventClass EventFunction for that UNIT.
|
||||
if EventData.EventUnit then
|
||||
|
||||
-- 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 or
|
||||
Event.id == EVENTS.RemoveUnit then
|
||||
|
||||
local UnitName = EventClass:GetName()
|
||||
|
||||
if ( EventMeta.Side == "I" and UnitName == Event.IniDCSUnitName ) or
|
||||
( EventMeta.Side == "T" and UnitName == Event.TgtDCSUnitName ) then
|
||||
|
||||
-- First test if a EventFunction is Set, otherwise search for the default function
|
||||
if EventData.EventFunction then
|
||||
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:F( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } )
|
||||
end
|
||||
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
return EventData.EventFunction( EventClass, Event )
|
||||
end, ErrorHandler )
|
||||
|
||||
else
|
||||
|
||||
-- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object.
|
||||
local EventFunction = EventClass[ EventMeta.Event ]
|
||||
if EventFunction and type( EventFunction ) == "function" then
|
||||
|
||||
-- Now call the default event function.
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:F( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
end
|
||||
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
return EventFunction( EventClass, Event )
|
||||
end, ErrorHandler )
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
-- The EventClass is not alive anymore, we remove it from the EventHandlers...
|
||||
self:RemoveEvent( EventClass, Event.id )
|
||||
end
|
||||
else
|
||||
|
||||
-- If the EventData is for a GROUP, the call directly the EventClass EventFunction for the UNIT in that GROUP.
|
||||
if EventData.EventGroup then
|
||||
|
||||
-- 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 or
|
||||
Event.id == EVENTS.RemoveUnit then
|
||||
|
||||
-- We can get the name of the EventClass, which is now always a GROUP object.
|
||||
local GroupName = EventClass:GetName()
|
||||
|
||||
if ( EventMeta.Side == "I" and GroupName == Event.IniDCSGroupName ) or
|
||||
( EventMeta.Side == "T" and GroupName == Event.TgtDCSGroupName ) then
|
||||
|
||||
-- Check if this is a known event?
|
||||
if EventMeta then
|
||||
|
||||
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
|
||||
Event.IniDCSUnit = Event.initiator
|
||||
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
||||
Event.IniUnitName = Event.IniDCSUnitName
|
||||
Event.IniDCSGroup = Event.IniDCSUnit:getGroup()
|
||||
Event.IniUnit = UNIT:FindByName( Event.IniDCSUnitName )
|
||||
if not Event.IniUnit then
|
||||
-- Unit can be a CLIENT. Most likely this will be the case ...
|
||||
Event.IniUnit = CLIENT:FindByName( Event.IniDCSUnitName, '', true )
|
||||
end
|
||||
Event.IniDCSGroupName = ""
|
||||
if Event.IniDCSGroup and Event.IniDCSGroup:isExist() then
|
||||
Event.IniDCSGroupName = Event.IniDCSGroup:getName()
|
||||
Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName )
|
||||
--if Event.IniGroup then
|
||||
Event.IniGroupName = Event.IniDCSGroupName
|
||||
--end
|
||||
end
|
||||
Event.IniPlayerName = Event.IniDCSUnit:getPlayerName()
|
||||
Event.IniCoalition = Event.IniDCSUnit:getCoalition()
|
||||
Event.IniTypeName = Event.IniDCSUnit:getTypeName()
|
||||
Event.IniCategory = Event.IniDCSUnit:getDesc().category
|
||||
end
|
||||
|
||||
if Event.IniObjectCategory == Object.Category.STATIC then
|
||||
if Event.id==31 then
|
||||
--env.info("FF event 31")
|
||||
-- Event.initiator is a Static object representing the pilot. But getName() error due to DCS bug.
|
||||
Event.IniDCSUnit = Event.initiator
|
||||
local ID=Event.initiator.id_
|
||||
Event.IniDCSUnitName = string.format("Ejected Pilot ID %s", tostring(ID))
|
||||
Event.IniUnitName = Event.IniDCSUnitName
|
||||
Event.IniCoalition = 0
|
||||
Event.IniCategory = 0
|
||||
Event.IniTypeName = "Ejected Pilot"
|
||||
else
|
||||
Event.IniDCSUnit = Event.initiator
|
||||
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
||||
Event.IniUnitName = Event.IniDCSUnitName
|
||||
Event.IniUnit = STATIC:FindByName( Event.IniDCSUnitName, false )
|
||||
Event.IniCoalition = Event.IniDCSUnit:getCoalition()
|
||||
Event.IniCategory = Event.IniDCSUnit:getDesc().category
|
||||
Event.IniTypeName = Event.IniDCSUnit:getTypeName()
|
||||
end
|
||||
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.initiator:isExist() and Event.IniDCSUnit:getTypeName() or "SCENERY" -- TODO: Bug fix for 2.1!
|
||||
end
|
||||
end
|
||||
|
||||
if Event.target then
|
||||
|
||||
Event.TgtObjectCategory = Event.target:getCategory()
|
||||
|
||||
if Event.TgtObjectCategory == Object.Category.UNIT then
|
||||
Event.TgtDCSUnit = Event.target
|
||||
Event.TgtDCSGroup = Event.TgtDCSUnit:getGroup()
|
||||
Event.TgtDCSUnitName = Event.TgtDCSUnit:getName()
|
||||
Event.TgtUnitName = Event.TgtDCSUnitName
|
||||
Event.TgtUnit = UNIT:FindByName( Event.TgtDCSUnitName )
|
||||
Event.TgtDCSGroupName = ""
|
||||
if Event.TgtDCSGroup and Event.TgtDCSGroup:isExist() then
|
||||
Event.TgtDCSGroupName = Event.TgtDCSGroup:getName()
|
||||
Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName )
|
||||
--if Event.TgtGroup then
|
||||
Event.TgtGroupName = Event.TgtDCSGroupName
|
||||
--end
|
||||
end
|
||||
Event.TgtPlayerName = Event.TgtDCSUnit:getPlayerName()
|
||||
Event.TgtCoalition = Event.TgtDCSUnit:getCoalition()
|
||||
Event.TgtCategory = Event.TgtDCSUnit:getDesc().category
|
||||
Event.TgtTypeName = Event.TgtDCSUnit:getTypeName()
|
||||
end
|
||||
|
||||
if Event.TgtObjectCategory == Object.Category.STATIC then
|
||||
Event.TgtDCSUnit = Event.target
|
||||
Event.TgtDCSUnitName = Event.TgtDCSUnit:getName()
|
||||
Event.TgtUnitName = 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()
|
||||
end
|
||||
|
||||
if Event.TgtObjectCategory == Object.Category.SCENERY then
|
||||
Event.TgtDCSUnit = Event.target
|
||||
Event.TgtDCSUnitName = Event.TgtDCSUnit:getName()
|
||||
Event.TgtUnitName = Event.TgtDCSUnitName
|
||||
Event.TgtUnit = SCENERY:Register( Event.TgtDCSUnitName, Event.target )
|
||||
Event.TgtCategory = Event.TgtDCSUnit:getDesc().category
|
||||
Event.TgtTypeName = Event.TgtDCSUnit:getTypeName()
|
||||
end
|
||||
end
|
||||
|
||||
if Event.weapon then
|
||||
Event.Weapon = Event.weapon
|
||||
Event.WeaponName = Event.Weapon:getTypeName()
|
||||
Event.WeaponUNIT = CLIENT:Find( Event.Weapon, '', true ) -- Sometimes, the weapon is a player unit!
|
||||
Event.WeaponPlayerName = Event.WeaponUNIT and Event.Weapon:getPlayerName()
|
||||
Event.WeaponCoalition = Event.WeaponUNIT and Event.Weapon:getCoalition()
|
||||
Event.WeaponCategory = Event.WeaponUNIT and Event.Weapon:getDesc().category
|
||||
Event.WeaponTypeName = Event.WeaponUNIT and Event.Weapon:getTypeName()
|
||||
--Event.WeaponTgtDCSUnit = Event.Weapon:getTarget()
|
||||
end
|
||||
|
||||
-- Place should be given for takeoff and landing events as well as base captured. It should be a DCS airbase.
|
||||
if Event.place then
|
||||
if Event.id==EVENTS.LandingAfterEjection then
|
||||
-- Place is here the UNIT of which the pilot ejected.
|
||||
--local name=Event.place:getName() -- This returns a DCS error "Airbase doesn't exit" :(
|
||||
-- However, this is not a big thing, as the aircraft the pilot ejected from is usually long crashed before the ejected pilot touches the ground.
|
||||
--Event.Place=UNIT:Find(Event.place)
|
||||
else
|
||||
Event.Place=AIRBASE:Find(Event.place)
|
||||
Event.PlaceName=Event.Place:GetName()
|
||||
end
|
||||
end
|
||||
|
||||
-- Mark points.
|
||||
if Event.idx then
|
||||
Event.MarkID=Event.idx
|
||||
Event.MarkVec3=Event.pos
|
||||
Event.MarkCoordinate=COORDINATE:NewFromVec3(Event.pos)
|
||||
Event.MarkText=Event.text
|
||||
Event.MarkCoalition=Event.coalition
|
||||
Event.MarkGroupID = Event.groupID
|
||||
end
|
||||
|
||||
if Event.cargo then
|
||||
Event.Cargo = Event.cargo
|
||||
Event.CargoName = Event.cargo.Name
|
||||
end
|
||||
|
||||
if Event.zone then
|
||||
Event.Zone = Event.zone
|
||||
Event.ZoneName = Event.zone.ZoneName
|
||||
end
|
||||
|
||||
local PriorityOrder = EventMeta.Order
|
||||
local PriorityBegin = PriorityOrder == -1 and 5 or 1
|
||||
local PriorityEnd = PriorityOrder == -1 and 1 or 5
|
||||
|
||||
if Event.IniObjectCategory ~= Object.Category.STATIC then
|
||||
self:F( { EventMeta.Text, Event, Event.IniDCSUnitName, Event.TgtDCSUnitName, PriorityOrder } )
|
||||
end
|
||||
|
||||
for EventPriority = PriorityBegin, PriorityEnd, PriorityOrder do
|
||||
|
||||
if self.Events[Event.id][EventPriority] then
|
||||
|
||||
-- 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
|
||||
|
||||
Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName )
|
||||
Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName )
|
||||
|
||||
-- If the EventData is for a UNIT, the call directly the EventClass EventFunction for that UNIT.
|
||||
if EventData.EventUnit then
|
||||
|
||||
-- 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 or
|
||||
Event.id == EVENTS.RemoveUnit then
|
||||
|
||||
local UnitName = EventClass:GetName()
|
||||
|
||||
if ( EventMeta.Side == "I" and UnitName == Event.IniDCSUnitName ) or
|
||||
( EventMeta.Side == "T" and UnitName == Event.TgtDCSUnitName ) then
|
||||
|
||||
-- First test if a EventFunction is Set, otherwise search for the default function
|
||||
if EventData.EventFunction then
|
||||
|
||||
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:F( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } )
|
||||
self:F( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } )
|
||||
end
|
||||
|
||||
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
return EventData.EventFunction( EventClass, Event, unpack( EventData.Params ) )
|
||||
return EventData.EventFunction( EventClass, Event )
|
||||
end, ErrorHandler )
|
||||
|
||||
else
|
||||
@@ -1082,74 +1148,130 @@ function EVENT:onEvent( Event )
|
||||
|
||||
-- Now call the default event function.
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:F( { "Calling " .. EventMeta.Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
self:F( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
end
|
||||
|
||||
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
return EventFunction( EventClass, Event, unpack( EventData.Params ) )
|
||||
return EventFunction( EventClass, Event )
|
||||
end, ErrorHandler )
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
-- The EventClass is not alive anymore, we remove it from the EventHandlers...
|
||||
--self:RemoveEvent( EventClass, Event.id )
|
||||
self:RemoveEvent( EventClass, Event.id )
|
||||
end
|
||||
else
|
||||
|
||||
-- If the EventData is not bound to a specific unit, then call the EventClass EventFunction.
|
||||
-- Note that here the EventFunction will need to implement and determine the logic for the relevant source- or target unit, or weapon.
|
||||
if not EventData.EventUnit then
|
||||
|
||||
-- First test if a EventFunction is Set, otherwise search for the default function
|
||||
if EventData.EventFunction then
|
||||
|
||||
-- There is an EventFunction defined, so call the EventFunction.
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:F2( { "Calling EventFunction for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
end
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
return EventData.EventFunction( EventClass, Event )
|
||||
end, ErrorHandler )
|
||||
else
|
||||
|
||||
-- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object.
|
||||
local EventFunction = EventClass[ EventMeta.Event ]
|
||||
if EventFunction and type( EventFunction ) == "function" then
|
||||
|
||||
-- Now call the default event function.
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:F2( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
else
|
||||
|
||||
--- If the EventData is for a GROUP, the call directly the EventClass EventFunction for the UNIT in that GROUP.
|
||||
if EventData.EventGroup then
|
||||
|
||||
-- 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 or
|
||||
Event.id == EVENTS.RemoveUnit then
|
||||
|
||||
-- We can get the name of the EventClass, which is now always a GROUP object.
|
||||
local GroupName = EventClass:GetName()
|
||||
|
||||
if ( EventMeta.Side == "I" and GroupName == Event.IniDCSGroupName ) or
|
||||
( EventMeta.Side == "T" and GroupName == Event.TgtDCSGroupName ) then
|
||||
|
||||
-- First test if a EventFunction is Set, otherwise search for the default function
|
||||
if EventData.EventFunction then
|
||||
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:F( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } )
|
||||
end
|
||||
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
return EventData.EventFunction( EventClass, Event, unpack( EventData.Params ) )
|
||||
end, ErrorHandler )
|
||||
|
||||
else
|
||||
|
||||
-- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object.
|
||||
local EventFunction = EventClass[ EventMeta.Event ]
|
||||
if EventFunction and type( EventFunction ) == "function" then
|
||||
|
||||
-- Now call the default event function.
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:F( { "Calling " .. EventMeta.Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
end
|
||||
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
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:RemoveEvent( EventClass, Event.id )
|
||||
end
|
||||
else
|
||||
|
||||
-- If the EventData is not bound to a specific unit, then call the EventClass EventFunction.
|
||||
-- Note that here the EventFunction will need to implement and determine the logic for the relevant source- or target unit, or weapon.
|
||||
if not EventData.EventUnit then
|
||||
|
||||
-- First test if a EventFunction is Set, otherwise search for the default function
|
||||
if EventData.EventFunction then
|
||||
|
||||
-- There is an EventFunction defined, so call the EventFunction.
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:F2( { "Calling EventFunction for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
end
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
local Result, Value = EventFunction( EventClass, Event )
|
||||
return Result, Value
|
||||
return EventData.EventFunction( EventClass, Event )
|
||||
end, ErrorHandler )
|
||||
else
|
||||
|
||||
-- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object.
|
||||
local EventFunction = EventClass[ EventMeta.Event ]
|
||||
if EventFunction and type( EventFunction ) == "function" then
|
||||
|
||||
-- Now call the default event function.
|
||||
if Event.IniObjectCategory ~= 3 then
|
||||
self:F2( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||
end
|
||||
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
local Result, Value = EventFunction( EventClass, Event )
|
||||
return Result, Value
|
||||
end, ErrorHandler )
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
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
|
||||
|
||||
-- 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:T( { EventMeta.Text, Event } )
|
||||
end
|
||||
else
|
||||
self:T( { EventMeta.Text, Event } )
|
||||
self:E(string.format("WARNING: Could not get EVENTMETA data for event ID=%d! Is this an unknown/new DCS event?", tostring(Event.id)))
|
||||
end
|
||||
|
||||
Event = nil
|
||||
@@ -1165,7 +1287,7 @@ EVENTHANDLER = {
|
||||
|
||||
--- The EVENTHANDLER constructor
|
||||
-- @param #EVENTHANDLER self
|
||||
-- @return #EVENTHANDLER
|
||||
-- @return #EVENTHANDLER self
|
||||
function EVENTHANDLER:New()
|
||||
self = BASE:Inherit( self, BASE:New() ) -- #EVENTHANDLER
|
||||
return self
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
--- **Core** -- The **FSM** (**F**inite **S**tate **M**achine) class and derived **FSM\_** classes
|
||||
-- are design patterns allowing efficient (long-lasting) processes and workflows.
|
||||
--- **Core** - FSM (Finite State Machine) are objects that model and control long lasting business processes and workflow.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
-- * Provide a base class to model your own state machines.
|
||||
-- * Trigger events synchronously.
|
||||
-- * Trigger events asynchronously.
|
||||
-- * Handle events before or after the event was triggered.
|
||||
-- * Handle state transitions as a result of event before and after the state change.
|
||||
-- * For internal moose purposes, further state machines have been designed:
|
||||
-- - to handle controllables (groups and units).
|
||||
-- - to handle tasks.
|
||||
-- - to handle processes.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -581,7 +594,17 @@ do -- FSM
|
||||
return errmsg
|
||||
end
|
||||
if self[handler] then
|
||||
self:T( "*** FSM *** " .. step .. " *** " .. params[1] .. " --> " .. params[2] .. " --> " .. params[3] )
|
||||
if step == "onafter" or step == "OnAfter" then
|
||||
self:T( ":::>" .. step .. params[2] .. " : " .. params[1] .. " >> " .. params[2] .. ">" .. step .. params[2] .. "()" .. " >> " .. params[3] )
|
||||
elseif step == "onbefore" or step == "OnBefore" then
|
||||
self:T( ":::>" .. step .. params[2] .. " : " .. params[1] .. " >> " .. step .. params[2] .. "()" .. ">" .. params[2] .. " >> " .. params[3] )
|
||||
elseif step == "onenter" or step == "OnEnter" then
|
||||
self:T( ":::>" .. step .. params[3] .. " : " .. params[1] .. " >> " .. params[2] .. " >> " .. step .. params[3] .. "()" .. ">" .. params[3] )
|
||||
elseif step == "onleave" or step == "OnLeave" then
|
||||
self:T( ":::>" .. step .. params[1] .. " : " .. params[1] .. ">" .. step .. params[1] .. "()" .. " >> " .. params[2] .. " >> " .. params[3] )
|
||||
else
|
||||
self:T( ":::>" .. step .. " : " .. params[1] .. " >> " .. params[2] .. " >> " .. params[3] )
|
||||
end
|
||||
self._EventSchedules[EventName] = nil
|
||||
local Result, Value = xpcall( function() return self[handler]( self, unpack( params ) ) end, ErrorHandler )
|
||||
return Value
|
||||
@@ -704,14 +727,17 @@ do -- FSM
|
||||
if DelaySeconds ~= nil then
|
||||
if DelaySeconds < 0 then -- Only call the event ONCE!
|
||||
DelaySeconds = math.abs( DelaySeconds )
|
||||
if not self._EventSchedules[EventName] then
|
||||
CallID = self.CallScheduler:Schedule( self, self._handler, { EventName, ... }, DelaySeconds or 1 )
|
||||
if not self._EventSchedules[EventName] then
|
||||
CallID = self.CallScheduler:Schedule( self, self._handler, { EventName, ... }, DelaySeconds or 1, nil, nil, nil, 4, true )
|
||||
self._EventSchedules[EventName] = CallID
|
||||
self:T2(string.format("NEGATIVE Event %s delayed by %.1f sec SCHEDULED with CallID=%s", EventName, DelaySeconds, tostring(CallID)))
|
||||
else
|
||||
self:T2(string.format("NEGATIVE Event %s delayed by %.1f sec CANCELLED as we already have such an event in the queue.", EventName, DelaySeconds))
|
||||
-- reschedule
|
||||
end
|
||||
else
|
||||
CallID = self.CallScheduler:Schedule( self, self._handler, { EventName, ... }, DelaySeconds or 1 )
|
||||
CallID = self.CallScheduler:Schedule( self, self._handler, { EventName, ... }, DelaySeconds or 1, nil, nil, nil, 4, true )
|
||||
self:T2(string.format("Event %s delayed by %.1f sec SCHEDULED with CallID=%s", EventName, DelaySeconds, tostring(CallID)))
|
||||
end
|
||||
else
|
||||
error( "FSM: An asynchronous event trigger requires a DelaySeconds parameter!!! This can be positive or negative! Sorry, but will not process this." )
|
||||
|
||||
@@ -1,18 +1,28 @@
|
||||
--- **Core (WIP)** -- Base class to allow the modeling of processes to achieve Goals.
|
||||
--- **Core** - Models the process to achieve goal(s).
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- GOAL models processes that have an objective with a defined achievement. Derived classes implement the ways how the achievements can be realized.
|
||||
-- ## Features:
|
||||
--
|
||||
-- * Define the goal.
|
||||
-- * Monitor the goal achievement.
|
||||
-- * Manage goal contribution by players.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- Classes that implement a goal achievement, will derive from GOAL to implement the ways how the achievements can be realized.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions: **funkyfranky**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module Core.Goal
|
||||
-- @image Core_Goal.JPG
|
||||
|
||||
|
||||
do -- Goal
|
||||
|
||||
--- @type GOAL
|
||||
@@ -21,21 +31,43 @@ do -- 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
|
||||
-- # 1. GOAL constructor
|
||||
--
|
||||
-- * @{#GOAL.New}(): Creates a new GOAL object.
|
||||
--
|
||||
-- ## 2. GOAL is a finite state machine (FSM).
|
||||
-- # 2. GOAL is a finite state machine (FSM).
|
||||
--
|
||||
-- ### 2.1 GOAL States
|
||||
-- ## 2.1. GOAL States
|
||||
--
|
||||
-- * **Pending**: The goal object is in progress.
|
||||
-- * **Achieved**: The goal objective is Achieved.
|
||||
--
|
||||
-- ### 2.2 GOAL Events
|
||||
-- ## 2.2. GOAL Events
|
||||
--
|
||||
-- * **Achieved**: Set the goal objective to Achieved.
|
||||
--
|
||||
-- # 3. Player contributions.
|
||||
--
|
||||
-- Goals are most of the time achieved by players. These player achievements can be registered as part of the goal achievement.
|
||||
-- Use @{#GOAL.AddPlayerContribution}() to add a player contribution to the goal.
|
||||
-- The player contributions are based on a points system, an internal counter per player.
|
||||
-- So once the goal has been achieved, the player contributions can be queried using @{#GOAL.GetPlayerContributions}(),
|
||||
-- that retrieves all contributions done by the players. For one player, the contribution can be queried using @{#GOAL.GetPlayerContribution}().
|
||||
-- The total amount of player contributions can be queried using @{#GOAL.GetTotalContributions}().
|
||||
--
|
||||
-- # 4. Goal achievement.
|
||||
--
|
||||
-- Once the goal is achieved, the mission designer will need to trigger the goal achievement using the **Achieved** event.
|
||||
-- The underlying 2 examples will achieve the goals for the `Goal` object:
|
||||
--
|
||||
-- Goal:Achieved() -- Achieve the goal immediately.
|
||||
-- Goal:__Achieved( 30 ) -- Achieve the goal within 30 seconds.
|
||||
--
|
||||
-- # 5. Check goal achievement.
|
||||
--
|
||||
-- The method @{#GOAL.IsAchieved}() will return true if the goal is achieved (the trigger **Achieved** was executed).
|
||||
-- You can use this method to check asynchronously if a goal has been achieved, for example using a scheduler.
|
||||
--
|
||||
-- @field #GOAL
|
||||
GOAL = {
|
||||
ClassName = "GOAL",
|
||||
@@ -111,6 +143,7 @@ do -- Goal
|
||||
-- @param #GOAL self
|
||||
-- @param #string PlayerName The name of the player.
|
||||
function GOAL:AddPlayerContribution( PlayerName )
|
||||
self:F({PlayerName})
|
||||
self.Players[PlayerName] = self.Players[PlayerName] or 0
|
||||
self.Players[PlayerName] = self.Players[PlayerName] + 1
|
||||
self.TotalContributions = self.TotalContributions + 1
|
||||
|
||||
@@ -1,7 +1,27 @@
|
||||
--- **Core** -- MENU_ classes model the definition of **hierarchical menu structures** and **commands for players** within a mission.
|
||||
--- **Core** - Manage hierarchical menu structures and commands for players within a mission.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Features:
|
||||
--
|
||||
-- * Setup mission sub menus.
|
||||
-- * Setup mission command menus.
|
||||
-- * Setup coalition sub menus.
|
||||
-- * Setup coalition command menus.
|
||||
-- * Setup group sub menus.
|
||||
-- * Setup group command menus.
|
||||
-- * Manage menu creation intelligently, avoid double menu creation.
|
||||
-- * Only create or delete menus when required, and keep existing menus persistent.
|
||||
-- * Update menu structures.
|
||||
-- * Refresh menu structures intelligently, based on a time stamp of updates.
|
||||
-- - Delete obscolete menus.
|
||||
-- - Create new one where required.
|
||||
-- - Don't touch the existing ones.
|
||||
-- * Provide a variable amount of parameters to menus.
|
||||
-- * Update the parameters and the receiving methods, without updating the menu within DCS!
|
||||
-- * Provide a great performance boost in menu management.
|
||||
-- * Provide a great tool to manage menus in your code.
|
||||
--
|
||||
-- DCS Menus can be managed using the MENU classes.
|
||||
-- The advantage of using MENU classes is that it hides the complexity of dealing with menu management in more advanced scanerios where you need to
|
||||
-- set menus and later remove them, and later set them again. You'll find while using use normal DCS scripting functions, that setting and removing
|
||||
@@ -92,7 +112,7 @@ end
|
||||
---
|
||||
-- @param Wrapper.Group#GROUP Group
|
||||
function MENU_INDEX:PrepareGroup( Group )
|
||||
if Group and Group:IsAlive() then
|
||||
if Group and Group:IsAlive() ~= nil then -- something was changed here!
|
||||
local GroupName = Group:GetName()
|
||||
self.Group[GroupName] = self.Group[GroupName] or {}
|
||||
self.Group[GroupName].Menus = self.Group[GroupName].Menus or {}
|
||||
@@ -146,6 +166,7 @@ end
|
||||
function MENU_INDEX:SetGroupMenu( Group, Path, Menu )
|
||||
|
||||
local MenuGroupName = Group:GetName()
|
||||
Group:F({MenuGroupName=MenuGroupName,Path=Path})
|
||||
self.Group[MenuGroupName].Menus[Path] = Menu
|
||||
end
|
||||
|
||||
@@ -217,7 +238,7 @@ do -- MENU_BASE
|
||||
self.Path = ( self.ParentMenu and "@" .. table.concat( self.MenuParentPath or {}, "@" ) or "" ) .. "@" .. self.MenuText
|
||||
self.Menus = {}
|
||||
self.MenuCount = 0
|
||||
self.MenuTime = timer.getTime()
|
||||
self.MenuStamp = timer.getTime()
|
||||
self.MenuRemoveParent = false
|
||||
|
||||
if self.ParentMenu then
|
||||
@@ -264,13 +285,31 @@ do -- MENU_BASE
|
||||
function MENU_BASE:GetMenu( MenuText )
|
||||
return self.Menus[MenuText]
|
||||
end
|
||||
|
||||
--- Sets a menu stamp for later prevention of menu removal.
|
||||
-- @param #MENU_BASE self
|
||||
-- @param MenuStamp
|
||||
-- @return #MENU_BASE
|
||||
function MENU_BASE:SetStamp( MenuStamp )
|
||||
self.MenuStamp = MenuStamp
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Gets a menu stamp for later prevention of menu removal.
|
||||
-- @param #MENU_BASE self
|
||||
-- @return MenuStamp
|
||||
function MENU_BASE:GetStamp()
|
||||
return timer.getTime()
|
||||
end
|
||||
|
||||
|
||||
--- Sets a time stamp for later prevention of menu removal.
|
||||
-- @param #MENU_BASE self
|
||||
-- @param MenuTime
|
||||
-- @param MenuStamp
|
||||
-- @return #MENU_BASE
|
||||
function MENU_BASE:SetTime( MenuTime )
|
||||
self.MenuTime = MenuTime
|
||||
function MENU_BASE:SetTime( MenuStamp )
|
||||
self.MenuStamp = MenuStamp
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -422,7 +461,7 @@ do -- MENU_MISSION
|
||||
--- Removes the main menu and the sub menus recursively of this MENU_MISSION.
|
||||
-- @param #MENU_MISSION self
|
||||
-- @return #nil
|
||||
function MENU_MISSION:Remove( MenuTime, MenuTag )
|
||||
function MENU_MISSION:Remove( MenuStamp, MenuTag )
|
||||
|
||||
MENU_INDEX:PrepareMission()
|
||||
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
|
||||
@@ -430,7 +469,7 @@ do -- MENU_MISSION
|
||||
|
||||
if MissionMenu == self then
|
||||
self:RemoveSubMenus()
|
||||
if not MenuTime or self.MenuTime ~= MenuTime then
|
||||
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
||||
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
|
||||
self:F( { Text = self.MenuText, Path = self.MenuPath } )
|
||||
if self.MenuPath ~= nil then
|
||||
@@ -516,7 +555,7 @@ do -- MENU_MISSION_COMMAND
|
||||
local MissionMenu = MENU_INDEX:HasMissionMenu( Path )
|
||||
|
||||
if MissionMenu == self then
|
||||
if not MenuTime or self.MenuTime ~= MenuTime then
|
||||
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
||||
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
|
||||
self:F( { Text = self.MenuText, Path = self.MenuPath } )
|
||||
if self.MenuPath ~= nil then
|
||||
@@ -645,7 +684,7 @@ do -- MENU_COALITION
|
||||
--- Removes the main menu and the sub menus recursively of this MENU_COALITION.
|
||||
-- @param #MENU_COALITION self
|
||||
-- @return #nil
|
||||
function MENU_COALITION:Remove( MenuTime, MenuTag )
|
||||
function MENU_COALITION:Remove( MenuStamp, MenuTag )
|
||||
|
||||
MENU_INDEX:PrepareCoalition( self.Coalition )
|
||||
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
|
||||
@@ -653,7 +692,7 @@ do -- MENU_COALITION
|
||||
|
||||
if CoalitionMenu == self then
|
||||
self:RemoveSubMenus()
|
||||
if not MenuTime or self.MenuTime ~= MenuTime then
|
||||
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
||||
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
|
||||
self:F( { Coalition = self.Coalition, Text = self.MenuText, Path = self.MenuPath } )
|
||||
if self.MenuPath ~= nil then
|
||||
@@ -737,14 +776,14 @@ do -- MENU_COALITION_COMMAND
|
||||
--- Removes a radio command item for a coalition
|
||||
-- @param #MENU_COALITION_COMMAND self
|
||||
-- @return #nil
|
||||
function MENU_COALITION_COMMAND:Remove( MenuTime, MenuTag )
|
||||
function MENU_COALITION_COMMAND:Remove( MenuStamp, MenuTag )
|
||||
|
||||
MENU_INDEX:PrepareCoalition( self.Coalition )
|
||||
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
|
||||
local CoalitionMenu = MENU_INDEX:HasCoalitionMenu( self.Coalition, Path )
|
||||
|
||||
if CoalitionMenu == self then
|
||||
if not MenuTime or self.MenuTime ~= MenuTime then
|
||||
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
||||
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
|
||||
self:F( { Coalition = self.Coalition, Text = self.MenuText, Path = self.MenuPath } )
|
||||
if self.MenuPath ~= nil then
|
||||
@@ -886,13 +925,13 @@ do
|
||||
|
||||
--- Removes the sub menus recursively of this MENU_GROUP.
|
||||
-- @param #MENU_GROUP self
|
||||
-- @param MenuTime
|
||||
-- @param MenuStamp
|
||||
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
|
||||
-- @return #MENU_GROUP self
|
||||
function MENU_GROUP:RemoveSubMenus( MenuTime, MenuTag )
|
||||
function MENU_GROUP:RemoveSubMenus( MenuStamp, MenuTag )
|
||||
|
||||
for MenuText, Menu in pairs( self.Menus or {} ) do
|
||||
Menu:Remove( MenuTime, MenuTag )
|
||||
Menu:Remove( MenuStamp, MenuTag )
|
||||
end
|
||||
|
||||
self.Menus = nil
|
||||
@@ -902,18 +941,18 @@ do
|
||||
|
||||
--- Removes the main menu and sub menus recursively of this MENU_GROUP.
|
||||
-- @param #MENU_GROUP self
|
||||
-- @param MenuTime
|
||||
-- @param MenuStamp
|
||||
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
|
||||
-- @return #nil
|
||||
function MENU_GROUP:Remove( MenuTime, MenuTag )
|
||||
function MENU_GROUP:Remove( MenuStamp, MenuTag )
|
||||
|
||||
MENU_INDEX:PrepareGroup( self.Group )
|
||||
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
|
||||
local GroupMenu = MENU_INDEX:HasGroupMenu( self.Group, Path )
|
||||
|
||||
if GroupMenu == self then
|
||||
self:RemoveSubMenus( MenuTime, MenuTag )
|
||||
if not MenuTime or self.MenuTime ~= MenuTime then
|
||||
self:RemoveSubMenus( MenuStamp, MenuTag )
|
||||
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
||||
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
|
||||
if self.MenuPath ~= nil then
|
||||
self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } )
|
||||
@@ -993,17 +1032,17 @@ do
|
||||
|
||||
--- Removes a menu structure for a group.
|
||||
-- @param #MENU_GROUP_COMMAND self
|
||||
-- @param MenuTime
|
||||
-- @param MenuStamp
|
||||
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
|
||||
-- @return #nil
|
||||
function MENU_GROUP_COMMAND:Remove( MenuTime, MenuTag )
|
||||
function MENU_GROUP_COMMAND:Remove( MenuStamp, MenuTag )
|
||||
|
||||
MENU_INDEX:PrepareGroup( self.Group )
|
||||
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
|
||||
local GroupMenu = MENU_INDEX:HasGroupMenu( self.Group, Path )
|
||||
|
||||
if GroupMenu == self then
|
||||
if not MenuTime or self.MenuTime ~= MenuTime then
|
||||
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
||||
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
|
||||
if self.MenuPath ~= nil then
|
||||
self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } )
|
||||
@@ -1115,13 +1154,13 @@ do
|
||||
|
||||
--- Removes the sub menus recursively of this MENU_GROUP_DELAYED.
|
||||
-- @param #MENU_GROUP_DELAYED self
|
||||
-- @param MenuTime
|
||||
-- @param MenuStamp
|
||||
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
|
||||
-- @return #MENU_GROUP_DELAYED self
|
||||
function MENU_GROUP_DELAYED:RemoveSubMenus( MenuTime, MenuTag )
|
||||
function MENU_GROUP_DELAYED:RemoveSubMenus( MenuStamp, MenuTag )
|
||||
|
||||
for MenuText, Menu in pairs( self.Menus or {} ) do
|
||||
Menu:Remove( MenuTime, MenuTag )
|
||||
Menu:Remove( MenuStamp, MenuTag )
|
||||
end
|
||||
|
||||
self.Menus = nil
|
||||
@@ -1131,18 +1170,18 @@ do
|
||||
|
||||
--- Removes the main menu and sub menus recursively of this MENU_GROUP.
|
||||
-- @param #MENU_GROUP_DELAYED self
|
||||
-- @param MenuTime
|
||||
-- @param MenuStamp
|
||||
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
|
||||
-- @return #nil
|
||||
function MENU_GROUP_DELAYED:Remove( MenuTime, MenuTag )
|
||||
function MENU_GROUP_DELAYED:Remove( MenuStamp, MenuTag )
|
||||
|
||||
MENU_INDEX:PrepareGroup( self.Group )
|
||||
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
|
||||
local GroupMenu = MENU_INDEX:HasGroupMenu( self.Group, Path )
|
||||
|
||||
if GroupMenu == self then
|
||||
self:RemoveSubMenus( MenuTime, MenuTag )
|
||||
if not MenuTime or self.MenuTime ~= MenuTime then
|
||||
self:RemoveSubMenus( MenuStamp, MenuTag )
|
||||
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
||||
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
|
||||
if self.MenuPath ~= nil then
|
||||
self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } )
|
||||
@@ -1242,17 +1281,17 @@ do
|
||||
|
||||
--- Removes a menu structure for a group.
|
||||
-- @param #MENU_GROUP_COMMAND_DELAYED self
|
||||
-- @param MenuTime
|
||||
-- @param MenuStamp
|
||||
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
|
||||
-- @return #nil
|
||||
function MENU_GROUP_COMMAND_DELAYED:Remove( MenuTime, MenuTag )
|
||||
function MENU_GROUP_COMMAND_DELAYED:Remove( MenuStamp, MenuTag )
|
||||
|
||||
MENU_INDEX:PrepareGroup( self.Group )
|
||||
local Path = MENU_INDEX:ParentPath( self.ParentMenu, self.MenuText )
|
||||
local GroupMenu = MENU_INDEX:HasGroupMenu( self.Group, Path )
|
||||
|
||||
if GroupMenu == self then
|
||||
if not MenuTime or self.MenuTime ~= MenuTime then
|
||||
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
||||
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
|
||||
if self.MenuPath ~= nil then
|
||||
self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } )
|
||||
|
||||
@@ -1,4 +1,15 @@
|
||||
--- **Core** -- MESSAGE class takes are of the **real-time notifications** and **messages to players** during a simulation.
|
||||
--- **Core** - Informs the players using messages during a simulation.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
-- * A more advanced messaging system using the DCS message system.
|
||||
-- * Time messages.
|
||||
-- * Send messages based on a message type, which has a pre-defined duration that can be tweaked in SETTINGS.
|
||||
-- * Send message to all players.
|
||||
-- * Send messages to a coalition.
|
||||
-- * Send messages to a specific group.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
--- **Core** -- Defines an **extensive API** to **manage 3D points** in the DCS World 3D simulation space.
|
||||
--- **Core** - Defines an extensive API to manage 3D points in the DCS World 3D simulation space.
|
||||
--
|
||||
-- **Features:**
|
||||
-- ## Features:
|
||||
--
|
||||
-- * Provides a COORDINATE class, which allows to manage points in 3D space and perform various operations on it.
|
||||
-- * Provides a POINT\_VEC2 class, which is derived from COORDINATE, and allows to manage points in 3D space, but from a Lat/Lon and Altitude perspective.
|
||||
@@ -210,6 +210,7 @@ do -- COORDINATE
|
||||
FromParkingAreaHot = "From Parking Area Hot",
|
||||
FromRunway = "From Runway",
|
||||
Landing = "Landing",
|
||||
LandingReFuAr = "LandingReFuAr",
|
||||
}
|
||||
|
||||
--- @field COORDINATE.WaypointType
|
||||
@@ -219,6 +220,7 @@ do -- COORDINATE
|
||||
TakeOff = "TakeOffParkingHot",
|
||||
TurningPoint = "Turning Point",
|
||||
Land = "Land",
|
||||
LandingReFuAr = "LandingReFuAr",
|
||||
}
|
||||
|
||||
|
||||
@@ -342,20 +344,20 @@ do -- COORDINATE
|
||||
return x - Precision <= self.x and x + Precision >= self.x and z - Precision <= self.z and z + Precision >= self.z
|
||||
end
|
||||
|
||||
--- Returns if the 2 coordinates are at the same 2D position.
|
||||
--- Scan/find objects (units, statics, scenery) within a certain radius around the coordinate using the world.searchObjects() DCS API function.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #number radius (Optional) Scan radius in meters. Default 100 m.
|
||||
-- @param #boolean scanunits (Optional) If true scan for units. Default true.
|
||||
-- @param #boolean scanstatics (Optional) If true scan for static objects. Default true.
|
||||
-- @param #boolean scanscenery (Optional) If true scan for scenery objects. Default false.
|
||||
-- @return True if units were found.
|
||||
-- @return True if statics were found.
|
||||
-- @return True if scenery objects were found.
|
||||
-- @return Unit objects found.
|
||||
-- @return Static objects found.
|
||||
-- @return Scenery objects found.
|
||||
-- @return #boolean True if units were found.
|
||||
-- @return #boolean True if statics were found.
|
||||
-- @return #boolean True if scenery objects were found.
|
||||
-- @return #table Table of MOOSE @[#Wrapper.Unit#UNIT} objects found.
|
||||
-- @return #table Table of DCS static objects found.
|
||||
-- @return #table Table of DCS scenery objects found.
|
||||
function COORDINATE:ScanObjects(radius, scanunits, scanstatics, scanscenery)
|
||||
self:F(string.format("Scanning in radius %.1f m.", radius))
|
||||
self:F(string.format("Scanning in radius %.1f m.", radius or 100))
|
||||
|
||||
local SphereSearch = {
|
||||
id = world.VolumeType.SPHERE,
|
||||
@@ -405,18 +407,17 @@ do -- COORDINATE
|
||||
local ObjectCategory = ZoneObject:getCategory()
|
||||
|
||||
-- Check for unit or static objects
|
||||
--if (ObjectCategory == Object.Category.UNIT and ZoneObject:isExist() and ZoneObject:isActive()) then
|
||||
if (ObjectCategory == Object.Category.UNIT and ZoneObject:isExist()) then
|
||||
if ObjectCategory==Object.Category.UNIT and ZoneObject:isExist() then
|
||||
|
||||
table.insert(Units, UNIT:Find(ZoneObject))
|
||||
gotunits=true
|
||||
|
||||
elseif (ObjectCategory == Object.Category.STATIC and ZoneObject:isExist()) then
|
||||
elseif ObjectCategory==Object.Category.STATIC and ZoneObject:isExist() then
|
||||
|
||||
table.insert(Statics, ZoneObject)
|
||||
gotstatics=true
|
||||
|
||||
elseif ObjectCategory == Object.Category.SCENERY then
|
||||
elseif ObjectCategory==Object.Category.SCENERY then
|
||||
|
||||
table.insert(Scenery, ZoneObject)
|
||||
gotscenery=true
|
||||
@@ -436,13 +437,56 @@ do -- COORDINATE
|
||||
end
|
||||
for _,static in pairs(Statics) do
|
||||
self:T(string.format("Scan found static %s", static:getName()))
|
||||
_DATABASE:AddStatic(static:getName())
|
||||
end
|
||||
for _,scenery in pairs(Scenery) do
|
||||
self:T(string.format("Scan found scenery %s", scenery:getTypeName()))
|
||||
self:T(string.format("Scan found scenery %s typename=%s", scenery:getName(), scenery:getTypeName()))
|
||||
SCENERY:Register(scenery:getName(), scenery)
|
||||
end
|
||||
|
||||
return gotunits, gotstatics, gotscenery, Units, Statics, Scenery
|
||||
end
|
||||
|
||||
--- Scan/find UNITS within a certain radius around the coordinate using the world.searchObjects() DCS API function.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #number radius (Optional) Scan radius in meters. Default 100 m.
|
||||
-- @return Core.Set#SET_UNIT Set of units.
|
||||
function COORDINATE:ScanUnits(radius)
|
||||
|
||||
local _,_,_,units=self:ScanObjects(radius, true, false, false)
|
||||
|
||||
local set=SET_UNIT:New()
|
||||
|
||||
for _,unit in pairs(units) do
|
||||
set:AddUnit(unit)
|
||||
end
|
||||
|
||||
return set
|
||||
end
|
||||
|
||||
--- Find the closest unit to the COORDINATE within a certain radius.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #number radius Scan radius in meters. Default 100 m.
|
||||
-- @return Wrapper.Unit#UNIT The closest unit or #nil if no unit is inside the given radius.
|
||||
function COORDINATE:FindClosestUnit(radius)
|
||||
|
||||
local units=self:ScanUnits(radius)
|
||||
|
||||
local umin=nil --Wrapper.Unit#UNIT
|
||||
local dmin=math.huge
|
||||
for _,_unit in pairs(units.Set) do
|
||||
local unit=_unit --Wrapper.Unit#UNIT
|
||||
local coordinate=unit:GetCoordinate()
|
||||
local d=self:Get2DDistance(coordinate)
|
||||
if d<dmin then
|
||||
dmin=d
|
||||
umin=unit
|
||||
end
|
||||
end
|
||||
|
||||
return umin
|
||||
end
|
||||
|
||||
|
||||
--- Calculate the distance from a reference @{#COORDINATE}.
|
||||
-- @param #COORDINATE self
|
||||
@@ -460,18 +504,47 @@ do -- COORDINATE
|
||||
--- Add a Distance in meters from the COORDINATE orthonormal plane, with the given angle, and calculate the new COORDINATE.
|
||||
-- @param #COORDINATE self
|
||||
-- @param DCS#Distance Distance The Distance to be added in meters.
|
||||
-- @param DCS#Angle Angle The Angle in degrees.
|
||||
-- @return #COORDINATE The new calculated COORDINATE.
|
||||
function COORDINATE:Translate( Distance, Angle )
|
||||
-- @param DCS#Angle Angle The Angle in degrees. Defaults to 0 if not specified (nil).
|
||||
-- @param #boolean Keepalt If true, keep altitude of original coordinate. Default is that the new coordinate is created at the translated land height.
|
||||
-- @return Core.Point#COORDINATE The new calculated COORDINATE.
|
||||
function COORDINATE:Translate( Distance, Angle, Keepalt )
|
||||
local SX = self.x
|
||||
local SY = self.z
|
||||
local Radians = Angle / 180 * math.pi
|
||||
local Radians = (Angle or 0) / 180 * math.pi
|
||||
local TX = Distance * math.cos( Radians ) + SX
|
||||
local TY = Distance * math.sin( Radians ) + SY
|
||||
|
||||
return COORDINATE:NewFromVec2( { x = TX, y = TY } )
|
||||
|
||||
if Keepalt then
|
||||
return COORDINATE:NewFromVec3( { x = TX, y=self.y, z = TY } )
|
||||
else
|
||||
return COORDINATE:NewFromVec2( { x = TX, y = TY } )
|
||||
end
|
||||
end
|
||||
|
||||
--- Rotate coordinate in 2D (x,z) space.
|
||||
-- @param #COORDINATE self
|
||||
-- @param DCS#Angle Angle Angle of rotation in degrees.
|
||||
-- @return Core.Point#COORDINATE The rotated coordinate.
|
||||
function COORDINATE:Rotate2D(Angle)
|
||||
|
||||
if not Angle then
|
||||
return self
|
||||
end
|
||||
|
||||
local phi=math.rad(Angle)
|
||||
|
||||
local X=self.z
|
||||
local Y=self.x
|
||||
|
||||
--slocal R=math.sqrt(X*X+Y*Y)
|
||||
|
||||
local x=X*math.cos(phi)-Y*math.sin(phi)
|
||||
local y=X*math.sin(phi)+Y*math.cos(phi)
|
||||
|
||||
-- Coordinate assignment looks bit strange but is correct.
|
||||
return COORDINATE:NewFromVec3({x=y, y=self.y, z=x})
|
||||
end
|
||||
|
||||
--- Return a random Vec2 within an Outer Radius and optionally NOT within an Inner Radius of the COORDINATE.
|
||||
-- @param #COORDINATE self
|
||||
-- @param DCS#Distance OuterRadius
|
||||
@@ -532,7 +605,7 @@ do -- COORDINATE
|
||||
|
||||
--- Return the height of the land at the coordinate.
|
||||
-- @param #COORDINATE self
|
||||
-- @return #number
|
||||
-- @return #number Land height (ASL) in meters.
|
||||
function COORDINATE:GetLandHeight()
|
||||
local Vec2 = { x = self.x, y = self.z }
|
||||
return land.getHeight( Vec2 )
|
||||
@@ -623,6 +696,28 @@ do -- COORDINATE
|
||||
return Angle
|
||||
end
|
||||
|
||||
--- Return an intermediate COORDINATE between this an another coordinate.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #COORDINATE ToCoordinate The other coordinate.
|
||||
-- @param #number Fraction The fraction (0,1) where the new coordinate is created. Default 0.5, i.e. in the middle.
|
||||
-- @return #COORDINATE Coordinate between this and the other coordinate.
|
||||
function COORDINATE:GetIntermediateCoordinate( ToCoordinate, Fraction )
|
||||
|
||||
local f=Fraction or 0.5
|
||||
|
||||
-- Get the vector from A to B
|
||||
local vec=UTILS.VecSubstract(ToCoordinate, self)
|
||||
|
||||
-- Scale the vector.
|
||||
vec.x=f*vec.x
|
||||
vec.y=f*vec.y
|
||||
vec.z=f*vec.z
|
||||
|
||||
-- Move the vector to start at the end of A.
|
||||
vec=UTILS.VecAdd(self, vec)
|
||||
|
||||
return self:New(vec.x,vec.y,vec.z)
|
||||
end
|
||||
|
||||
--- Return the 2D distance in meters between the target COORDINATE and the COORDINATE.
|
||||
-- @param #COORDINATE self
|
||||
@@ -767,6 +862,24 @@ do -- COORDINATE
|
||||
-- Return wind direction and strength km/h.
|
||||
return direction, strength
|
||||
end
|
||||
|
||||
--- Returns the wind direction (from) and strength.
|
||||
-- @param #COORDINATE self
|
||||
-- @param height (Optional) parameter specifying the height ASL. The minimum height will be always be the land height since the wind is zero below the ground.
|
||||
-- @return Direction the wind is blowing from in degrees.
|
||||
function COORDINATE:GetWindWithTurbulenceVec3(height)
|
||||
|
||||
-- AGL height if
|
||||
local landheight=self:GetLandHeight()+0.1 -- we at 0.1 meters to be sure to be above ground since wind is zero below ground level.
|
||||
|
||||
-- Point at which the wind is evaluated.
|
||||
local point={x=self.x, y=math.max(height or self.y, landheight), z=self.z}
|
||||
|
||||
-- Get wind velocity vector including turbulences.
|
||||
local vec3 = atmosphere.getWindWithTurbulence(point)
|
||||
|
||||
return vec3
|
||||
end
|
||||
|
||||
|
||||
--- Returns a text documenting the wind direction (from) and strength according the measurement system @{Settings}.
|
||||
@@ -819,7 +932,7 @@ do -- COORDINATE
|
||||
-- @param #number Precision The precision.
|
||||
-- @param Core.Settings#SETTINGS Settings
|
||||
-- @return #string The bearing text in degrees.
|
||||
function COORDINATE:GetBearingText( AngleRadians, Precision, Settings )
|
||||
function COORDINATE:GetBearingText( AngleRadians, Precision, Settings, Language )
|
||||
|
||||
local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS
|
||||
|
||||
@@ -835,16 +948,25 @@ do -- COORDINATE
|
||||
-- @param #number Distance The distance in meters.
|
||||
-- @param Core.Settings#SETTINGS Settings
|
||||
-- @return #string The distance text expressed in the units of measurement.
|
||||
function COORDINATE:GetDistanceText( Distance, Settings )
|
||||
function COORDINATE:GetDistanceText( Distance, Settings, Language )
|
||||
|
||||
local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS
|
||||
local Language = Language or "EN"
|
||||
|
||||
local DistanceText
|
||||
|
||||
if Settings:IsMetric() then
|
||||
DistanceText = " for " .. UTILS.Round( Distance / 1000, 2 ) .. " km"
|
||||
if Language == "EN" then
|
||||
DistanceText = " for " .. UTILS.Round( Distance / 1000, 2 ) .. " km"
|
||||
elseif Language == "RU" then
|
||||
DistanceText = " за " .. UTILS.Round( Distance / 1000, 2 ) .. " километров"
|
||||
end
|
||||
else
|
||||
DistanceText = " for " .. UTILS.Round( UTILS.MetersToNM( Distance ), 2 ) .. " miles"
|
||||
if Language == "EN" then
|
||||
DistanceText = " for " .. UTILS.Round( UTILS.MetersToNM( Distance ), 2 ) .. " miles"
|
||||
elseif Language == "RU" then
|
||||
DistanceText = " за " .. UTILS.Round( UTILS.MetersToNM( Distance ), 2 ) .. " миль"
|
||||
end
|
||||
end
|
||||
|
||||
return DistanceText
|
||||
@@ -853,14 +975,24 @@ do -- COORDINATE
|
||||
--- Return the altitude text of the COORDINATE.
|
||||
-- @param #COORDINATE self
|
||||
-- @return #string Altitude text.
|
||||
function COORDINATE:GetAltitudeText( Settings )
|
||||
function COORDINATE:GetAltitudeText( Settings, Language )
|
||||
local Altitude = self.y
|
||||
local Settings = Settings or _SETTINGS
|
||||
local Language = Language or "EN"
|
||||
|
||||
if Altitude ~= 0 then
|
||||
if Settings:IsMetric() then
|
||||
return " at " .. UTILS.Round( self.y, -3 ) .. " meters"
|
||||
if Language == "EN" then
|
||||
return " at " .. UTILS.Round( self.y, -3 ) .. " meters"
|
||||
elseif Language == "RU" then
|
||||
return " в " .. UTILS.Round( self.y, -3 ) .. " метры"
|
||||
end
|
||||
else
|
||||
return " at " .. UTILS.Round( UTILS.MetersToFeet( self.y ), -3 ) .. " feet"
|
||||
if Language == "EN" then
|
||||
return " at " .. UTILS.Round( UTILS.MetersToFeet( self.y ), -3 ) .. " feet"
|
||||
elseif Language == "RU" then
|
||||
return " в " .. UTILS.Round( self.y, -3 ) .. " ноги"
|
||||
end
|
||||
end
|
||||
else
|
||||
return ""
|
||||
@@ -906,12 +1038,12 @@ do -- COORDINATE
|
||||
-- @param #number Distance The distance
|
||||
-- @param Core.Settings#SETTINGS Settings
|
||||
-- @return #string The BR Text
|
||||
function COORDINATE:GetBRText( AngleRadians, Distance, Settings )
|
||||
function COORDINATE:GetBRText( AngleRadians, Distance, Settings, Language )
|
||||
|
||||
local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS
|
||||
|
||||
local BearingText = self:GetBearingText( AngleRadians, 0, Settings )
|
||||
local DistanceText = self:GetDistanceText( Distance, Settings )
|
||||
local BearingText = self:GetBearingText( AngleRadians, 0, Settings, Language )
|
||||
local DistanceText = self:GetDistanceText( Distance, Settings, Language )
|
||||
|
||||
local BRText = BearingText .. DistanceText
|
||||
|
||||
@@ -924,13 +1056,13 @@ do -- COORDINATE
|
||||
-- @param #number Distance The distance
|
||||
-- @param Core.Settings#SETTINGS Settings
|
||||
-- @return #string The BRA Text
|
||||
function COORDINATE:GetBRAText( AngleRadians, Distance, Settings )
|
||||
function COORDINATE:GetBRAText( AngleRadians, Distance, Settings, Language )
|
||||
|
||||
local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS
|
||||
|
||||
local BearingText = self:GetBearingText( AngleRadians, 0, Settings )
|
||||
local DistanceText = self:GetDistanceText( Distance, Settings )
|
||||
local AltitudeText = self:GetAltitudeText( Settings )
|
||||
local BearingText = self:GetBearingText( AngleRadians, 0, Settings, Language )
|
||||
local DistanceText = self:GetDistanceText( Distance, Settings, Language )
|
||||
local AltitudeText = self:GetAltitudeText( Settings, Language )
|
||||
|
||||
local BRAText = BearingText .. DistanceText .. AltitudeText -- When the POINT is a VEC2, there will be no altitude shown.
|
||||
|
||||
@@ -981,15 +1113,20 @@ do -- COORDINATE
|
||||
-- @param Wrapper.Airbase#AIRBASE airbase The airbase for takeoff and landing points.
|
||||
-- @param #table DCSTasks A table of @{DCS#Task} items which are executed at the waypoint.
|
||||
-- @param #string description A text description of the waypoint, which will be shown on the F10 map.
|
||||
-- @param #number timeReFuAr Time in minutes the aircraft stays at the airport for ReFueling and ReArming.
|
||||
-- @return #table The route point.
|
||||
function COORDINATE:WaypointAir( AltType, Type, Action, Speed, SpeedLocked, airbase, DCSTasks, description )
|
||||
function COORDINATE:WaypointAir( AltType, Type, Action, Speed, SpeedLocked, airbase, DCSTasks, description, timeReFuAr )
|
||||
self:F2( { AltType, Type, Action, Speed, SpeedLocked } )
|
||||
|
||||
-- Defaults
|
||||
-- Set alttype or "RADIO" which is AGL.
|
||||
AltType=AltType or "RADIO"
|
||||
|
||||
-- Speedlocked by default
|
||||
if SpeedLocked==nil then
|
||||
SpeedLocked=true
|
||||
end
|
||||
|
||||
-- Speed or default 500 km/h.
|
||||
Speed=Speed or 500
|
||||
|
||||
-- Waypoint array.
|
||||
@@ -998,52 +1135,60 @@ do -- COORDINATE
|
||||
-- Coordinates.
|
||||
RoutePoint.x = self.x
|
||||
RoutePoint.y = self.z
|
||||
|
||||
-- Altitude.
|
||||
RoutePoint.alt = self.y
|
||||
RoutePoint.alt_type = AltType
|
||||
|
||||
-- Waypoint type.
|
||||
RoutePoint.type = Type or nil
|
||||
RoutePoint.action = Action or nil
|
||||
-- Set speed/ETA.
|
||||
RoutePoint.action = Action or nil
|
||||
|
||||
-- Speed.
|
||||
RoutePoint.speed = Speed/3.6
|
||||
RoutePoint.speed_locked = SpeedLocked
|
||||
RoutePoint.ETA=nil
|
||||
RoutePoint.ETA_locked = false
|
||||
|
||||
-- ETA.
|
||||
RoutePoint.ETA=0
|
||||
RoutePoint.ETA_locked=true
|
||||
|
||||
-- Waypoint description.
|
||||
RoutePoint.name=description
|
||||
|
||||
-- Airbase parameters for takeoff and landing points.
|
||||
if airbase then
|
||||
local AirbaseID = airbase:GetID()
|
||||
local AirbaseCategory = airbase:GetDesc().category
|
||||
local AirbaseCategory = airbase:GetAirbaseCategory()
|
||||
if AirbaseCategory == Airbase.Category.SHIP or AirbaseCategory == Airbase.Category.HELIPAD then
|
||||
RoutePoint.linkUnit = AirbaseID
|
||||
RoutePoint.helipadId = AirbaseID
|
||||
elseif AirbaseCategory == Airbase.Category.AIRDROME then
|
||||
RoutePoint.airdromeId = AirbaseID
|
||||
RoutePoint.airdromeId = AirbaseID
|
||||
else
|
||||
self:T("ERROR: Unknown airbase category in COORDINATE:WaypointAir()!")
|
||||
end
|
||||
end
|
||||
self:E("ERROR: Unknown airbase category in COORDINATE:WaypointAir()!")
|
||||
end
|
||||
end
|
||||
|
||||
-- Time in minutes to stay at the airbase before resuming route.
|
||||
if Type==COORDINATE.WaypointType.LandingReFuAr then
|
||||
RoutePoint.timeReFuAr=timeReFuAr or 10
|
||||
end
|
||||
|
||||
|
||||
-- ["task"] =
|
||||
-- {
|
||||
-- ["id"] = "ComboTask",
|
||||
-- ["params"] =
|
||||
-- {
|
||||
-- ["tasks"] =
|
||||
-- {
|
||||
-- }, -- end of ["tasks"]
|
||||
-- }, -- end of ["params"]
|
||||
-- }, -- end of ["task"]
|
||||
|
||||
-- Waypoint tasks.
|
||||
RoutePoint.task = {}
|
||||
RoutePoint.task.id = "ComboTask"
|
||||
RoutePoint.task.params = {}
|
||||
RoutePoint.task.params.tasks = DCSTasks or {}
|
||||
|
||||
--RoutePoint.properties={}
|
||||
--RoutePoint.properties.addopt={}
|
||||
|
||||
--RoutePoint.formation_template=""
|
||||
|
||||
-- Debug.
|
||||
self:T({RoutePoint=RoutePoint})
|
||||
|
||||
-- Return waypoint.
|
||||
return RoutePoint
|
||||
end
|
||||
|
||||
@@ -1103,6 +1248,9 @@ do -- COORDINATE
|
||||
--- Build a Waypoint Air "Landing".
|
||||
-- @param #COORDINATE self
|
||||
-- @param DCS#Speed Speed Airspeed in km/h.
|
||||
-- @param Wrapper.Airbase#AIRBASE airbase The airbase for takeoff and landing points.
|
||||
-- @param #table DCSTasks A table of @{DCS#Task} items which are executed at the waypoint.
|
||||
-- @param #string description A text description of the waypoint, which will be shown on the F10 map.
|
||||
-- @return #table The route point.
|
||||
-- @usage
|
||||
--
|
||||
@@ -1111,50 +1259,88 @@ do -- COORDINATE
|
||||
-- LandingWaypoint = LandingCoord:WaypointAirLanding( 60 )
|
||||
-- HeliGroup:Route( { LandWaypoint }, 1 ) -- Start landing the helicopter in one second.
|
||||
--
|
||||
function COORDINATE:WaypointAirLanding( Speed )
|
||||
return self:WaypointAir( nil, COORDINATE.WaypointType.Land, COORDINATE.WaypointAction.Landing, Speed )
|
||||
function COORDINATE:WaypointAirLanding( Speed, airbase, DCSTasks, description )
|
||||
return self:WaypointAir(nil, COORDINATE.WaypointType.Land, COORDINATE.WaypointAction.Landing, Speed, false, airbase, DCSTasks, description)
|
||||
end
|
||||
|
||||
|
||||
--- Build a Waypoint Air "LandingReFuAr". Mimics the aircraft ReFueling and ReArming.
|
||||
-- @param #COORDINATE self
|
||||
-- @param DCS#Speed Speed Airspeed in km/h.
|
||||
-- @param Wrapper.Airbase#AIRBASE airbase The airbase for takeoff and landing points.
|
||||
-- @param #number timeReFuAr Time in minutes, the aircraft stays at the airbase. Default 10 min.
|
||||
-- @param #table DCSTasks A table of @{DCS#Task} items which are executed at the waypoint.
|
||||
-- @param #string description A text description of the waypoint, which will be shown on the F10 map.
|
||||
-- @return #table The route point.
|
||||
function COORDINATE:WaypointAirLandingReFu( Speed, airbase, timeReFuAr, DCSTasks, description )
|
||||
return self:WaypointAir(nil, COORDINATE.WaypointType.LandingReFuAr, COORDINATE.WaypointAction.LandingReFuAr, Speed, false, airbase, DCSTasks, description, timeReFuAr or 10)
|
||||
end
|
||||
|
||||
|
||||
--- Build an ground type route point.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #number Speed (optional) Speed in km/h. The default speed is 20 km/h.
|
||||
-- @param #string Formation (optional) The route point Formation, which is a text string that specifies exactly the Text in the Type of the route point, like "Vee", "Echelon Right".
|
||||
-- @param #number Speed (Optional) Speed in km/h. The default speed is 20 km/h.
|
||||
-- @param #string Formation (Optional) The route point Formation, which is a text string that specifies exactly the Text in the Type of the route point, like "Vee", "Echelon Right".
|
||||
-- @param #table DCSTasks (Optional) A table of DCS tasks that are executed at the waypoints. Mind the curly brackets {}!
|
||||
-- @return #table The route point.
|
||||
function COORDINATE:WaypointGround( Speed, Formation )
|
||||
self:F2( { Formation, Speed } )
|
||||
function COORDINATE:WaypointGround( Speed, Formation, DCSTasks )
|
||||
self:F2( { Speed, Formation, DCSTasks } )
|
||||
|
||||
|
||||
local RoutePoint = {}
|
||||
RoutePoint.x = self.x
|
||||
RoutePoint.y = self.z
|
||||
|
||||
RoutePoint.action = Formation or ""
|
||||
--RoutePoint.formation_template = Formation and "" or nil
|
||||
|
||||
|
||||
RoutePoint.x = self.x
|
||||
RoutePoint.y = self.z
|
||||
|
||||
RoutePoint.alt = self:GetLandHeight()+1 -- self.y
|
||||
RoutePoint.alt_type = COORDINATE.WaypointAltType.BARO
|
||||
|
||||
RoutePoint.action = Formation or "Off Road"
|
||||
RoutePoint.formation_template=""
|
||||
|
||||
RoutePoint.ETA=0
|
||||
RoutePoint.ETA_locked=true
|
||||
|
||||
RoutePoint.speed = ( Speed or 20 ) / 3.6
|
||||
RoutePoint.speed_locked = true
|
||||
|
||||
-- ["task"] =
|
||||
-- {
|
||||
-- ["id"] = "ComboTask",
|
||||
-- ["params"] =
|
||||
-- {
|
||||
-- ["tasks"] =
|
||||
-- {
|
||||
-- }, -- end of ["tasks"]
|
||||
-- }, -- end of ["params"]
|
||||
-- }, -- end of ["task"]
|
||||
RoutePoint.task = {}
|
||||
RoutePoint.task.id = "ComboTask"
|
||||
RoutePoint.task.params = {}
|
||||
RoutePoint.task.params.tasks = DCSTasks or {}
|
||||
|
||||
return RoutePoint
|
||||
end
|
||||
|
||||
--- Build route waypoint point for Naval units.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #number Speed (Optional) Speed in km/h. The default speed is 20 km/h.
|
||||
-- @param #string Depth (Optional) Dive depth in meters. Only for submarines. Default is COORDINATE.y component.
|
||||
-- @param #table DCSTasks (Optional) A table of DCS tasks that are executed at the waypoints. Mind the curly brackets {}!
|
||||
-- @return #table The route point.
|
||||
function COORDINATE:WaypointNaval( Speed, Depth, DCSTasks )
|
||||
self:F2( { Speed, Depth, DCSTasks } )
|
||||
|
||||
local RoutePoint = {}
|
||||
|
||||
RoutePoint.x = self.x
|
||||
RoutePoint.y = self.z
|
||||
|
||||
RoutePoint.alt = Depth or self.y -- Depth is for submarines only. Ships should have alt=0.
|
||||
RoutePoint.alt_type = "BARO"
|
||||
|
||||
RoutePoint.type = "Turning Point"
|
||||
RoutePoint.action = "Turning Point"
|
||||
RoutePoint.formation_template = ""
|
||||
|
||||
RoutePoint.ETA=0
|
||||
RoutePoint.ETA_locked=true
|
||||
|
||||
RoutePoint.speed = ( Speed or 20 ) / 3.6
|
||||
RoutePoint.speed_locked = true
|
||||
|
||||
RoutePoint.task = {}
|
||||
RoutePoint.task.id = "ComboTask"
|
||||
RoutePoint.task.params = {}
|
||||
RoutePoint.task.params.tasks = {}
|
||||
|
||||
RoutePoint.task.params.tasks = DCSTasks or {}
|
||||
|
||||
return RoutePoint
|
||||
end
|
||||
@@ -1175,17 +1361,23 @@ do -- COORDINATE
|
||||
-- Loop over all airbases.
|
||||
for _,_airbase in pairs(airbases) do
|
||||
local airbase=_airbase --Wrapper.Airbase#AIRBASE
|
||||
local category=airbase:GetDesc().category
|
||||
if Category and Category==category or Category==nil then
|
||||
local dist=self:Get2DDistance(airbase:GetCoordinate())
|
||||
if closest==nil then
|
||||
distmin=dist
|
||||
closest=airbase
|
||||
else
|
||||
if dist<distmin then
|
||||
if airbase then
|
||||
local category=airbase:GetAirbaseCategory()
|
||||
if Category and Category==category or Category==nil then
|
||||
|
||||
-- Distance to airbase.
|
||||
local dist=self:Get2DDistance(airbase:GetCoordinate())
|
||||
|
||||
if closest==nil then
|
||||
distmin=dist
|
||||
closest=airbase
|
||||
end
|
||||
else
|
||||
if dist<distmin then
|
||||
distmin=dist
|
||||
closest=airbase
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1201,6 +1393,7 @@ do -- COORDINATE
|
||||
-- @return Core.Point#COORDINATE Coordinate of the nearest parking spot.
|
||||
-- @return #number Terminal ID.
|
||||
-- @return #number Distance to closest parking spot in meters.
|
||||
-- @return Wrapper.Airbase#AIRBASE#ParkingSpot Parking spot table.
|
||||
function COORDINATE:GetClosestParkingSpot(airbase, terminaltype, free)
|
||||
|
||||
-- Get airbase table.
|
||||
@@ -1215,6 +1408,7 @@ do -- COORDINATE
|
||||
local _closest=nil --Core.Point#COORDINATE
|
||||
local _termID=nil
|
||||
local _distmin=nil
|
||||
local spot=nil --Wrapper.Airbase#AIRBASE.ParkingSpot
|
||||
|
||||
-- Loop over all airbases.
|
||||
for _,_airbase in pairs(airbases) do
|
||||
@@ -1234,11 +1428,13 @@ do -- COORDINATE
|
||||
_closest=_coord
|
||||
_distmin=_dist
|
||||
_termID=_spot.TerminalID
|
||||
spot=_spot
|
||||
else
|
||||
if _dist<_distmin then
|
||||
_distmin=_dist
|
||||
_closest=_coord
|
||||
_termID=_spot.TerminalID
|
||||
spot=_spot
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1246,7 +1442,7 @@ do -- COORDINATE
|
||||
end
|
||||
end
|
||||
|
||||
return _closest, _termID, _distmin
|
||||
return _closest, _termID, _distmin, spot
|
||||
end
|
||||
|
||||
--- Gets the nearest free parking spot.
|
||||
@@ -1383,17 +1579,70 @@ do -- COORDINATE
|
||||
return surface
|
||||
end
|
||||
|
||||
--- Checks if the surface type is on land.
|
||||
-- @param #COORDINATE self
|
||||
-- @return #boolean If true, the surface type at the coordinate is land.
|
||||
function COORDINATE:IsSurfaceTypeLand()
|
||||
return self:GetSurfaceType()==land.SurfaceType.LAND
|
||||
end
|
||||
|
||||
--- Checks if the surface type is road.
|
||||
-- @param #COORDINATE self
|
||||
-- @return #boolean If true, the surface type at the coordinate is land.
|
||||
function COORDINATE:IsSurfaceTypeLand()
|
||||
return self:GetSurfaceType()==land.SurfaceType.LAND
|
||||
end
|
||||
|
||||
|
||||
--- Checks if the surface type is road.
|
||||
-- @param #COORDINATE self
|
||||
-- @return #boolean If true, the surface type at the coordinate is a road.
|
||||
function COORDINATE:IsSurfaceTypeRoad()
|
||||
return self:GetSurfaceType()==land.SurfaceType.ROAD
|
||||
end
|
||||
|
||||
--- Checks if the surface type is runway.
|
||||
-- @param #COORDINATE self
|
||||
-- @return #boolean If true, the surface type at the coordinate is a runway or taxi way.
|
||||
function COORDINATE:IsSurfaceTypeRunway()
|
||||
return self:GetSurfaceType()==land.SurfaceType.RUNWAY
|
||||
end
|
||||
|
||||
--- Checks if the surface type is shallow water.
|
||||
-- @param #COORDINATE self
|
||||
-- @return #boolean If true, the surface type at the coordinate is a shallow water.
|
||||
function COORDINATE:IsSurfaceTypeShallowWater()
|
||||
return self:GetSurfaceType()==land.SurfaceType.SHALLOW_WATER
|
||||
end
|
||||
|
||||
--- Checks if the surface type is water.
|
||||
-- @param #COORDINATE self
|
||||
-- @return #boolean If true, the surface type at the coordinate is a deep water.
|
||||
function COORDINATE:IsSurfaceTypeWater()
|
||||
return self:GetSurfaceType()==land.SurfaceType.WATER
|
||||
end
|
||||
|
||||
|
||||
--- Creates an explosion at the point of a certain intensity.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #number ExplosionIntensity
|
||||
function COORDINATE:Explosion( ExplosionIntensity )
|
||||
-- @param #number ExplosionIntensity Intensity of the explosion in kg TNT. Default 100 kg.
|
||||
-- @param #number Delay Delay before explosion in seconds.
|
||||
-- @return #COORDINATE self
|
||||
function COORDINATE:Explosion( ExplosionIntensity, Delay )
|
||||
self:F2( { ExplosionIntensity } )
|
||||
trigger.action.explosion( self:GetVec3(), ExplosionIntensity )
|
||||
ExplosionIntensity=ExplosionIntensity or 100
|
||||
if Delay and Delay>0 then
|
||||
SCHEDULER:New(nil, self.Explosion, {self,ExplosionIntensity}, Delay)
|
||||
else
|
||||
trigger.action.explosion( self:GetVec3(), ExplosionIntensity )
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Creates an illumination bomb at the point.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #number power
|
||||
-- @param #number power Power of illumination bomb in Candela.
|
||||
-- @return #COORDINATE self
|
||||
function COORDINATE:IlluminationBomb(power)
|
||||
self:F2()
|
||||
trigger.action.illuminationBomb( self:GetVec3(), power )
|
||||
@@ -1690,7 +1939,7 @@ do -- COORDINATE
|
||||
|
||||
--- Returns if a Coordinate is in a certain Radius of this Coordinate in 2D plane using the X and Z axis.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #COORDINATE ToCoordinate The coordinate that will be tested if it is in the radius of this coordinate.
|
||||
-- @param #COORDINATE Coordinate The coordinate that will be tested if it is in the radius of this coordinate.
|
||||
-- @param #number Radius The radius of the circle on the 2D plane around this coordinate.
|
||||
-- @return #boolean true if in the Radius.
|
||||
function COORDINATE:IsInRadius( Coordinate, Radius )
|
||||
@@ -1723,7 +1972,8 @@ do -- COORDINATE
|
||||
|
||||
--- Return a BR string from a COORDINATE to the COORDINATE.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #COORDINATE TargetCoordinate The target COORDINATE.
|
||||
-- @param #COORDINATE FromCoordinate The coordinate to measure the distance and the bearing from.
|
||||
-- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object.
|
||||
-- @return #string The BR text.
|
||||
function COORDINATE:ToStringBR( FromCoordinate, Settings )
|
||||
local DirectionVec3 = FromCoordinate:GetDirectionVec3( self )
|
||||
@@ -1734,19 +1984,21 @@ do -- COORDINATE
|
||||
|
||||
--- Return a BRAA string from a COORDINATE to the COORDINATE.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #COORDINATE TargetCoordinate The target COORDINATE.
|
||||
-- @param #COORDINATE FromCoordinate The coordinate to measure the distance and the bearing from.
|
||||
-- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object.
|
||||
-- @return #string The BR text.
|
||||
function COORDINATE:ToStringBRA( FromCoordinate, Settings )
|
||||
function COORDINATE:ToStringBRA( FromCoordinate, Settings, Language )
|
||||
local DirectionVec3 = FromCoordinate:GetDirectionVec3( self )
|
||||
local AngleRadians = self:GetAngleRadians( DirectionVec3 )
|
||||
local Distance = FromCoordinate:Get2DDistance( self )
|
||||
local Altitude = self:GetAltitudeText()
|
||||
return "BRA, " .. self:GetBRAText( AngleRadians, Distance, Settings )
|
||||
return "BRA, " .. self:GetBRAText( AngleRadians, Distance, Settings, Language )
|
||||
end
|
||||
|
||||
--- Return a BULLS string out of the BULLS of the coalition to the COORDINATE.
|
||||
-- @param #COORDINATE self
|
||||
-- @param DCS#coalition.side Coalition The coalition.
|
||||
-- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object.
|
||||
-- @return #string The BR text.
|
||||
function COORDINATE:ToStringBULLS( Coalition, Settings )
|
||||
local BullsCoordinate = COORDINATE:NewFromVec3( coalition.getMainRefPoint( Coalition ) )
|
||||
@@ -1786,46 +2038,48 @@ do -- COORDINATE
|
||||
|
||||
--- Provides a Lat Lon string in Degree Minute Second format.
|
||||
-- @param #COORDINATE self
|
||||
-- @param Core.Settings#SETTINGS Settings (optional) Settings
|
||||
-- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object.
|
||||
-- @return #string The LL DMS Text
|
||||
function COORDINATE:ToStringLLDMS( Settings )
|
||||
|
||||
local LL_Accuracy = Settings and Settings.LL_Accuracy or _SETTINGS.LL_Accuracy
|
||||
local lat, lon = coord.LOtoLL( self:GetVec3() )
|
||||
return "LL DMS, " .. UTILS.tostringLL( lat, lon, LL_Accuracy, true )
|
||||
return "LL DMS " .. UTILS.tostringLL( lat, lon, LL_Accuracy, true )
|
||||
end
|
||||
|
||||
--- Provides a Lat Lon string in Degree Decimal Minute format.
|
||||
-- @param #COORDINATE self
|
||||
-- @param Core.Settings#SETTINGS Settings (optional) Settings
|
||||
-- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object.
|
||||
-- @return #string The LL DDM Text
|
||||
function COORDINATE:ToStringLLDDM( Settings )
|
||||
|
||||
local LL_Accuracy = Settings and Settings.LL_Accuracy or _SETTINGS.LL_Accuracy
|
||||
local lat, lon = coord.LOtoLL( self:GetVec3() )
|
||||
return "LL DDM, " .. UTILS.tostringLL( lat, lon, LL_Accuracy, false )
|
||||
return "LL DDM " .. UTILS.tostringLL( lat, lon, LL_Accuracy, false )
|
||||
end
|
||||
|
||||
--- Provides a MGRS string
|
||||
-- @param #COORDINATE self
|
||||
-- @param Core.Settings#SETTINGS Settings (optional) Settings
|
||||
-- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object.
|
||||
-- @return #string The MGRS Text
|
||||
function COORDINATE:ToStringMGRS( Settings ) --R2.1 Fixes issue #424.
|
||||
|
||||
local MGRS_Accuracy = Settings and Settings.MGRS_Accuracy or _SETTINGS.MGRS_Accuracy
|
||||
local lat, lon = coord.LOtoLL( self:GetVec3() )
|
||||
local MGRS = coord.LLtoMGRS( lat, lon )
|
||||
return "MGRS, " .. UTILS.tostringMGRS( MGRS, MGRS_Accuracy )
|
||||
return "MGRS " .. UTILS.tostringMGRS( MGRS, MGRS_Accuracy )
|
||||
end
|
||||
|
||||
--- Provides a coordinate string of the point, based on a coordinate format system:
|
||||
-- * Uses default settings in COORDINATE.
|
||||
-- * Can be overridden if for a GROUP containing x clients, a menu was selected to override the default.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #COORDINATE ReferenceCoord The refrence coordinate.
|
||||
-- @param #string ReferenceName The refrence name.
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable
|
||||
-- @param Core.Settings#SETTINGS Settings
|
||||
-- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object.
|
||||
-- @return #string The coordinate Text in the configured coordinate system.
|
||||
function COORDINATE:ToStringFromRP( ReferenceCoord, ReferenceName, Controllable, Settings ) -- R2.2
|
||||
function COORDINATE:ToStringFromRP( ReferenceCoord, ReferenceName, Controllable, Settings )
|
||||
|
||||
self:F2( { ReferenceCoord = ReferenceCoord, ReferenceName = ReferenceName } )
|
||||
|
||||
@@ -1852,9 +2106,9 @@ do -- COORDINATE
|
||||
--- Provides a coordinate string of the point, based on the A2G coordinate format system.
|
||||
-- @param #COORDINATE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable
|
||||
-- @param Core.Settings#SETTINGS Settings
|
||||
-- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object.
|
||||
-- @return #string The coordinate Text in the configured coordinate system.
|
||||
function COORDINATE:ToStringA2G( Controllable, Settings ) -- R2.2
|
||||
function COORDINATE:ToStringA2G( Controllable, Settings )
|
||||
|
||||
self:F2( { Controllable = Controllable and Controllable:GetName() } )
|
||||
|
||||
@@ -1887,9 +2141,9 @@ do -- COORDINATE
|
||||
--- Provides a coordinate string of the point, based on the A2A coordinate format system.
|
||||
-- @param #COORDINATE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable
|
||||
-- @param Core.Settings#SETTINGS Settings
|
||||
-- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object.
|
||||
-- @return #string The coordinate Text in the configured coordinate system.
|
||||
function COORDINATE:ToStringA2A( Controllable, Settings ) -- R2.2
|
||||
function COORDINATE:ToStringA2A( Controllable, Settings, Language ) -- R2.2
|
||||
|
||||
self:F2( { Controllable = Controllable and Controllable:GetName() } )
|
||||
|
||||
@@ -1898,23 +2152,23 @@ do -- COORDINATE
|
||||
if Settings:IsA2A_BRAA() then
|
||||
if Controllable then
|
||||
local Coordinate = Controllable:GetCoordinate()
|
||||
return self:ToStringBRA( Coordinate, Settings )
|
||||
return self:ToStringBRA( Coordinate, Settings, Language )
|
||||
else
|
||||
return self:ToStringMGRS( Settings )
|
||||
return self:ToStringMGRS( Settings, Language )
|
||||
end
|
||||
end
|
||||
if Settings:IsA2A_BULLS() then
|
||||
local Coalition = Controllable:GetCoalition()
|
||||
return self:ToStringBULLS( Coalition, Settings )
|
||||
return self:ToStringBULLS( Coalition, Settings, Language )
|
||||
end
|
||||
if Settings:IsA2A_LL_DMS() then
|
||||
return self:ToStringLLDMS( Settings )
|
||||
return self:ToStringLLDMS( Settings, Language )
|
||||
end
|
||||
if Settings:IsA2A_LL_DDM() then
|
||||
return self:ToStringLLDDM( Settings )
|
||||
return self:ToStringLLDDM( Settings, Language )
|
||||
end
|
||||
if Settings:IsA2A_MGRS() then
|
||||
return self:ToStringMGRS( Settings )
|
||||
return self:ToStringMGRS( Settings, Language )
|
||||
end
|
||||
|
||||
return nil
|
||||
@@ -1925,37 +2179,38 @@ do -- COORDINATE
|
||||
-- * Uses default settings in COORDINATE.
|
||||
-- * Can be overridden if for a GROUP containing x clients, a menu was selected to override the default.
|
||||
-- @param #COORDINATE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable
|
||||
-- @param Core.Settings#SETTINGS Settings
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The controllable to retrieve the settings from, otherwise the default settings will be chosen.
|
||||
-- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object.
|
||||
-- @param Tasking.Task#TASK Task The task for which coordinates need to be calculated.
|
||||
-- @return #string The coordinate Text in the configured coordinate system.
|
||||
function COORDINATE:ToString( Controllable, Settings, Task )
|
||||
|
||||
self:F2( { Controllable = Controllable and Controllable:GetName() } )
|
||||
-- self:E( { Controllable = Controllable and Controllable:GetName() } )
|
||||
|
||||
local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS
|
||||
|
||||
local ModeA2A = false
|
||||
self:E('A2A false')
|
||||
local ModeA2A = nil
|
||||
|
||||
if Task then
|
||||
self:E('Task ' .. Task.ClassName )
|
||||
if Task:IsInstanceOf( TASK_A2A ) then
|
||||
ModeA2A = true
|
||||
self:E('A2A true')
|
||||
else
|
||||
if Task:IsInstanceOf( TASK_A2G ) then
|
||||
ModeA2A = false
|
||||
else
|
||||
if Task:IsInstanceOf( TASK_CARGO ) then
|
||||
ModeA2A = false
|
||||
else
|
||||
ModeA2A = false
|
||||
end
|
||||
if Task:IsInstanceOf( TASK_CAPTURE_ZONE ) then
|
||||
ModeA2A = false
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
local IsAir = Controllable and Controllable:IsAirPlane() or false
|
||||
end
|
||||
|
||||
|
||||
if ModeA2A == nil then
|
||||
local IsAir = Controllable and ( Controllable:IsAirPlane() or Controllable:IsHelicopter() ) or false
|
||||
if IsAir then
|
||||
ModeA2A = true
|
||||
else
|
||||
@@ -1979,7 +2234,7 @@ do -- COORDINATE
|
||||
-- * Can be overridden if for a GROUP containing x clients, a menu was selected to override the default.
|
||||
-- @param #COORDINATE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable
|
||||
-- @param Core.Settings#SETTINGS Settings
|
||||
-- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object.
|
||||
-- @return #string The pressure text in the configured measurement system.
|
||||
function COORDINATE:ToStringPressure( Controllable, Settings ) -- R2.3
|
||||
|
||||
@@ -1995,9 +2250,9 @@ do -- COORDINATE
|
||||
-- * Can be overridden if for a GROUP containing x clients, a menu was selected to override the default.
|
||||
-- @param #COORDINATE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable
|
||||
-- @param Core.Settings#SETTINGS Settings
|
||||
-- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object.
|
||||
-- @return #string The wind text in the configured measurement system.
|
||||
function COORDINATE:ToStringWind( Controllable, Settings ) -- R2.3
|
||||
function COORDINATE:ToStringWind( Controllable, Settings )
|
||||
|
||||
self:F2( { Controllable = Controllable and Controllable:GetName() } )
|
||||
|
||||
@@ -2011,9 +2266,9 @@ do -- COORDINATE
|
||||
-- * Can be overridden if for a GROUP containing x clients, a menu was selected to override the default.
|
||||
-- @param #COORDINATE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable
|
||||
-- @param Core.Settings#SETTINGS Settings
|
||||
-- @param Core.Settings#SETTINGS
|
||||
-- @return #string The temperature text in the configured measurement system.
|
||||
function COORDINATE:ToStringTemperature( Controllable, Settings ) -- R2.3
|
||||
function COORDINATE:ToStringTemperature( Controllable, Settings )
|
||||
|
||||
self:F2( { Controllable = Controllable and Controllable:GetName() } )
|
||||
|
||||
|
||||
@@ -1,40 +1,45 @@
|
||||
--- **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** - Is responsible for everything that is related to radio transmission and you can hear in DCS, be it TACAN beacons, Radio transmissions.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
-- * Provide radio functionality to broadcast radio transmissions.
|
||||
-- * Provide beacon functionality to assist pilots.
|
||||
--
|
||||
-- The Radio contains 2 classes : RADIO and BEACON
|
||||
--
|
||||
-- What are radio communications in DCS ?
|
||||
-- What are radio communications in DCS?
|
||||
--
|
||||
-- * Radio transmissions consist of **sound files** that are broadcasted on a specific **frequency** (e.g. 115MHz) and **modulation** (e.g. AM),
|
||||
-- * They can be **subtitled** for a specific **duration**, the **power** in Watts of the transmiter's antenna can be set, and the transmission can be **looped**.
|
||||
--
|
||||
-- How to supply DCS my own Sound Files ?
|
||||
-- How to supply DCS my own Sound Files?
|
||||
--
|
||||
-- * Your sound files need to be encoded in **.ogg** or .wav,
|
||||
-- * Your sound files should be **as tiny as possible**. It is suggested you encode in .ogg with low bitrate and sampling settings,
|
||||
-- * They need to be added in .\l10n\DEFAULT\ in you .miz file (wich can be decompressed like a .zip file),
|
||||
-- * For simplicty sake, you can **let DCS' Mission Editor add the file** itself, by creating a new Trigger with the action "Sound to Country", and choosing your sound file and a country you don't use in your mission.
|
||||
-- * For simplicity sake, you can **let DCS' Mission Editor add the file** itself, by creating a new Trigger with the action "Sound to Country", and choosing your sound file and a country you don't use in your mission.
|
||||
--
|
||||
-- Due to weird DCS quirks, **radio communications behave differently** if sent by a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP} or by any other @{Wrapper.Positionable#POSITIONABLE}
|
||||
--
|
||||
-- * If the transmitter is a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}, DCS will set the power of the transmission automatically,
|
||||
-- * If the transmitter is a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}, DCS will set the power of the transmission automatically,
|
||||
-- * If the transmitter is any other @{Wrapper.Positionable#POSITIONABLE}, the transmisison can't be subtitled or looped.
|
||||
--
|
||||
-- Note that obviously, the **frequency** and the **modulation** of the transmission are important only if the players are piloting an **Advanced System Modelling** enabled aircraft,
|
||||
-- like the A10C or the Mirage 2000C. They will **hear the transmission** if they are tuned on the **right frequency and modulation** (and if they are close enough - more on that below).
|
||||
-- If a FC3 airacraft is used, it will **hear every communication, whatever the frequency and the modulation** is set to. The same is true for TACAN beacons. If your aircaft isn't compatible,
|
||||
-- If an FC3 aircraft is used, it will **hear every communication, whatever the frequency and the modulation** is set to. The same is true for TACAN beacons. If your aircraft isn't compatible,
|
||||
-- you won't hear/be able to use the TACAN beacon informations.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: Hugues "Grey_Echo" Bousquet
|
||||
-- ### Authors: Hugues "Grey_Echo" Bousquet, funkyfranky
|
||||
--
|
||||
-- @module Core.Radio
|
||||
-- @image Core_Radio.JPG
|
||||
|
||||
|
||||
--- Models the radio capabilty.
|
||||
--- Models the radio capability.
|
||||
--
|
||||
-- ## RADIO usage
|
||||
--
|
||||
@@ -51,34 +56,35 @@
|
||||
-- * @{#RADIO.SetModulation}() : Sets the modulation of your transmission.
|
||||
-- * @{#RADIO.SetLoop}() : Choose if you want the transmission to be looped. If you need your transmission to be looped, you might need a @{#BEACON} instead...
|
||||
--
|
||||
-- Additional Methods to set relevant parameters if the transmiter is a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}
|
||||
-- Additional Methods to set relevant parameters if the transmitter is a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}
|
||||
--
|
||||
-- * @{#RADIO.SetSubtitle}() : Set both the subtitle and its duration,
|
||||
-- * @{#RADIO.NewUnitTransmission}() : Shortcut to set all the relevant parameters in one method call
|
||||
--
|
||||
-- Additional Methods to set relevant parameters if the transmiter is any other @{Wrapper.Positionable#POSITIONABLE}
|
||||
-- Additional Methods to set relevant parameters if the transmitter is any other @{Wrapper.Positionable#POSITIONABLE}
|
||||
--
|
||||
-- * @{#RADIO.SetPower}() : Sets the power of the antenna in Watts
|
||||
-- * @{#RADIO.NewGenericTransmission}() : Shortcut to set all the relevant parameters in one method call
|
||||
--
|
||||
-- What is this power thing ?
|
||||
-- What is this power thing?
|
||||
--
|
||||
-- * If your transmission is sent by a @{Wrapper.Positionable#POSITIONABLE} other than a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}, you can set the power of the antenna,
|
||||
-- * Otherwise, DCS sets it automatically, depending on what's available on your Unit,
|
||||
-- * If the player gets **too far** from the transmiter, or if the antenna is **too weak**, the transmission will **fade** and **become noisyer**,
|
||||
-- * If the player gets **too far** from the transmitter, or if the antenna is **too weak**, the transmission will **fade** and **become noisyer**,
|
||||
-- * This an automated DCS calculation you have no say on,
|
||||
-- * For reference, a standard VOR station has a 100W antenna, a standard AA TACAN has a 120W antenna, and civilian ATC's antenna usually range between 300 and 500W,
|
||||
-- * For reference, a standard VOR station has a 100 W antenna, a standard AA TACAN has a 120 W antenna, and civilian ATC's antenna usually range between 300 and 500 W,
|
||||
-- * Note that if the transmission has a subtitle, it will be readable, regardless of the quality of the transmission.
|
||||
--
|
||||
-- @type RADIO
|
||||
-- @field Positionable#POSITIONABLE Positionable The transmiter
|
||||
-- @field #string FileName Name of the sound file
|
||||
-- @field #number Frequency Frequency of the transmission in Hz
|
||||
-- @field #number Modulation Modulation of the transmission (either radio.modulation.AM or radio.modulation.FM)
|
||||
-- @field #string Subtitle Subtitle of the transmission
|
||||
-- @field #number SubtitleDuration Duration of the Subtitle in seconds
|
||||
-- @field #number Power Power of the antenna is Watts
|
||||
-- @field #boolean Loop (default true)
|
||||
-- @field Wrapper.Controllable#CONTROLLABLE Positionable The @{#CONTROLLABLE} that will transmit the radio calls.
|
||||
-- @field #string FileName Name of the sound file played.
|
||||
-- @field #number Frequency Frequency of the transmission in Hz.
|
||||
-- @field #number Modulation Modulation of the transmission (either radio.modulation.AM or radio.modulation.FM).
|
||||
-- @field #string Subtitle Subtitle of the transmission.
|
||||
-- @field #number SubtitleDuration Duration of the Subtitle in seconds.
|
||||
-- @field #number Power Power of the antenna is Watts.
|
||||
-- @field #boolean Loop Transmission is repeated (default true).
|
||||
-- @field #string alias Name of the radio transmitter.
|
||||
-- @extends Core.Base#BASE
|
||||
RADIO = {
|
||||
ClassName = "RADIO",
|
||||
@@ -88,19 +94,19 @@ RADIO = {
|
||||
Subtitle = "",
|
||||
SubtitleDuration = 0,
|
||||
Power = 100,
|
||||
Loop = true,
|
||||
Loop = false,
|
||||
alias=nil,
|
||||
}
|
||||
|
||||
--- Create a new RADIO Object. This doesn't broadcast a transmission, though, use @{#RADIO.Broadcast} to actually broadcast
|
||||
-- If you want to create a RADIO, you probably should use @{Wrapper.Positionable#POSITIONABLE.GetRadio}() instead
|
||||
--- Create a new RADIO Object. This doesn't broadcast a transmission, though, use @{#RADIO.Broadcast} to actually broadcast.
|
||||
-- If you want to create a RADIO, you probably should use @{Wrapper.Positionable#POSITIONABLE.GetRadio}() instead.
|
||||
-- @param #RADIO self
|
||||
-- @param Wrapper.Positionable#POSITIONABLE Positionable The @{Positionable} that will receive radio capabilities.
|
||||
-- @return #RADIO Radio
|
||||
-- @return #nil If Positionable is invalid
|
||||
-- @return #RADIO The RADIO object or #nil if Positionable is invalid.
|
||||
function RADIO:New(Positionable)
|
||||
|
||||
-- Inherit base
|
||||
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
|
||||
@@ -108,11 +114,27 @@ function RADIO:New(Positionable)
|
||||
return self
|
||||
end
|
||||
|
||||
self:E({"The passed positionable is invalid, no RADIO created", Positionable})
|
||||
self:E({error="The passed positionable is invalid, no RADIO created!", positionable=Positionable})
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Check validity of the filename passed and sets RADIO.FileName
|
||||
--- Set alias of the transmitter.
|
||||
-- @param #RADIO self
|
||||
-- @param #string alias Name of the radio transmitter.
|
||||
-- @return #RADIO self
|
||||
function RADIO:SetAlias(alias)
|
||||
self.alias=tostring(alias)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get alias of the transmitter.
|
||||
-- @param #RADIO self
|
||||
-- @return #string Name of the transmitter.
|
||||
function RADIO:GetAlias()
|
||||
return tostring(self.alias)
|
||||
end
|
||||
|
||||
--- Set the file name for the radio transmission.
|
||||
-- @param #RADIO self
|
||||
-- @param #string FileName File name of the sound file (i.e. "Noise.ogg")
|
||||
-- @return #RADIO self
|
||||
@@ -120,49 +142,63 @@ function RADIO:SetFileName(FileName)
|
||||
self:F2(FileName)
|
||||
|
||||
if type(FileName) == "string" then
|
||||
|
||||
if FileName:find(".ogg") or FileName:find(".wav") then
|
||||
if not FileName:find("l10n/DEFAULT/") then
|
||||
FileName = "l10n/DEFAULT/" .. FileName
|
||||
end
|
||||
|
||||
self.FileName = FileName
|
||||
return self
|
||||
end
|
||||
end
|
||||
|
||||
self:E({"File name invalid. Maybe something wrong with the extension ?", self.FileName})
|
||||
self:E({"File name invalid. Maybe something wrong with the extension?", FileName})
|
||||
return self
|
||||
end
|
||||
|
||||
--- Check validity of the frequency passed and sets RADIO.Frequency
|
||||
--- Set the frequency for the radio transmission.
|
||||
-- If the transmitting positionable is a unit or group, this also set the command "SetFrequency" with the defined frequency and modulation.
|
||||
-- @param #RADIO self
|
||||
-- @param #number Frequency in MHz (Ranges allowed for radio transmissions in DCS : 30-88 / 108-152 / 225-400MHz)
|
||||
-- @param #number Frequency Frequency in MHz. Ranges allowed for radio transmissions in DCS : 30-87.995 / 108-173.995 / 225-399.975MHz.
|
||||
-- @return #RADIO self
|
||||
function RADIO:SetFrequency(Frequency)
|
||||
self:F2(Frequency)
|
||||
|
||||
if type(Frequency) == "number" then
|
||||
|
||||
-- If frequency is in range
|
||||
if (Frequency >= 30 and Frequency < 88) or (Frequency >= 108 and Frequency < 152) or (Frequency >= 225 and Frequency < 400) then
|
||||
self.Frequency = Frequency * 1000000 -- Conversion in Hz
|
||||
if (Frequency >= 30 and Frequency <= 87.995) or (Frequency >= 108 and Frequency <= 173.995) or (Frequency >= 225 and Frequency <= 399.975) then
|
||||
|
||||
-- Convert frequency from MHz to Hz
|
||||
self.Frequency = Frequency * 1000000
|
||||
|
||||
-- If the RADIO is attached to a UNIT or a GROUP, we need to send the DCS Command "SetFrequency" to change the UNIT or GROUP frequency
|
||||
if self.Positionable.ClassName == "UNIT" or self.Positionable.ClassName == "GROUP" then
|
||||
self.Positionable:SetCommand({
|
||||
|
||||
local commandSetFrequency={
|
||||
id = "SetFrequency",
|
||||
params = {
|
||||
frequency = self.Frequency,
|
||||
frequency = self.Frequency,
|
||||
modulation = self.Modulation,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
self:T2(commandSetFrequency)
|
||||
self.Positionable:SetCommand(commandSetFrequency)
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
end
|
||||
self:E({"Frequency is outside of DCS Frequency ranges (30-80, 108-152, 225-400). Frequency unchanged.", self.Frequency})
|
||||
|
||||
self:E({"Frequency is outside of DCS Frequency ranges (30-80, 108-152, 225-400). Frequency unchanged.", Frequency})
|
||||
return self
|
||||
end
|
||||
|
||||
--- Check validity of the frequency passed and sets RADIO.Modulation
|
||||
--- Set AM or FM modulation of the radio transmitter.
|
||||
-- @param #RADIO self
|
||||
-- @param #number Modulation either radio.modulation.AM or radio.modulation.FM
|
||||
-- @param #number Modulation Modulation is either radio.modulation.AM or radio.modulation.FM.
|
||||
-- @return #RADIO self
|
||||
function RADIO:SetModulation(Modulation)
|
||||
self:F2(Modulation)
|
||||
@@ -178,23 +214,24 @@ end
|
||||
|
||||
--- Check validity of the power passed and sets RADIO.Power
|
||||
-- @param #RADIO self
|
||||
-- @param #number Power in W
|
||||
-- @param #number Power Power in W.
|
||||
-- @return #RADIO self
|
||||
function RADIO:SetPower(Power)
|
||||
self:F2(Power)
|
||||
|
||||
if type(Power) == "number" then
|
||||
self.Power = math.floor(math.abs(Power)) --TODO Find what is the maximum power allowed by DCS and limit power to that
|
||||
return self
|
||||
else
|
||||
self:E({"Power is invalid. Power unchanged.", self.Power})
|
||||
end
|
||||
self:E({"Power is invalid. Power unchanged.", self.Power})
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Check validity of the loop passed and sets RADIO.Loop
|
||||
--- Set message looping on or off.
|
||||
-- @param #RADIO self
|
||||
-- @param #boolean Loop
|
||||
-- @param #boolean Loop If true, message is repeated indefinitely.
|
||||
-- @return #RADIO self
|
||||
-- @usage
|
||||
function RADIO:SetLoop(Loop)
|
||||
self:F2(Loop)
|
||||
if type(Loop) == "boolean" then
|
||||
@@ -227,13 +264,12 @@ function RADIO:SetSubtitle(Subtitle, SubtitleDuration)
|
||||
self:E({"Subtitle is invalid. Subtitle reset.", self.Subtitle})
|
||||
end
|
||||
if type(SubtitleDuration) == "number" then
|
||||
if math.floor(math.abs(SubtitleDuration)) == SubtitleDuration then
|
||||
self.SubtitleDuration = SubtitleDuration
|
||||
return self
|
||||
end
|
||||
self.SubtitleDuration = SubtitleDuration
|
||||
else
|
||||
self.SubtitleDuration = 0
|
||||
self:E({"SubtitleDuration is invalid. SubtitleDuration reset.", self.SubtitleDuration})
|
||||
end
|
||||
self.SubtitleDuration = 0
|
||||
self:E({"SubtitleDuration is invalid. SubtitleDuration reset.", self.SubtitleDuration})
|
||||
return self
|
||||
end
|
||||
|
||||
--- Create a new transmission, that is to say, populate the RADIO with relevant data
|
||||
@@ -241,10 +277,10 @@ end
|
||||
-- but it will work with a UNIT or a GROUP anyway.
|
||||
-- Only the #RADIO and the Filename are mandatory
|
||||
-- @param #RADIO self
|
||||
-- @param #string FileName
|
||||
-- @param #number Frequency in MHz
|
||||
-- @param #number Modulation either radio.modulation.AM or radio.modulation.FM
|
||||
-- @param #number Power in W
|
||||
-- @param #string FileName Name of the sound file that will be transmitted.
|
||||
-- @param #number Frequency Frequency in MHz.
|
||||
-- @param #number Modulation Modulation of frequency, which is either radio.modulation.AM or radio.modulation.FM.
|
||||
-- @param #number Power Power in W.
|
||||
-- @return #RADIO self
|
||||
function RADIO:NewGenericTransmission(FileName, Frequency, Modulation, Power, Loop)
|
||||
self:F({FileName, Frequency, Modulation, Power})
|
||||
@@ -264,31 +300,43 @@ end
|
||||
-- but it will work for any @{Wrapper.Positionable#POSITIONABLE}.
|
||||
-- Only the RADIO and the Filename are mandatory.
|
||||
-- @param #RADIO self
|
||||
-- @param #string FileName
|
||||
-- @param #string Subtitle
|
||||
-- @param #number SubtitleDuration in s
|
||||
-- @param #number Frequency in MHz
|
||||
-- @param #number Modulation either radio.modulation.AM or radio.modulation.FM
|
||||
-- @param #boolean Loop
|
||||
-- @param #string FileName Name of sound file.
|
||||
-- @param #string Subtitle Subtitle to be displayed with sound file.
|
||||
-- @param #number SubtitleDuration Duration of subtitle display in seconds.
|
||||
-- @param #number Frequency Frequency in MHz.
|
||||
-- @param #number Modulation Modulation which can be either radio.modulation.AM or radio.modulation.FM
|
||||
-- @param #boolean Loop If true, loop message.
|
||||
-- @return #RADIO self
|
||||
function RADIO:NewUnitTransmission(FileName, Subtitle, SubtitleDuration, Frequency, Modulation, Loop)
|
||||
self:F({FileName, Subtitle, SubtitleDuration, Frequency, Modulation, Loop})
|
||||
|
||||
-- Set file name.
|
||||
self:SetFileName(FileName)
|
||||
local Duration = 5
|
||||
if SubtitleDuration then Duration = SubtitleDuration end
|
||||
-- SubtitleDuration argument was missing, adding it
|
||||
if Subtitle then self:SetSubtitle(Subtitle, Duration) end
|
||||
-- self:SetSubtitleDuration is non existent, removing faulty line
|
||||
-- if SubtitleDuration then self:SetSubtitleDuration(SubtitleDuration) end
|
||||
if Frequency then self:SetFrequency(Frequency) end
|
||||
if Modulation then self:SetModulation(Modulation) end
|
||||
if Loop then self:SetLoop(Loop) end
|
||||
|
||||
-- Set modulation AM/FM.
|
||||
if Modulation then
|
||||
self:SetModulation(Modulation)
|
||||
end
|
||||
|
||||
-- Set frequency.
|
||||
if Frequency then
|
||||
self:SetFrequency(Frequency)
|
||||
end
|
||||
|
||||
-- Set subtitle.
|
||||
if Subtitle then
|
||||
self:SetSubtitle(Subtitle, SubtitleDuration or 0)
|
||||
end
|
||||
|
||||
-- Set Looping.
|
||||
if Loop then
|
||||
self:SetLoop(Loop)
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Actually Broadcast the transmission
|
||||
--- Broadcast the transmission.
|
||||
-- * The Radio has to be populated with the new transmission before broadcasting.
|
||||
-- * Please use RADIO setters or either @{#RADIO.NewGenericTransmission} or @{#RADIO.NewUnitTransmission}
|
||||
-- * This class is in fact pretty smart, it determines the right DCS function to use depending on the type of POSITIONABLE
|
||||
@@ -297,31 +345,38 @@ end
|
||||
-- * If your POSITIONABLE is a UNIT or a GROUP, the Power is ignored.
|
||||
-- * If your POSITIONABLE is not a UNIT or a GROUP, the Subtitle, SubtitleDuration are ignored
|
||||
-- @param #RADIO self
|
||||
-- @param #boolean viatrigger Use trigger.action.radioTransmission() in any case, i.e. also for UNITS and GROUPS.
|
||||
-- @return #RADIO self
|
||||
function RADIO:Broadcast()
|
||||
self:F()
|
||||
function RADIO:Broadcast(viatrigger)
|
||||
self:F({viatrigger=viatrigger})
|
||||
|
||||
-- If the POSITIONABLE is actually a UNIT or a GROUP, use the more complicated DCS command system
|
||||
if self.Positionable.ClassName == "UNIT" or self.Positionable.ClassName == "GROUP" then
|
||||
self:T2("Broadcasting from a UNIT or a GROUP")
|
||||
self.Positionable:SetCommand({
|
||||
-- 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") and (not viatrigger) then
|
||||
self:T("Broadcasting from a UNIT or a GROUP")
|
||||
|
||||
local commandTransmitMessage={
|
||||
id = "TransmitMessage",
|
||||
params = {
|
||||
file = self.FileName,
|
||||
duration = self.SubtitleDuration,
|
||||
subtitle = self.Subtitle,
|
||||
loop = self.Loop,
|
||||
}
|
||||
})
|
||||
}}
|
||||
|
||||
self:T3(commandTransmitMessage)
|
||||
self.Positionable:SetCommand(commandTransmitMessage)
|
||||
else
|
||||
-- If the POSITIONABLE is anything else, we revert to the general singleton function
|
||||
-- I need to give it a unique name, so that the transmission can be stopped later. I use the class ID
|
||||
self:T2("Broadcasting from a POSITIONABLE")
|
||||
self:T("Broadcasting from a POSITIONABLE")
|
||||
trigger.action.radioTransmission(self.FileName, self.Positionable:GetPositionVec3(), self.Modulation, self.Loop, self.Frequency, self.Power, tostring(self.ID))
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Stops a transmission
|
||||
-- This function is especially usefull to stop the broadcast of looped transmissions
|
||||
-- @param #RADIO self
|
||||
@@ -330,10 +385,10 @@ function RADIO:StopBroadcast()
|
||||
self:F()
|
||||
-- If the POSITIONABLE is a UNIT or a GROUP, stop the transmission with the DCS "StopTransmission" command
|
||||
if self.Positionable.ClassName == "UNIT" or self.Positionable.ClassName == "GROUP" then
|
||||
self.Positionable:SetCommand({
|
||||
id = "StopTransmission",
|
||||
params = {}
|
||||
})
|
||||
|
||||
local commandStopTransmission={id="StopTransmission", params={}}
|
||||
|
||||
self.Positionable:SetCommand(commandStopTransmission)
|
||||
else
|
||||
-- Else, we use the appropriate singleton funciton
|
||||
trigger.action.stopRadioTransmission(tostring(self.ID))
|
||||
@@ -359,24 +414,91 @@ end
|
||||
-- Use @{#BEACON:StopRadioBeacon}() to stop it.
|
||||
--
|
||||
-- @type BEACON
|
||||
-- @field #string ClassName Name of the class "BEACON".
|
||||
-- @field Wrapper.Controllable#CONTROLLABLE Positionable The @{#CONTROLLABLE} that will receive radio capabilities.
|
||||
-- @extends Core.Base#BASE
|
||||
BEACON = {
|
||||
ClassName = "BEACON",
|
||||
Positionable = nil,
|
||||
name=nil,
|
||||
}
|
||||
|
||||
--- Create a new BEACON Object. This doesn't activate the beacon, though, use @{#BEACON.AATACAN} or @{#BEACON.Generic}
|
||||
--- Beacon types supported by DCS.
|
||||
-- @type BEACON.Type
|
||||
-- @field #number NULL
|
||||
-- @field #number VOR
|
||||
-- @field #number DME
|
||||
-- @field #number VOR_DME
|
||||
-- @field #number TACAN
|
||||
-- @field #number VORTAC
|
||||
-- @field #number RSBN
|
||||
-- @field #number BROADCAST_STATION
|
||||
-- @field #number HOMER
|
||||
-- @field #number AIRPORT_HOMER
|
||||
-- @field #number AIRPORT_HOMER_WITH_MARKER
|
||||
-- @field #number ILS_FAR_HOMER
|
||||
-- @field #number ILS_NEAR_HOMER
|
||||
-- @field #number ILS_LOCALIZER
|
||||
-- @field #number ILS_GLIDESLOPE
|
||||
-- @field #number NAUTICAL_HOMER
|
||||
-- @field #number ICLS
|
||||
BEACON.Type={
|
||||
NULL = 0,
|
||||
VOR = 1,
|
||||
DME = 2,
|
||||
VOR_DME = 3,
|
||||
TACAN = 4,
|
||||
VORTAC = 5,
|
||||
RSBN = 32,
|
||||
BROADCAST_STATION = 1024,
|
||||
HOMER = 8,
|
||||
AIRPORT_HOMER = 4104,
|
||||
AIRPORT_HOMER_WITH_MARKER = 4136,
|
||||
ILS_FAR_HOMER = 16408,
|
||||
ILS_NEAR_HOMER = 16456,
|
||||
ILS_LOCALIZER = 16640,
|
||||
ILS_GLIDESLOPE = 16896,
|
||||
NAUTICAL_HOMER = 32776,
|
||||
ICLS = 131584,
|
||||
}
|
||||
|
||||
--- Beacon systems supported by DCS. https://wiki.hoggitworld.com/view/DCS_command_activateBeacon
|
||||
-- @type BEACON.System
|
||||
-- @field #number PAR_10
|
||||
-- @field #number RSBN_5
|
||||
-- @field #number TACAN
|
||||
-- @field #number TACAN_TANKER
|
||||
-- @field #number ILS_LOCALIZER (This is the one to be used for AA TACAN Tanker!)
|
||||
-- @field #number ILS_GLIDESLOPE
|
||||
-- @field #number BROADCAST_STATION
|
||||
BEACON.System={
|
||||
PAR_10 = 1,
|
||||
RSBN_5 = 2,
|
||||
TACAN = 3,
|
||||
TACAN_TANKER = 4,
|
||||
ILS_LOCALIZER = 5,
|
||||
ILS_GLIDESLOPE = 6,
|
||||
BROADCAST_STATION = 7,
|
||||
}
|
||||
|
||||
--- Create a new BEACON Object. This doesn't activate the beacon, though, use @{#BEACON.ActivateTACAN} etc.
|
||||
-- If you want to create a BEACON, you probably should use @{Wrapper.Positionable#POSITIONABLE.GetBeacon}() instead.
|
||||
-- @param #BEACON self
|
||||
-- @param Wrapper.Positionable#POSITIONABLE Positionable The @{Positionable} that will receive radio capabilities.
|
||||
-- @return #BEACON Beacon
|
||||
-- @return #nil If Positionable is invalid
|
||||
-- @return #BEACON Beacon object or #nil if the positionable is invalid.
|
||||
function BEACON:New(Positionable)
|
||||
local self = BASE:Inherit(self, BASE:New())
|
||||
|
||||
-- Inherit BASE.
|
||||
local self=BASE:Inherit(self, BASE:New()) --#BEACON
|
||||
|
||||
-- Debug.
|
||||
self:F(Positionable)
|
||||
|
||||
-- Set positionable.
|
||||
if Positionable:GetPointVec2() then -- It's stupid, but the only way I found to make sure positionable is valid
|
||||
self.Positionable = Positionable
|
||||
self.name=Positionable:GetName()
|
||||
self:I(string.format("New BEACON %s", tostring(self.name)))
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -385,44 +507,95 @@ function BEACON:New(Positionable)
|
||||
end
|
||||
|
||||
|
||||
--- Converts a TACAN Channel/Mode couple into a frequency in Hz
|
||||
--- Activates a TACAN BEACON.
|
||||
-- @param #BEACON self
|
||||
-- @param #number TACANChannel
|
||||
-- @param #string TACANMode
|
||||
-- @return #number Frequecy
|
||||
-- @return #nil if parameters are invalid
|
||||
function BEACON:_TACANToFrequency(TACANChannel, TACANMode)
|
||||
self:F3({TACANChannel, TACANMode})
|
||||
|
||||
if type(TACANChannel) ~= "number" then
|
||||
if TACANMode ~= "X" and TACANMode ~= "Y" then
|
||||
return nil -- error in arguments
|
||||
end
|
||||
-- @param #number Channel TACAN channel, i.e. the "10" part in "10Y".
|
||||
-- @param #string Mode TACAN mode, i.e. the "Y" part in "10Y".
|
||||
-- @param #string Message The Message that is going to be coded in Morse and broadcasted by the beacon.
|
||||
-- @param #boolean Bearing If true, beacon provides bearing information. If false (or nil), only distance information is available.
|
||||
-- @param #number Duration How long will the beacon last in seconds. Omit for forever.
|
||||
-- @return #BEACON self
|
||||
-- @usage
|
||||
-- -- Let's create a TACAN Beacon for a tanker
|
||||
-- local myUnit = UNIT:FindByName("MyUnit")
|
||||
-- local myBeacon = myUnit:GetBeacon() -- Creates the beacon
|
||||
--
|
||||
-- myBeacon:ActivateTACAN(20, "Y", "TEXACO", true) -- Activate the beacon
|
||||
function BEACON:ActivateTACAN(Channel, Mode, Message, Bearing, Duration)
|
||||
self:T({channel=Channel, mode=Mode, callsign=Message, bearing=Bearing, duration=Duration})
|
||||
|
||||
-- Get frequency.
|
||||
local Frequency=UTILS.TACANToFrequency(Channel, Mode)
|
||||
|
||||
-- Check.
|
||||
if not Frequency then
|
||||
self:E({"The passed TACAN channel is invalid, the BEACON is not emitting"})
|
||||
return self
|
||||
end
|
||||
|
||||
-- This code is largely based on ED's code, in DCS World\Scripts\World\Radio\BeaconTypes.lua, line 137.
|
||||
-- I have no idea what it does but it seems to work
|
||||
local A = 1151 -- 'X', channel >= 64
|
||||
local B = 64 -- channel >= 64
|
||||
-- Beacon type.
|
||||
local Type=BEACON.Type.TACAN
|
||||
|
||||
if TACANChannel < 64 then
|
||||
B = 1
|
||||
end
|
||||
-- Beacon system.
|
||||
local System=BEACON.System.TACAN
|
||||
|
||||
if TACANMode == 'Y' then
|
||||
A = 1025
|
||||
if TACANChannel < 64 then
|
||||
A = 1088
|
||||
end
|
||||
else -- 'X'
|
||||
if TACANChannel < 64 then
|
||||
A = 962
|
||||
-- Check if unit is an aircraft and set system accordingly.
|
||||
local AA=self.Positionable:IsAir()
|
||||
if AA then
|
||||
System=5 --NOTE: 5 is how you cat the correct tanker behaviour! --BEACON.System.TACAN_TANKER
|
||||
-- Check if "Y" mode is selected for aircraft.
|
||||
if Mode~="Y" then
|
||||
self:E({"WARNING: The POSITIONABLE you want to attach the AA Tacan Beacon is an aircraft: Mode should Y !The BEACON is not emitting.", self.Positionable})
|
||||
end
|
||||
end
|
||||
|
||||
return (A + TACANChannel - B) * 1000000
|
||||
-- Attached unit.
|
||||
local UnitID=self.Positionable:GetID()
|
||||
|
||||
-- Debug.
|
||||
self:I({string.format("BEACON Activating TACAN %s: Channel=%d%s, Morse=%s, Bearing=%s, Duration=%s!", tostring(self.name), Channel, Mode, Message, tostring(Bearing), tostring(Duration))})
|
||||
|
||||
-- Start beacon.
|
||||
self.Positionable:CommandActivateBeacon(Type, System, Frequency, UnitID, Channel, Mode, AA, Message, Bearing)
|
||||
|
||||
-- Stop sheduler.
|
||||
if Duration then
|
||||
self.Positionable:DeactivateBeacon(Duration)
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Activates an ICLS BEACON. The unit the BEACON is attached to should be an aircraft carrier supporting this system.
|
||||
-- @param #BEACON self
|
||||
-- @param #number Channel ICLS channel.
|
||||
-- @param #string Callsign The Message that is going to be coded in Morse and broadcasted by the beacon.
|
||||
-- @param #number Duration How long will the beacon last in seconds. Omit for forever.
|
||||
-- @return #BEACON self
|
||||
function BEACON:ActivateICLS(Channel, Callsign, Duration)
|
||||
self:F({Channel=Channel, Callsign=Callsign, Duration=Duration})
|
||||
|
||||
-- Attached unit.
|
||||
local UnitID=self.Positionable:GetID()
|
||||
|
||||
-- Debug
|
||||
self:T2({"ICLS BEACON started!"})
|
||||
|
||||
-- Start beacon.
|
||||
self.Positionable:CommandActivateICLS(Channel, UnitID, Callsign)
|
||||
|
||||
-- Stop sheduler
|
||||
if Duration then -- Schedule the stop of the BEACON if asked by the MD
|
||||
self.Positionable:DeactivateBeacon(Duration)
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
--- Activates a TACAN BEACON on an Aircraft.
|
||||
-- @param #BEACON self
|
||||
@@ -475,7 +648,7 @@ function BEACON:AATACAN(TACANChannel, Message, Bearing, BeaconDuration)
|
||||
})
|
||||
|
||||
if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD
|
||||
SCHEDULER:New( nil,
|
||||
SCHEDULER:New(nil,
|
||||
function()
|
||||
self:StopAATACAN()
|
||||
end, {}, BeaconDuration)
|
||||
@@ -586,4 +759,45 @@ function BEACON:StopRadioBeacon()
|
||||
self:F()
|
||||
-- The unique name of the transmission is the class ID
|
||||
trigger.action.stopRadioTransmission(tostring(self.ID))
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Converts a TACAN Channel/Mode couple into a frequency in Hz
|
||||
-- @param #BEACON self
|
||||
-- @param #number TACANChannel
|
||||
-- @param #string TACANMode
|
||||
-- @return #number Frequecy
|
||||
-- @return #nil if parameters are invalid
|
||||
function BEACON:_TACANToFrequency(TACANChannel, TACANMode)
|
||||
self:F3({TACANChannel, TACANMode})
|
||||
|
||||
if type(TACANChannel) ~= "number" then
|
||||
if TACANMode ~= "X" and TACANMode ~= "Y" then
|
||||
return nil -- error in arguments
|
||||
end
|
||||
end
|
||||
|
||||
-- This code is largely based on ED's code, in DCS World\Scripts\World\Radio\BeaconTypes.lua, line 137.
|
||||
-- I have no idea what it does but it seems to work
|
||||
local A = 1151 -- 'X', channel >= 64
|
||||
local B = 64 -- channel >= 64
|
||||
|
||||
if TACANChannel < 64 then
|
||||
B = 1
|
||||
end
|
||||
|
||||
if TACANMode == 'Y' then
|
||||
A = 1025
|
||||
if TACANChannel < 64 then
|
||||
A = 1088
|
||||
end
|
||||
else -- 'X'
|
||||
if TACANChannel < 64 then
|
||||
A = 962
|
||||
end
|
||||
end
|
||||
|
||||
return (A + TACANChannel - B) * 1000000
|
||||
end
|
||||
|
||||
|
||||
|
||||
577
Moose Development/Moose/Core/RadioQueue.lua
Normal file
577
Moose Development/Moose/Core/RadioQueue.lua
Normal file
@@ -0,0 +1,577 @@
|
||||
--- **Core** - Queues Radio Transmissions.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
-- * Managed Radio Transmissions.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Authors: funkyfranky
|
||||
--
|
||||
-- @module Core.RadioQueue
|
||||
-- @image Core_Radio.JPG
|
||||
|
||||
--- Manages radio transmissions.
|
||||
--
|
||||
-- @type RADIOQUEUE
|
||||
-- @field #string ClassName Name of the class "RADIOQUEUE".
|
||||
-- @field #boolean Debug Debug mode. More info.
|
||||
-- @field #string lid ID for dcs.log.
|
||||
-- @field #number frequency The radio frequency in Hz.
|
||||
-- @field #number modulation The radio modulation. Either radio.modulation.AM or radio.modulation.FM.
|
||||
-- @field Core.Scheduler#SCHEDULER scheduler The scheduler.
|
||||
-- @field #string RQid The radio queue scheduler ID.
|
||||
-- @field #table queue The queue of transmissions.
|
||||
-- @field #string alias Name of the radio.
|
||||
-- @field #number dt Time interval in seconds for checking the radio queue.
|
||||
-- @field #number delay Time delay before starting the radio queue.
|
||||
-- @field #number Tlast Time (abs) when the last transmission finished.
|
||||
-- @field Core.Point#COORDINATE sendercoord Coordinate from where transmissions are broadcasted.
|
||||
-- @field #number sendername Name of the sending unit or static.
|
||||
-- @field #boolean senderinit Set frequency was initialized.
|
||||
-- @field #number power Power of radio station in Watts. Default 100 W.
|
||||
-- @field #table numbers Table of number transmission parameters.
|
||||
-- @field #boolean checking Scheduler is checking the radio queue.
|
||||
-- @field #boolean schedonce Call ScheduleOnce instead of normal scheduler.
|
||||
-- @extends Core.Base#BASE
|
||||
RADIOQUEUE = {
|
||||
ClassName = "RADIOQUEUE",
|
||||
Debug = nil,
|
||||
lid = nil,
|
||||
frequency = nil,
|
||||
modulation = nil,
|
||||
scheduler = nil,
|
||||
RQid = nil,
|
||||
queue = {},
|
||||
alias = nil,
|
||||
dt = nil,
|
||||
delay = nil,
|
||||
Tlast = nil,
|
||||
sendercoord = nil,
|
||||
sendername = nil,
|
||||
senderinit = nil,
|
||||
power = nil,
|
||||
numbers = {},
|
||||
checking = nil,
|
||||
schedonce = nil,
|
||||
}
|
||||
|
||||
--- Radio queue transmission data.
|
||||
-- @type RADIOQUEUE.Transmission
|
||||
-- @field #string filename Name of the file to be transmitted.
|
||||
-- @field #string path Path in miz file where the file is located.
|
||||
-- @field #number duration Duration in seconds.
|
||||
-- @field #string subtitle Subtitle of the transmission.
|
||||
-- @field #number subduration Duration of the subtitle being displayed.
|
||||
-- @field #number Tstarted Mission time (abs) in seconds when the transmission started.
|
||||
-- @field #boolean isplaying If true, transmission is currently playing.
|
||||
-- @field #number Tplay Mission time (abs) in seconds when the transmission should be played.
|
||||
-- @field #number interval Interval in seconds before next transmission.
|
||||
|
||||
|
||||
--- Create a new RADIOQUEUE object for a given radio frequency/modulation.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @param #number frequency The radio frequency in MHz.
|
||||
-- @param #number modulation (Optional) The radio modulation. Default radio.modulation.AM.
|
||||
-- @param #string alias (Optional) Name of the radio queue.
|
||||
-- @return #RADIOQUEUE self The RADIOQUEUE object.
|
||||
function RADIOQUEUE:New(frequency, modulation, alias)
|
||||
|
||||
-- Inherit base
|
||||
local self=BASE:Inherit(self, BASE:New()) -- #RADIOQUEUE
|
||||
|
||||
self.alias=alias or "My Radio"
|
||||
|
||||
self.lid=string.format("RADIOQUEUE %s | ", self.alias)
|
||||
|
||||
if frequency==nil then
|
||||
self:E(self.lid.."ERROR: No frequency specified as first parameter!")
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Frequency in Hz.
|
||||
self.frequency=frequency*1000000
|
||||
|
||||
-- Modulation.
|
||||
self.modulation=modulation or radio.modulation.AM
|
||||
|
||||
-- Set radio power.
|
||||
self:SetRadioPower()
|
||||
|
||||
-- Scheduler.
|
||||
self.scheduler=SCHEDULER:New()
|
||||
self.scheduler:NoTrace()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Start the radio queue.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @param #number delay (Optional) Delay in seconds, before the radio queue is started. Default 1 sec.
|
||||
-- @param #number dt (Optional) Time step in seconds for checking the queue. Default 0.01 sec.
|
||||
-- @return #RADIOQUEUE self The RADIOQUEUE object.
|
||||
function RADIOQUEUE:Start(delay, dt)
|
||||
|
||||
-- Delay before start.
|
||||
self.delay=delay or 1
|
||||
|
||||
-- Time interval for queue check.
|
||||
self.dt=dt or 0.01
|
||||
|
||||
-- Debug message.
|
||||
self:I(self.lid..string.format("Starting RADIOQUEUE %s on Frequency %.2f MHz [modulation=%d] in %.1f seconds (dt=%.3f sec)", self.alias, self.frequency/1000000, self.modulation, self.delay, self.dt))
|
||||
|
||||
-- Start Scheduler.
|
||||
if self.schedonce then
|
||||
self:_CheckRadioQueueDelayed(delay)
|
||||
else
|
||||
self.RQid=self.scheduler:Schedule(nil, RADIOQUEUE._CheckRadioQueue, {self}, delay, dt)
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Stop the radio queue. Stop scheduler and delete queue.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @return #RADIOQUEUE self The RADIOQUEUE object.
|
||||
function RADIOQUEUE:Stop()
|
||||
self:I(self.lid.."Stopping RADIOQUEUE.")
|
||||
self.scheduler:Stop(self.RQid)
|
||||
self.queue={}
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set coordinate from where the transmission is broadcasted.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @param Core.Point#COORDINATE coordinate Coordinate of the sender.
|
||||
-- @return #RADIOQUEUE self The RADIOQUEUE object.
|
||||
function RADIOQUEUE:SetSenderCoordinate(coordinate)
|
||||
self.sendercoord=coordinate
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set name of unit or static from which transmissions are made.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @param #string name Name of the unit or static used for transmissions.
|
||||
-- @return #RADIOQUEUE self The RADIOQUEUE object.
|
||||
function RADIOQUEUE:SetSenderUnitName(name)
|
||||
self.sendername=name
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set radio power. Note that this only applies if no relay unit is used.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @param #number power Radio power in Watts. Default 100 W.
|
||||
-- @return #RADIOQUEUE self The RADIOQUEUE object.
|
||||
function RADIOQUEUE:SetRadioPower(power)
|
||||
self.power=power or 100
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set parameters of a digit.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @param #number digit The digit 0-9.
|
||||
-- @param #string filename The name of the sound file.
|
||||
-- @param #number duration The duration of the sound file in seconds.
|
||||
-- @param #string path The directory within the miz file where the sound is located. Default "l10n/DEFAULT/".
|
||||
-- @param #string subtitle Subtitle of the transmission.
|
||||
-- @param #number subduration Duration [sec] of the subtitle being displayed. Default 5 sec.
|
||||
-- @return #RADIOQUEUE self The RADIOQUEUE object.
|
||||
function RADIOQUEUE:SetDigit(digit, filename, duration, path, subtitle, subduration)
|
||||
|
||||
local transmission={} --#RADIOQUEUE.Transmission
|
||||
transmission.filename=filename
|
||||
transmission.duration=duration
|
||||
transmission.path=path or "l10n/DEFAULT/"
|
||||
transmission.subtitle=nil
|
||||
transmission.subduration=nil
|
||||
|
||||
-- Convert digit to string in case it is given as a number.
|
||||
if type(digit)=="number" then
|
||||
digit=tostring(digit)
|
||||
end
|
||||
|
||||
-- Set transmission.
|
||||
self.numbers[digit]=transmission
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add a transmission to the radio queue.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @param #RADIOQUEUE.Transmission transmission The transmission data table.
|
||||
-- @return #RADIOQUEUE self The RADIOQUEUE object.
|
||||
function RADIOQUEUE:AddTransmission(transmission)
|
||||
self:F({transmission=transmission})
|
||||
|
||||
-- Init.
|
||||
transmission.isplaying=false
|
||||
transmission.Tstarted=nil
|
||||
|
||||
-- Add to queue.
|
||||
table.insert(self.queue, transmission)
|
||||
|
||||
-- Start checking.
|
||||
if self.schedonce and not self.checking then
|
||||
self:_CheckRadioQueueDelayed()
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add a transmission to the radio queue.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @param #string filename Name of the sound file. Usually an ogg or wav file type.
|
||||
-- @param #number duration Duration in seconds the file lasts.
|
||||
-- @param #number path Directory path inside the miz file where the sound file is located. Default "l10n/DEFAULT/".
|
||||
-- @param #number tstart Start time (abs) seconds. Default now.
|
||||
-- @param #number interval Interval in seconds after the last transmission finished.
|
||||
-- @param #string subtitle Subtitle of the transmission.
|
||||
-- @param #number subduration Duration [sec] of the subtitle being displayed. Default 5 sec.
|
||||
-- @return #RADIOQUEUE self The RADIOQUEUE object.
|
||||
function RADIOQUEUE:NewTransmission(filename, duration, path, tstart, interval, subtitle, subduration)
|
||||
|
||||
-- Sanity checks.
|
||||
if not filename then
|
||||
self:E(self.lid.."ERROR: No filename specified.")
|
||||
return nil
|
||||
end
|
||||
if type(filename)~="string" then
|
||||
self:E(self.lid.."ERROR: Filename specified is NOT a string.")
|
||||
return nil
|
||||
end
|
||||
|
||||
if not duration then
|
||||
self:E(self.lid.."ERROR: No duration of transmission specified.")
|
||||
return nil
|
||||
end
|
||||
if type(duration)~="number" then
|
||||
self:E(self.lid.."ERROR: Duration specified is NOT a number.")
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
local transmission={} --#RADIOQUEUE.Transmission
|
||||
transmission.filename=filename
|
||||
transmission.duration=duration
|
||||
transmission.path=path or "l10n/DEFAULT/"
|
||||
transmission.Tplay=tstart or timer.getAbsTime()
|
||||
transmission.subtitle=subtitle
|
||||
transmission.interval=interval or 0
|
||||
if transmission.subtitle then
|
||||
transmission.subduration=subduration or 5
|
||||
else
|
||||
transmission.subduration=nil
|
||||
end
|
||||
|
||||
-- Add transmission to queue.
|
||||
self:AddTransmission(transmission)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Convert a number (as string) into a radio transmission.
|
||||
-- E.g. for board number or headings.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @param #string number Number string, e.g. "032" or "183".
|
||||
-- @param #number delay Delay before transmission in seconds.
|
||||
-- @param #number interval Interval between the next call.
|
||||
-- @return #number Duration of the call in seconds.
|
||||
function RADIOQUEUE:Number2Transmission(number, delay, interval)
|
||||
|
||||
--- Split string into characters.
|
||||
local function _split(str)
|
||||
local chars={}
|
||||
for i=1,#str do
|
||||
local c=str:sub(i,i)
|
||||
table.insert(chars, c)
|
||||
end
|
||||
return chars
|
||||
end
|
||||
|
||||
-- Split string into characters.
|
||||
local numbers=_split(number)
|
||||
|
||||
local wait=0
|
||||
for i=1,#numbers do
|
||||
|
||||
-- Current number
|
||||
local n=numbers[i]
|
||||
|
||||
-- Radio call.
|
||||
local transmission=UTILS.DeepCopy(self.numbers[n]) --#RADIOQUEUE.Transmission
|
||||
|
||||
transmission.Tplay=timer.getAbsTime()+(delay or 0)
|
||||
|
||||
if interval and i==1 then
|
||||
transmission.interval=interval
|
||||
end
|
||||
|
||||
self:AddTransmission(transmission)
|
||||
|
||||
-- Add up duration of the number.
|
||||
wait=wait+transmission.duration
|
||||
end
|
||||
|
||||
-- Return the total duration of the call.
|
||||
return wait
|
||||
end
|
||||
|
||||
|
||||
--- Broadcast radio message.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @param #RADIOQUEUE.Transmission transmission The transmission.
|
||||
function RADIOQUEUE:Broadcast(transmission)
|
||||
|
||||
-- Get unit sending the transmission.
|
||||
local sender=self:_GetRadioSender()
|
||||
|
||||
-- Construct file name.
|
||||
local filename=string.format("%s%s", transmission.path, transmission.filename)
|
||||
|
||||
if sender then
|
||||
|
||||
-- Broadcasting from aircraft. Only players tuned in to the right frequency will see the message.
|
||||
self:T(self.lid..string.format("Broadcasting from aircraft %s", sender:GetName()))
|
||||
|
||||
|
||||
if not self.senderinit then
|
||||
|
||||
-- Command to set the Frequency for the transmission.
|
||||
local commandFrequency={
|
||||
id="SetFrequency",
|
||||
params={
|
||||
frequency=self.frequency, -- Frequency in Hz.
|
||||
modulation=self.modulation,
|
||||
}}
|
||||
|
||||
-- Set commend for frequency
|
||||
sender:SetCommand(commandFrequency)
|
||||
|
||||
self.senderinit=true
|
||||
end
|
||||
|
||||
-- Set subtitle only if duration>0 sec.
|
||||
local subtitle=nil
|
||||
local duration=nil
|
||||
if transmission.subtitle and transmission.subduration and transmission.subduration>0 then
|
||||
subtitle=transmission.subtitle
|
||||
duration=transmission.subduration
|
||||
end
|
||||
|
||||
-- Command to tranmit the call.
|
||||
local commandTransmit={
|
||||
id = "TransmitMessage",
|
||||
params = {
|
||||
file=filename,
|
||||
duration=duration,
|
||||
subtitle=subtitle,
|
||||
loop=false,
|
||||
}}
|
||||
|
||||
-- Set command for radio transmission.
|
||||
sender:SetCommand(commandTransmit)
|
||||
|
||||
-- Debug message.
|
||||
local text=string.format("file=%s, freq=%.2f MHz, duration=%.2f sec, subtitle=%s", filename, self.frequency/1000000, transmission.duration, transmission.subtitle or "")
|
||||
MESSAGE:New(text, 2, "RADIOQUEUE "..self.alias):ToAllIf(self.Debug)
|
||||
|
||||
else
|
||||
|
||||
-- Broadcasting from carrier. No subtitle possible. Need to send messages to players.
|
||||
self:T(self.lid..string.format("Broadcasting via trigger.action.radioTransmission()."))
|
||||
|
||||
-- Position from where to transmit.
|
||||
local vec3=nil
|
||||
|
||||
-- Try to get positon from sender unit/static.
|
||||
if self.sendername then
|
||||
local coord=self:_GetRadioSenderCoord()
|
||||
if coord then
|
||||
vec3=coord:GetVec3()
|
||||
end
|
||||
end
|
||||
|
||||
-- Try to get fixed positon.
|
||||
if self.sendercoord and not vec3 then
|
||||
vec3=self.sendercoord:GetVec3()
|
||||
end
|
||||
|
||||
-- Transmit via trigger.
|
||||
if vec3 then
|
||||
self:T("Sending")
|
||||
self:T( { filename = filename, vec3 = vec3, modulation = self.modulation, frequency = self.frequency, power = self.power } )
|
||||
|
||||
-- Trigger transmission.
|
||||
trigger.action.radioTransmission(filename, vec3, self.modulation, false, self.frequency, self.power)
|
||||
|
||||
-- Debug message.
|
||||
local text=string.format("file=%s, freq=%.2f MHz, duration=%.2f sec, subtitle=%s", filename, self.frequency/1000000, transmission.duration, transmission.subtitle or "")
|
||||
MESSAGE:New(string.format(text, filename, transmission.duration, transmission.subtitle or ""), 5, "RADIOQUEUE "..self.alias):ToAllIf(self.Debug)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
--- Start checking the radio queue.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @param #number delay Delay in seconds before checking.
|
||||
function RADIOQUEUE:_CheckRadioQueueDelayed(delay)
|
||||
self.checking=true
|
||||
self:ScheduleOnce(delay or self.dt, RADIOQUEUE._CheckRadioQueue, self)
|
||||
end
|
||||
|
||||
--- Check radio queue for transmissions to be broadcasted.
|
||||
-- @param #RADIOQUEUE self
|
||||
function RADIOQUEUE:_CheckRadioQueue()
|
||||
--env.info("FF check radio queue "..self.alias)
|
||||
|
||||
-- Check if queue is empty.
|
||||
if #self.queue==0 then
|
||||
-- Queue is now empty. Nothing to else to do.
|
||||
self.checking=false
|
||||
return
|
||||
end
|
||||
|
||||
-- Get current abs time.
|
||||
local time=timer.getAbsTime()
|
||||
|
||||
local playing=false
|
||||
local next=nil --#RADIOQUEUE.Transmission
|
||||
local remove=nil
|
||||
for i,_transmission in ipairs(self.queue) do
|
||||
local transmission=_transmission --#RADIOQUEUE.Transmission
|
||||
|
||||
-- Check if transmission time has passed.
|
||||
if time>=transmission.Tplay then
|
||||
|
||||
-- Check if transmission is currently playing.
|
||||
if transmission.isplaying then
|
||||
|
||||
-- Check if transmission is finished.
|
||||
if time>=transmission.Tstarted+transmission.duration then
|
||||
|
||||
-- Transmission over.
|
||||
transmission.isplaying=false
|
||||
|
||||
-- Remove ith element in queue.
|
||||
remove=i
|
||||
|
||||
-- Store time last transmission finished.
|
||||
self.Tlast=time
|
||||
|
||||
else -- still playing
|
||||
|
||||
-- Transmission is still playing.
|
||||
playing=true
|
||||
|
||||
end
|
||||
|
||||
else -- not playing yet
|
||||
|
||||
local Tlast=self.Tlast
|
||||
|
||||
if transmission.interval==nil then
|
||||
|
||||
-- Not playing ==> this will be next.
|
||||
if next==nil then
|
||||
next=transmission
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
if Tlast==nil or time-Tlast>=transmission.interval then
|
||||
next=transmission
|
||||
else
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
-- We got a transmission or one with an interval that is not due yet. No need for anything else.
|
||||
if next or Tlast then
|
||||
break
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
-- Transmission not due yet.
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
-- Found a new transmission.
|
||||
if next~=nil and not playing then
|
||||
self:Broadcast(next)
|
||||
next.isplaying=true
|
||||
next.Tstarted=time
|
||||
end
|
||||
|
||||
-- Remove completed calls from queue.
|
||||
if remove then
|
||||
table.remove(self.queue, remove)
|
||||
end
|
||||
|
||||
-- Check queue.
|
||||
if self.schedonce then
|
||||
self:_CheckRadioQueueDelayed()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Get unit from which we want to transmit a radio message. This has to be an aircraft for subtitles to work.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @return Wrapper.Unit#UNIT Sending aircraft unit or nil if was not setup, is not an aircraft or is not alive.
|
||||
function RADIOQUEUE:_GetRadioSender()
|
||||
|
||||
-- Check if we have a sending aircraft.
|
||||
local sender=nil --Wrapper.Unit#UNIT
|
||||
|
||||
-- Try the general default.
|
||||
if self.sendername then
|
||||
-- First try to find a unit
|
||||
sender=UNIT:FindByName(self.sendername)
|
||||
|
||||
-- Check that sender is alive and an aircraft.
|
||||
if sender and sender:IsAlive() and sender:IsAir() then
|
||||
return sender
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Get unit from which we want to transmit a radio message. This has to be an aircraft for subtitles to work.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @return Core.Point#COORDINATE Coordinate of the sender unit.
|
||||
function RADIOQUEUE:_GetRadioSenderCoord()
|
||||
|
||||
local vec3=nil
|
||||
|
||||
-- Try the general default.
|
||||
if self.sendername then
|
||||
|
||||
-- First try to find a unit
|
||||
local sender=UNIT:FindByName(self.sendername)
|
||||
|
||||
-- Check that sender is alive and an aircraft.
|
||||
if sender and sender:IsAlive() then
|
||||
return sender:GetCoordinate()
|
||||
end
|
||||
|
||||
-- Now try a static.
|
||||
local sender=STATIC:FindByName( self.sendername, false )
|
||||
|
||||
-- Check that sender is alive and an aircraft.
|
||||
if sender then
|
||||
return sender:GetCoordinate()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
405
Moose Development/Moose/Core/RadioSpeech.lua
Normal file
405
Moose Development/Moose/Core/RadioSpeech.lua
Normal file
@@ -0,0 +1,405 @@
|
||||
--- **Core** - Makes the radio talk.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
-- * Send text strings using a vocabulary that is converted in spoken language.
|
||||
-- * Possiblity to implement multiple language.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Authors: FlightControl
|
||||
--
|
||||
-- @module Core.RadioSpeech
|
||||
-- @image Core_Radio.JPG
|
||||
|
||||
--- Makes the radio speak.
|
||||
--
|
||||
-- # RADIOSPEECH usage
|
||||
--
|
||||
--
|
||||
-- @type RADIOSPEECH
|
||||
-- @extends Core.RadioQueue#RADIOQUEUE
|
||||
RADIOSPEECH = {
|
||||
ClassName = "RADIOSPEECH",
|
||||
Vocabulary = {
|
||||
EN = {},
|
||||
DE = {},
|
||||
RU = {},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
RADIOSPEECH.Vocabulary.EN = {
|
||||
["1"] = { "1", 0.25 },
|
||||
["2"] = { "2", 0.25 },
|
||||
["3"] = { "3", 0.30 },
|
||||
["4"] = { "4", 0.35 },
|
||||
["5"] = { "5", 0.35 },
|
||||
["6"] = { "6", 0.42 },
|
||||
["7"] = { "7", 0.38 },
|
||||
["8"] = { "8", 0.20 },
|
||||
["9"] = { "9", 0.32 },
|
||||
["10"] = { "10", 0.35 },
|
||||
["11"] = { "11", 0.40 },
|
||||
["12"] = { "12", 0.42 },
|
||||
["13"] = { "13", 0.38 },
|
||||
["14"] = { "14", 0.42 },
|
||||
["15"] = { "15", 0.42 },
|
||||
["16"] = { "16", 0.52 },
|
||||
["17"] = { "17", 0.59 },
|
||||
["18"] = { "18", 0.40 },
|
||||
["19"] = { "19", 0.47 },
|
||||
["20"] = { "20", 0.38 },
|
||||
["30"] = { "30", 0.29 },
|
||||
["40"] = { "40", 0.35 },
|
||||
["50"] = { "50", 0.32 },
|
||||
["60"] = { "60", 0.44 },
|
||||
["70"] = { "70", 0.48 },
|
||||
["80"] = { "80", 0.26 },
|
||||
["90"] = { "90", 0.36 },
|
||||
["100"] = { "100", 0.55 },
|
||||
["200"] = { "200", 0.55 },
|
||||
["300"] = { "300", 0.61 },
|
||||
["400"] = { "400", 0.60 },
|
||||
["500"] = { "500", 0.61 },
|
||||
["600"] = { "600", 0.65 },
|
||||
["700"] = { "700", 0.70 },
|
||||
["800"] = { "800", 0.54 },
|
||||
["900"] = { "900", 0.60 },
|
||||
["1000"] = { "1000", 0.60 },
|
||||
["2000"] = { "2000", 0.61 },
|
||||
["3000"] = { "3000", 0.64 },
|
||||
["4000"] = { "4000", 0.62 },
|
||||
["5000"] = { "5000", 0.69 },
|
||||
["6000"] = { "6000", 0.69 },
|
||||
["7000"] = { "7000", 0.75 },
|
||||
["8000"] = { "8000", 0.59 },
|
||||
["9000"] = { "9000", 0.65 },
|
||||
|
||||
["chevy"] = { "chevy", 0.35 },
|
||||
["colt"] = { "colt", 0.35 },
|
||||
["springfield"] = { "springfield", 0.65 },
|
||||
["dodge"] = { "dodge", 0.35 },
|
||||
["enfield"] = { "enfield", 0.5 },
|
||||
["ford"] = { "ford", 0.32 },
|
||||
["pontiac"] = { "pontiac", 0.55 },
|
||||
["uzi"] = { "uzi", 0.28 },
|
||||
|
||||
["degrees"] = { "degrees", 0.5 },
|
||||
["kilometers"] = { "kilometers", 0.65 },
|
||||
["km"] = { "kilometers", 0.65 },
|
||||
["miles"] = { "miles", 0.45 },
|
||||
["meters"] = { "meters", 0.41 },
|
||||
["mi"] = { "miles", 0.45 },
|
||||
["feet"] = { "feet", 0.29 },
|
||||
|
||||
["br"] = { "br", 1.1 },
|
||||
["bra"] = { "bra", 0.3 },
|
||||
|
||||
|
||||
["returning to base"] = { "returning_to_base", 0.85 },
|
||||
["on route to ground target"] = { "on_route_to_ground_target", 1.05 },
|
||||
["intercepting bogeys"] = { "intercepting_bogeys", 1.00 },
|
||||
["engaging ground target"] = { "engaging_ground_target", 1.20 },
|
||||
["engaging bogeys"] = { "engaging_bogeys", 0.81 },
|
||||
["wheels up"] = { "wheels_up", 0.42 },
|
||||
["landing at base"] = { "landing at base", 0.8 },
|
||||
["patrolling"] = { "patrolling", 0.55 },
|
||||
|
||||
["for"] = { "for", 0.31 },
|
||||
["and"] = { "and", 0.31 },
|
||||
["at"] = { "at", 0.3 },
|
||||
["dot"] = { "dot", 0.26 },
|
||||
["defender"] = { "defender", 0.45 },
|
||||
}
|
||||
|
||||
RADIOSPEECH.Vocabulary.RU = {
|
||||
["1"] = { "1", 0.34 },
|
||||
["2"] = { "2", 0.30 },
|
||||
["3"] = { "3", 0.23 },
|
||||
["4"] = { "4", 0.51 },
|
||||
["5"] = { "5", 0.31 },
|
||||
["6"] = { "6", 0.44 },
|
||||
["7"] = { "7", 0.25 },
|
||||
["8"] = { "8", 0.43 },
|
||||
["9"] = { "9", 0.45 },
|
||||
["10"] = { "10", 0.53 },
|
||||
["11"] = { "11", 0.66 },
|
||||
["12"] = { "12", 0.70 },
|
||||
["13"] = { "13", 0.66 },
|
||||
["14"] = { "14", 0.80 },
|
||||
["15"] = { "15", 0.65 },
|
||||
["16"] = { "16", 0.75 },
|
||||
["17"] = { "17", 0.74 },
|
||||
["18"] = { "18", 0.85 },
|
||||
["19"] = { "19", 0.80 },
|
||||
["20"] = { "20", 0.58 },
|
||||
["30"] = { "30", 0.51 },
|
||||
["40"] = { "40", 0.51 },
|
||||
["50"] = { "50", 0.67 },
|
||||
["60"] = { "60", 0.76 },
|
||||
["70"] = { "70", 0.68 },
|
||||
["80"] = { "80", 0.84 },
|
||||
["90"] = { "90", 0.71 },
|
||||
["100"] = { "100", 0.35 },
|
||||
["200"] = { "200", 0.59 },
|
||||
["300"] = { "300", 0.53 },
|
||||
["400"] = { "400", 0.70 },
|
||||
["500"] = { "500", 0.50 },
|
||||
["600"] = { "600", 0.58 },
|
||||
["700"] = { "700", 0.64 },
|
||||
["800"] = { "800", 0.77 },
|
||||
["900"] = { "900", 0.75 },
|
||||
["1000"] = { "1000", 0.87 },
|
||||
["2000"] = { "2000", 0.83 },
|
||||
["3000"] = { "3000", 0.84 },
|
||||
["4000"] = { "4000", 1.00 },
|
||||
["5000"] = { "5000", 0.77 },
|
||||
["6000"] = { "6000", 0.90 },
|
||||
["7000"] = { "7000", 0.77 },
|
||||
["8000"] = { "8000", 0.92 },
|
||||
["9000"] = { "9000", 0.87 },
|
||||
|
||||
["степени"] = { "degrees", 0.5 },
|
||||
["километров"] = { "kilometers", 0.65 },
|
||||
["km"] = { "kilometers", 0.65 },
|
||||
["миль"] = { "miles", 0.45 },
|
||||
["mi"] = { "miles", 0.45 },
|
||||
["метры"] = { "meters", 0.41 },
|
||||
["m"] = { "meters", 0.41 },
|
||||
["ноги"] = { "feet", 0.37 },
|
||||
|
||||
["br"] = { "br", 1.1 },
|
||||
["bra"] = { "bra", 0.3 },
|
||||
|
||||
|
||||
["возвращаясь на базу"] = { "returning_to_base", 1.40 },
|
||||
["на пути к наземной цели"] = { "on_route_to_ground_target", 1.45 },
|
||||
["перехват самолетов"] = { "intercepting_bogeys", 1.22 },
|
||||
["поражение наземной цели"] = { "engaging_ground_target", 1.53 },
|
||||
["захватывающие самолеты"] = { "engaging_bogeys", 1.68 },
|
||||
["колеса вверх"] = { "wheels_up", 0.92 },
|
||||
["посадка на базу"] = { "landing at base", 1.04 },
|
||||
["патрулирующий"] = { "patrolling", 0.96 },
|
||||
|
||||
["за"] = { "for", 0.27 },
|
||||
["и"] = { "and", 0.17 },
|
||||
["в"] = { "at", 0.19 },
|
||||
["dot"] = { "dot", 0.51 },
|
||||
["defender"] = { "defender", 0.45 },
|
||||
}
|
||||
|
||||
--- Create a new RADIOSPEECH object for a given radio frequency/modulation.
|
||||
-- @param #RADIOSPEECH self
|
||||
-- @param #number frequency The radio frequency in MHz.
|
||||
-- @param #number modulation (Optional) The radio modulation. Default radio.modulation.AM.
|
||||
-- @return #RADIOSPEECH self The RADIOSPEECH object.
|
||||
function RADIOSPEECH:New(frequency, modulation)
|
||||
|
||||
-- Inherit base
|
||||
local self = BASE:Inherit( self, RADIOQUEUE:New( frequency, modulation ) ) -- #RADIOSPEECH
|
||||
|
||||
self.Language = "EN"
|
||||
|
||||
self:BuildTree()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function RADIOSPEECH:SetLanguage( Langauge )
|
||||
|
||||
self.Language = Langauge
|
||||
end
|
||||
|
||||
|
||||
--- Add Sentence to the Speech collection.
|
||||
-- @param #RADIOSPEECH self
|
||||
-- @param #string RemainingSentence The remaining sentence during recursion.
|
||||
-- @param #table Speech The speech node.
|
||||
-- @param #string Sentence The full sentence.
|
||||
-- @param #string Data The speech data.
|
||||
-- @return #RADIOSPEECH self The RADIOSPEECH object.
|
||||
function RADIOSPEECH:AddSentenceToSpeech( RemainingSentence, Speech, Sentence, Data )
|
||||
|
||||
self:I( { RemainingSentence, Speech, Sentence, Data } )
|
||||
|
||||
local Token, RemainingSentence = RemainingSentence:match( "^ *([^ ]+)(.*)" )
|
||||
self:I( { Token = Token, RemainingSentence = RemainingSentence } )
|
||||
|
||||
-- Is there a Token?
|
||||
if Token then
|
||||
|
||||
-- We check if the Token is already in the Speech collection.
|
||||
if not Speech[Token] then
|
||||
|
||||
-- There is not yet a vocabulary registered for this.
|
||||
Speech[Token] = {}
|
||||
|
||||
if RemainingSentence and RemainingSentence ~= "" then
|
||||
-- We use recursion to iterate through the complete Sentence, and make a chain of Tokens.
|
||||
-- The last Speech node in the collection contains the Sentence and the Data to be spoken.
|
||||
-- This to ensure that during the actual speech:
|
||||
-- - Complete sentences are being understood.
|
||||
-- - Words without speech are ignored.
|
||||
-- - Incorrect sequence of words are ignored.
|
||||
Speech[Token].Next = {}
|
||||
self:AddSentenceToSpeech( RemainingSentence, Speech[Token].Next, Sentence, Data )
|
||||
else
|
||||
-- There is no remaining sentence, so we add speech to the Sentence.
|
||||
-- The recursion stops here.
|
||||
Speech[Token].Sentence = Sentence
|
||||
Speech[Token].Data = Data
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Build the tree structure based on the language words, in order to find the correct sentences and to ignore incomprehensible words.
|
||||
-- @param #RADIOSPEECH self
|
||||
-- @return #RADIOSPEECH self The RADIOSPEECH object.
|
||||
function RADIOSPEECH:BuildTree()
|
||||
|
||||
self.Speech = {}
|
||||
|
||||
for Language, Sentences in pairs( self.Vocabulary ) do
|
||||
self:I( { Language = Language, Sentences = Sentences })
|
||||
self.Speech[Language] = {}
|
||||
for Sentence, Data in pairs( Sentences ) do
|
||||
self:I( { Sentence = Sentence, Data = Data } )
|
||||
self:AddSentenceToSpeech( Sentence, self.Speech[Language], Sentence, Data )
|
||||
end
|
||||
end
|
||||
|
||||
self:I( { Speech = self.Speech } )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Speak a sentence.
|
||||
-- @param #RADIOSPEECH self
|
||||
-- @param #string Sentence The sentence to be spoken.
|
||||
function RADIOSPEECH:SpeakWords( Sentence, Speech, Language )
|
||||
|
||||
local OriginalSentence = Sentence
|
||||
|
||||
-- lua does not parse UTF-8, so the match statement will fail on cyrillic using %a.
|
||||
-- therefore, the only way to parse the statement is to use blank, comma or dot as a delimiter.
|
||||
-- and then check if the character can be converted to a number or not.
|
||||
local Word, RemainderSentence = Sentence:match( "^[., ]*([^ .,]+)(.*)" )
|
||||
|
||||
self:I( { Word = Word, Speech = Speech[Word], RemainderSentence = RemainderSentence } )
|
||||
|
||||
|
||||
if Word then
|
||||
if Word ~= "" and tonumber(Word) == nil then
|
||||
|
||||
-- Construct of words
|
||||
Word = Word:lower()
|
||||
if Speech[Word] then
|
||||
-- The end of the sentence has been reached. Now Speech.Next should be nil, otherwise there is an error.
|
||||
if Speech[Word].Next == nil then
|
||||
self:I( { Sentence = Speech[Word].Sentence, Data = Speech[Word].Data } )
|
||||
self:NewTransmission( Speech[Word].Data[1] .. ".wav", Speech[Word].Data[2], Language .. "/" )
|
||||
else
|
||||
if RemainderSentence and RemainderSentence ~= "" then
|
||||
return self:SpeakWords( RemainderSentence, Speech[Word].Next, Language )
|
||||
end
|
||||
end
|
||||
end
|
||||
return RemainderSentence
|
||||
end
|
||||
return OriginalSentence
|
||||
else
|
||||
return ""
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Speak a sentence.
|
||||
-- @param #RADIOSPEECH self
|
||||
-- @param #string Sentence The sentence to be spoken.
|
||||
function RADIOSPEECH:SpeakDigits( Sentence, Speech, Langauge )
|
||||
|
||||
local OriginalSentence = Sentence
|
||||
|
||||
-- lua does not parse UTF-8, so the match statement will fail on cyrillic using %a.
|
||||
-- therefore, the only way to parse the statement is to use blank, comma or dot as a delimiter.
|
||||
-- and then check if the character can be converted to a number or not.
|
||||
local Digits, RemainderSentence = Sentence:match( "^[., ]*([^ .,]+)(.*)" )
|
||||
|
||||
self:I( { Digits = Digits, Speech = Speech[Digits], RemainderSentence = RemainderSentence } )
|
||||
|
||||
if Digits then
|
||||
if Digits ~= "" and tonumber( Digits ) ~= nil then
|
||||
|
||||
-- Construct numbers
|
||||
local Number = tonumber( Digits )
|
||||
local Multiple = nil
|
||||
while Number >= 0 do
|
||||
if Number > 1000 then
|
||||
Multiple = math.floor( Number / 1000 ) * 1000
|
||||
elseif Number > 100 then
|
||||
Multiple = math.floor( Number / 100 ) * 100
|
||||
elseif Number > 20 then
|
||||
Multiple = math.floor( Number / 10 ) * 10
|
||||
elseif Number >= 0 then
|
||||
Multiple = Number
|
||||
end
|
||||
Sentence = tostring( Multiple )
|
||||
if Speech[Sentence] then
|
||||
self:I( { Speech = Speech[Sentence].Sentence, Data = Speech[Sentence].Data } )
|
||||
self:NewTransmission( Speech[Sentence].Data[1] .. ".wav", Speech[Sentence].Data[2], Langauge .. "/" )
|
||||
end
|
||||
Number = Number - Multiple
|
||||
Number = ( Number == 0 ) and -1 or Number
|
||||
end
|
||||
return RemainderSentence
|
||||
end
|
||||
return OriginalSentence
|
||||
else
|
||||
return ""
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Speak a sentence.
|
||||
-- @param #RADIOSPEECH self
|
||||
-- @param #string Sentence The sentence to be spoken.
|
||||
function RADIOSPEECH:Speak( Sentence, Language )
|
||||
|
||||
self:I( { Sentence, Language } )
|
||||
|
||||
local Language = Language or "EN"
|
||||
|
||||
self:I( { Language = Language } )
|
||||
|
||||
-- If there is no node for Speech, then we start at the first nodes of the language.
|
||||
local Speech = self.Speech[Language]
|
||||
|
||||
self:I( { Speech = Speech, Language = Language } )
|
||||
|
||||
self:NewTransmission( "_In.wav", 0.52, Language .. "/" )
|
||||
|
||||
repeat
|
||||
|
||||
Sentence = self:SpeakWords( Sentence, Speech, Language )
|
||||
|
||||
self:I( { Sentence = Sentence } )
|
||||
|
||||
Sentence = self:SpeakDigits( Sentence, Speech, Language )
|
||||
|
||||
self:I( { Sentence = Sentence } )
|
||||
|
||||
-- Sentence = self:SpeakSymbols( Sentence, Speech )
|
||||
--
|
||||
-- self:I( { Sentence = Sentence } )
|
||||
|
||||
until not Sentence or Sentence == ""
|
||||
|
||||
self:NewTransmission( "_Out.wav", 0.28, Language .. "/" )
|
||||
|
||||
end
|
||||
@@ -1,20 +1,26 @@
|
||||
--- **Core** -- **REPORT** class provides a handy means to create messages and reports.
|
||||
--- **Core** - Provides a handy means to create messages and reports.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
-- * Create text blocks that are formatted.
|
||||
-- * Create automatic indents.
|
||||
-- * Variate the delimiters between reporting lines.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Authors:
|
||||
--
|
||||
-- * FlightControl : Design & Programming
|
||||
--
|
||||
-- ### Contributions:
|
||||
-- ### Authors: FlightControl : Design & Programming
|
||||
--
|
||||
-- @module Core.Report
|
||||
-- @image Core_Report.JPG
|
||||
|
||||
|
||||
--- The REPORT class
|
||||
-- @type REPORT
|
||||
--- @type REPORT
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
--- Provides a handy means to create messages and reports.
|
||||
-- @field #REPORT
|
||||
REPORT = {
|
||||
ClassName = "REPORT",
|
||||
Title = "",
|
||||
@@ -64,11 +70,12 @@ function REPORT:Add( Text )
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add a new line to a REPORT.
|
||||
--- Add a new line to a REPORT, but indented. A separator character can be specified to separate the reported lines visually.
|
||||
-- @param #REPORT self
|
||||
-- @param #string Text
|
||||
-- @param #string Text The report text.
|
||||
-- @param #string Separator (optional) The start of each report line can begin with an optional separator character. This can be a "-", or "#", or "*". You're free to choose what you find the best.
|
||||
-- @return #REPORT
|
||||
function REPORT:AddIndent( Text, Separator ) --R2.1
|
||||
function REPORT:AddIndent( Text, Separator )
|
||||
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
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
--
|
||||
-- Takes care of the creation and dispatching of scheduled functions for SCHEDULER objects.
|
||||
--
|
||||
-- This class is tricky and needs some thorought explanation.
|
||||
-- This class is tricky and needs some thorough explanation.
|
||||
-- SCHEDULE classes are used to schedule functions for objects, or as persistent objects.
|
||||
-- The SCHEDULEDISPATCHER class ensures that:
|
||||
--
|
||||
@@ -13,9 +13,10 @@
|
||||
-- - Scheduled functions are automatically removed when the schedule is finished, according the SCHEDULER object parameters.
|
||||
--
|
||||
-- The SCHEDULEDISPATCHER class will manage SCHEDULER object in memory during garbage collection:
|
||||
-- - When a SCHEDULER object is not attached to another object (that is, it's first :Schedule() parameter is nil), then the SCHEDULER
|
||||
-- object is _persistent_ within memory.
|
||||
--
|
||||
-- - When a SCHEDULER object is not attached to another object (that is, it's first :Schedule() parameter is nil), then the SCHEDULER object is _persistent_ within memory.
|
||||
-- - When a SCHEDULER object *is* attached to another object, then the SCHEDULER object is _not persistent_ within memory after a garbage collection!
|
||||
--
|
||||
-- The none persistency of SCHEDULERS attached to objects is required to allow SCHEDULER objects to be garbage collectged, when the parent object is also desroyed or nillified and garbage collected.
|
||||
-- Even when there are pending timer scheduled functions to be executed for the SCHEDULER object,
|
||||
-- these will not be executed anymore when the SCHEDULER object has been destroyed.
|
||||
@@ -33,13 +34,41 @@
|
||||
-- @module Core.ScheduleDispatcher
|
||||
-- @image Core_Schedule_Dispatcher.JPG
|
||||
|
||||
--- SCHEDULEDISPATCHER class.
|
||||
-- @type SCHEDULEDISPATCHER
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @field #number CallID Call ID counter.
|
||||
-- @field #table PersistentSchedulers Persistant schedulers.
|
||||
-- @field #table ObjectSchedulers Schedulers that only exist as long as the master object exists.
|
||||
-- @field #table Schedule Meta table setmetatable( {}, { __mode = "k" } ).
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
--- The SCHEDULEDISPATCHER structure
|
||||
-- @type SCHEDULEDISPATCHER
|
||||
SCHEDULEDISPATCHER = {
|
||||
ClassName = "SCHEDULEDISPATCHER",
|
||||
CallID = 0,
|
||||
ClassName = "SCHEDULEDISPATCHER",
|
||||
CallID = 0,
|
||||
PersistentSchedulers = {},
|
||||
ObjectSchedulers = {},
|
||||
Schedule = nil,
|
||||
}
|
||||
|
||||
--- Player data table holding all important parameters of each player.
|
||||
-- @type SCHEDULEDISPATCHER.ScheduleData
|
||||
-- @field #function Function The schedule function to be called.
|
||||
-- @field #table Arguments Schedule function arguments.
|
||||
-- @field #number Start Start time in seconds.
|
||||
-- @field #number Repeat Repeat time intervall in seconds.
|
||||
-- @field #number Randomize Randomization factor [0,1].
|
||||
-- @field #number Stop Stop time in seconds.
|
||||
-- @field #number StartTime Time in seconds when the scheduler is created.
|
||||
-- @field #number ScheduleID Schedule ID.
|
||||
-- @field #function CallHandler Function to be passed to the DCS timer.scheduleFunction().
|
||||
-- @field #boolean ShowTrace If true, show tracing info.
|
||||
|
||||
--- Create a new schedule dispatcher object.
|
||||
-- @param #SCHEDULEDISPATCHER self
|
||||
-- @return #SCHEDULEDISPATCHER self
|
||||
function SCHEDULEDISPATCHER:New()
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
self:F3()
|
||||
@@ -51,15 +80,28 @@ end
|
||||
-- It is constructed as such that a garbage collection is executed on the weak tables, when the Scheduler is nillified.
|
||||
-- Nothing of this code should be modified without testing it thoroughly.
|
||||
-- @param #SCHEDULEDISPATCHER self
|
||||
-- @param Core.Scheduler#SCHEDULER Scheduler
|
||||
function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleArguments, Start, Repeat, Randomize, Stop )
|
||||
self:F2( { Scheduler, ScheduleFunction, ScheduleArguments, Start, Repeat, Randomize, Stop } )
|
||||
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
|
||||
-- @param #function ScheduleFunction Scheduler function.
|
||||
-- @param #table ScheduleArguments Table of arguments passed to the ScheduleFunction.
|
||||
-- @param #number Start Start time in seconds.
|
||||
-- @param #number Repeat Repeat interval in seconds.
|
||||
-- @param #number Randomize Radomization factor [0,1].
|
||||
-- @param #number Stop Stop time in seconds.
|
||||
-- @param #number TraceLevel Trace level [0,3].
|
||||
-- @param Core.Fsm#FSM Fsm Finite state model.
|
||||
-- @return #string Call ID or nil.
|
||||
function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleArguments, Start, Repeat, Randomize, Stop, TraceLevel, Fsm )
|
||||
self:F2( { Scheduler, ScheduleFunction, ScheduleArguments, Start, Repeat, Randomize, Stop, TraceLevel, Fsm } )
|
||||
|
||||
-- Increase counter.
|
||||
self.CallID = self.CallID + 1
|
||||
|
||||
-- Create ID.
|
||||
local CallID = self.CallID .. "#" .. ( Scheduler.MasterObject and Scheduler.MasterObject.GetClassNameAndID and Scheduler.MasterObject:GetClassNameAndID() or "" ) or ""
|
||||
|
||||
self:T2(string.format("Adding schedule #%d CallID=%s", self.CallID, CallID))
|
||||
|
||||
-- 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.
|
||||
-- Initialize PersistentSchedulers
|
||||
self.PersistentSchedulers = self.PersistentSchedulers or {}
|
||||
|
||||
-- Initialize the ObjectSchedulers array, which is a weakly coupled table.
|
||||
@@ -76,19 +118,60 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
||||
|
||||
self.Schedule = self.Schedule or setmetatable( {}, { __mode = "k" } )
|
||||
self.Schedule[Scheduler] = self.Schedule[Scheduler] or {}
|
||||
self.Schedule[Scheduler][CallID] = {}
|
||||
self.Schedule[Scheduler][CallID] = {} --#SCHEDULEDISPATCHER.ScheduleData
|
||||
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].Start = Start + 0.1
|
||||
self.Schedule[Scheduler][CallID].Repeat = Repeat or 0
|
||||
self.Schedule[Scheduler][CallID].Randomize = Randomize or 0
|
||||
self.Schedule[Scheduler][CallID].Stop = Stop
|
||||
|
||||
|
||||
-- This section handles the tracing of the scheduled calls.
|
||||
-- Because these calls will be executed with a delay, we inspect the place where these scheduled calls are initiated.
|
||||
-- The Info structure contains the output of the debug.getinfo() calls, which inspects the call stack for the function name, line number and source name.
|
||||
-- The call stack has many levels, and the correct semantical function call depends on where in the code AddSchedule was "used".
|
||||
-- - Using SCHEDULER:New()
|
||||
-- - Using Schedule:AddSchedule()
|
||||
-- - Using Fsm:__Func()
|
||||
-- - Using Class:ScheduleOnce()
|
||||
-- - Using Class:ScheduleRepeat()
|
||||
-- - ...
|
||||
-- So for each of these scheduled call variations, AddSchedule is the workhorse which will schedule the call.
|
||||
-- But the correct level with the correct semantical function location will differ depending on the above scheduled call invocation forms.
|
||||
-- That's where the field TraceLevel contains optionally the level in the call stack where the call information is obtained.
|
||||
-- The TraceLevel field indicates the correct level where the semantical scheduled call was invoked within the source, ensuring that function name, line number and source name are correct.
|
||||
-- There is one quick ...
|
||||
-- The FSM class models scheduled calls using the __Func syntax. However, these functions are "tailed".
|
||||
-- There aren't defined anywhere within the source code, but rather implemented as triggers within the FSM logic,
|
||||
-- and using the onbefore, onafter, onenter, onleave prefixes. (See the FSM for details).
|
||||
-- Therefore, in the call stack, at the TraceLevel these functions are mentioned as "tail calls", and the Info.name field will be nil as a result.
|
||||
-- To obtain the correct function name for FSM object calls, the function is mentioned in the call stack at a higher stack level.
|
||||
-- So when function name stored in Info.name is nil, then I inspect the function name within the call stack one level higher.
|
||||
-- So this little piece of code does its magic wonderfully, preformance overhead is neglectible, as scheduled calls don't happen that often.
|
||||
|
||||
local Info = {}
|
||||
|
||||
if debug then
|
||||
TraceLevel = TraceLevel or 2
|
||||
Info = debug.getinfo( TraceLevel, "nlS" )
|
||||
local name_fsm = debug.getinfo( TraceLevel - 1, "n" ).name -- #string
|
||||
if name_fsm then
|
||||
Info.name = name_fsm
|
||||
end
|
||||
end
|
||||
|
||||
self:T3( self.Schedule[Scheduler][CallID] )
|
||||
|
||||
self.Schedule[Scheduler][CallID].CallHandler = function( CallID )
|
||||
--self:E( CallID )
|
||||
--- Function passed to the DCS timer.scheduleFunction()
|
||||
self.Schedule[Scheduler][CallID].CallHandler = function( Params )
|
||||
|
||||
local CallID = Params.CallID
|
||||
local Info = Params.Info or {}
|
||||
local Source = Info.source or "?"
|
||||
local Line = Info.currentline or "?"
|
||||
local Name = Info.name or "?"
|
||||
|
||||
local ErrorHandler = function( errmsg )
|
||||
env.info( "Error in timer function: " .. errmsg )
|
||||
@@ -98,7 +181,8 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
||||
return errmsg
|
||||
end
|
||||
|
||||
local Scheduler = self.ObjectSchedulers[CallID]
|
||||
-- Get object or persistant scheduler object.
|
||||
local Scheduler = self.ObjectSchedulers[CallID] --Core.Scheduler#SCHEDULER
|
||||
if not Scheduler then
|
||||
Scheduler = self.PersistentSchedulers[CallID]
|
||||
end
|
||||
@@ -107,30 +191,42 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
||||
|
||||
if Scheduler then
|
||||
|
||||
local MasterObject = tostring(Scheduler.MasterObject)
|
||||
local Schedule = self.Schedule[Scheduler][CallID]
|
||||
local MasterObject = tostring(Scheduler.MasterObject)
|
||||
|
||||
-- Schedule object.
|
||||
local Schedule = self.Schedule[Scheduler][CallID] --#SCHEDULEDISPATCHER.ScheduleData
|
||||
|
||||
--self:T3( { Schedule = Schedule } )
|
||||
|
||||
local SchedulerObject = Scheduler.SchedulerObject
|
||||
--local ScheduleObjectName = Scheduler.SchedulerObject:GetNameAndClassID()
|
||||
local ScheduleFunction = Schedule.Function
|
||||
local ScheduleArguments = Schedule.Arguments
|
||||
local Start = Schedule.Start
|
||||
local Repeat = Schedule.Repeat or 0
|
||||
local Randomize = Schedule.Randomize or 0
|
||||
local Stop = Schedule.Stop or 0
|
||||
local ScheduleID = Schedule.ScheduleID
|
||||
local SchedulerObject = Scheduler.MasterObject --Scheduler.SchedulerObject Now is this the Maste or Scheduler object?
|
||||
local ShowTrace = Scheduler.ShowTrace
|
||||
|
||||
local ScheduleFunction = Schedule.Function
|
||||
local ScheduleArguments = Schedule.Arguments or {}
|
||||
local Start = Schedule.Start
|
||||
local Repeat = Schedule.Repeat or 0
|
||||
local Randomize = Schedule.Randomize or 0
|
||||
local Stop = Schedule.Stop or 0
|
||||
local ScheduleID = Schedule.ScheduleID
|
||||
|
||||
|
||||
local Prefix = ( Repeat == 0 ) and "--->" or "+++>"
|
||||
|
||||
local Status, Result
|
||||
--self:E( { SchedulerObject = SchedulerObject } )
|
||||
if SchedulerObject then
|
||||
local function Timer()
|
||||
if ShowTrace then
|
||||
SchedulerObject:T( Prefix .. Name .. ":" .. Line .. " (" .. Source .. ")" )
|
||||
end
|
||||
return ScheduleFunction( SchedulerObject, unpack( ScheduleArguments ) )
|
||||
end
|
||||
Status, Result = xpcall( Timer, ErrorHandler )
|
||||
else
|
||||
local function Timer()
|
||||
if ShowTrace then
|
||||
self:T( Prefix .. Name .. ":" .. Line .. " (" .. Source .. ")" )
|
||||
end
|
||||
return ScheduleFunction( unpack( ScheduleArguments ) )
|
||||
end
|
||||
Status, Result = xpcall( Timer, ErrorHandler )
|
||||
@@ -139,39 +235,39 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
||||
local CurrentTime = timer.getTime()
|
||||
local StartTime = Schedule.StartTime
|
||||
|
||||
self:F3( { Master = MasterObject, CurrentTime = CurrentTime, StartTime = StartTime, Start = Start, Repeat = Repeat, Randomize = Randomize, Stop = Stop } )
|
||||
-- Debug info.
|
||||
self:F3( { CallID=CallID, ScheduleID=ScheduleID, 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
|
||||
local ScheduleTime =
|
||||
CurrentTime +
|
||||
Repeat +
|
||||
math.random(
|
||||
- ( Randomize * Repeat / 2 ),
|
||||
( Randomize * Repeat / 2 )
|
||||
) +
|
||||
0.01
|
||||
local ScheduleTime = CurrentTime + Repeat + math.random(- ( Randomize * Repeat / 2 ), ( Randomize * Repeat / 2 )) + 0.0001 -- Accuracy
|
||||
--self:T3( { Repeat = CallID, CurrentTime, ScheduleTime, ScheduleArguments } )
|
||||
return ScheduleTime -- returns the next time the function needs to be called.
|
||||
else
|
||||
self:Stop( Scheduler, CallID )
|
||||
end
|
||||
|
||||
else
|
||||
self:Stop( Scheduler, CallID )
|
||||
end
|
||||
else
|
||||
self:E( "Scheduled obsolete call for CallID: " .. CallID )
|
||||
self:I( "<<<>" .. Name .. ":" .. Line .. " (" .. Source .. ")" )
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
self:Start( Scheduler, CallID )
|
||||
self:Start( Scheduler, CallID, Info )
|
||||
|
||||
return CallID
|
||||
end
|
||||
|
||||
--- Remove schedule.
|
||||
-- @param #SCHEDULEDISPATCHER self
|
||||
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
|
||||
-- @param #table CallID Call ID.
|
||||
function SCHEDULEDISPATCHER:RemoveSchedule( Scheduler, CallID )
|
||||
self:F2( { Remove = CallID, Scheduler = Scheduler } )
|
||||
|
||||
@@ -181,46 +277,80 @@ function SCHEDULEDISPATCHER:RemoveSchedule( Scheduler, CallID )
|
||||
end
|
||||
end
|
||||
|
||||
function SCHEDULEDISPATCHER:Start( Scheduler, CallID )
|
||||
--- Start dispatcher.
|
||||
-- @param #SCHEDULEDISPATCHER self
|
||||
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
|
||||
-- @param #table CallID (Optional) Call ID.
|
||||
-- @param #string Info (Optional) Debug info.
|
||||
function SCHEDULEDISPATCHER:Start( Scheduler, CallID, Info )
|
||||
self:F2( { Start = CallID, Scheduler = Scheduler } )
|
||||
|
||||
|
||||
if CallID then
|
||||
local Schedule = self.Schedule[Scheduler]
|
||||
|
||||
local Schedule = self.Schedule[Scheduler][CallID] --#SCHEDULEDISPATCHER.ScheduleData
|
||||
|
||||
-- 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
|
||||
)
|
||||
if not Schedule.ScheduleID then
|
||||
|
||||
-- Current time in seconds.
|
||||
local Tnow=timer.getTime()
|
||||
|
||||
Schedule.StartTime = Tnow -- Set the StartTime field to indicate when the scheduler started.
|
||||
|
||||
-- Start DCS schedule function https://wiki.hoggitworld.com/view/DCS_func_scheduleFunction
|
||||
Schedule.ScheduleID = timer.scheduleFunction(Schedule.CallHandler, { CallID = CallID, Info = Info }, Tnow + Schedule.Start)
|
||||
|
||||
self:T(string.format("Starting scheduledispatcher Call ID=%s ==> Schedule ID=%s", tostring(CallID), tostring(Schedule.ScheduleID)))
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
-- Recursive.
|
||||
for CallID, Schedule in pairs( self.Schedule[Scheduler] or {} ) do
|
||||
self:Start( Scheduler, CallID ) -- Recursive
|
||||
self:Start( Scheduler, CallID, Info ) -- Recursive
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
--- Stop dispatcher.
|
||||
-- @param #SCHEDULEDISPATCHER self
|
||||
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
|
||||
-- @param #table CallID Call ID.
|
||||
function SCHEDULEDISPATCHER:Stop( Scheduler, CallID )
|
||||
self:F2( { Stop = CallID, Scheduler = Scheduler } )
|
||||
|
||||
if CallID then
|
||||
local Schedule = self.Schedule[Scheduler]
|
||||
-- Only stop when there is a ScheduleID defined for the CallID.
|
||||
-- So, when the scheduler was stopped before, do nothing.
|
||||
if Schedule[CallID].ScheduleID then
|
||||
timer.removeFunction( Schedule[CallID].ScheduleID )
|
||||
Schedule[CallID].ScheduleID = nil
|
||||
|
||||
local Schedule = self.Schedule[Scheduler][CallID] --#SCHEDULEDISPATCHER.ScheduleData
|
||||
|
||||
-- Only stop when there is a ScheduleID defined for the CallID. So, when the scheduler was stopped before, do nothing.
|
||||
if Schedule.ScheduleID then
|
||||
|
||||
self:T(string.format("scheduledispatcher stopping scheduler CallID=%s, ScheduleID=%s", tostring(CallID), tostring(Schedule.ScheduleID)))
|
||||
|
||||
-- Remove schedule function https://wiki.hoggitworld.com/view/DCS_func_removeFunction
|
||||
timer.removeFunction(Schedule.ScheduleID)
|
||||
|
||||
Schedule.ScheduleID = nil
|
||||
|
||||
else
|
||||
self:T(string.format("Error no ScheduleID for CallID=%s", tostring(CallID)))
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
for CallID, Schedule in pairs( self.Schedule[Scheduler] or {} ) do
|
||||
self:Stop( Scheduler, CallID ) -- Recursive
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
--- Clear all schedules by stopping all dispatchers.
|
||||
-- @param #SCHEDULEDISPATCHER self
|
||||
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
|
||||
function SCHEDULEDISPATCHER:Clear( Scheduler )
|
||||
self:F2( { Scheduler = Scheduler } )
|
||||
|
||||
@@ -229,5 +359,19 @@ function SCHEDULEDISPATCHER:Clear( Scheduler )
|
||||
end
|
||||
end
|
||||
|
||||
--- Shopw tracing info.
|
||||
-- @param #SCHEDULEDISPATCHER self
|
||||
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
|
||||
function SCHEDULEDISPATCHER:ShowTrace( Scheduler )
|
||||
self:F2( { Scheduler = Scheduler } )
|
||||
Scheduler.ShowTrace = true
|
||||
end
|
||||
|
||||
--- No tracing info.
|
||||
-- @param #SCHEDULEDISPATCHER self
|
||||
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
|
||||
function SCHEDULEDISPATCHER:NoTrace( Scheduler )
|
||||
self:F2( { Scheduler = Scheduler } )
|
||||
Scheduler.ShowTrace = false
|
||||
end
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
--- **Core** -- SCHEDULER prepares and handles the **execution of functions over scheduled time (intervals)**.
|
||||
--- **Core** - Prepares and handles the execution of functions over scheduled time (intervals).
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- SCHEDULER manages the **scheduling of functions**:
|
||||
-- ## Features:
|
||||
--
|
||||
-- * optionally in an optional specified time interval,
|
||||
-- * optionally **repeating** with a specified time repeat interval,
|
||||
-- * optionally **randomizing** with a specified time interval randomization factor,
|
||||
-- * optionally **stop** the repeating after a specified time interval.
|
||||
-- * Schedule functions over time,
|
||||
-- * optionally in an optional specified time interval,
|
||||
-- * optionally **repeating** with a specified time repeat interval,
|
||||
-- * optionally **randomizing** with a specified time interval randomization factor,
|
||||
-- * optionally **stop** the repeating after a specified time interval.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -42,7 +43,9 @@
|
||||
|
||||
--- The SCHEDULER class
|
||||
-- @type SCHEDULER
|
||||
-- @field #number ScheduleID the ID of the scheduler.
|
||||
-- @field #table Schedules Table of schedules.
|
||||
-- @field #table MasterObject Master object.
|
||||
-- @field #boolean ShowTrace Trace info if true.
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
|
||||
@@ -68,53 +71,53 @@
|
||||
--
|
||||
-- * @{#SCHEDULER.New}( nil ): Setup a new SCHEDULER object, which is persistently executed after garbage collection.
|
||||
--
|
||||
-- SchedulerObject = SCHEDULER:New()
|
||||
-- SchedulerID = SchedulerObject:Schedule( nil, ScheduleFunction, {} )
|
||||
-- MasterObject = SCHEDULER:New()
|
||||
-- SchedulerID = MasterObject:Schedule( nil, ScheduleFunction, {} )
|
||||
--
|
||||
-- The above example creates a new SchedulerObject, but does not schedule anything.
|
||||
-- A separate schedule is created by using the SchedulerObject using the method :Schedule..., which returns a ScheduleID
|
||||
-- The above example creates a new MasterObject, but does not schedule anything.
|
||||
-- A separate schedule is created by using the MasterObject using the method :Schedule..., which returns a ScheduleID
|
||||
--
|
||||
-- ### Construct a SCHEDULER object without a volatile schedule, but volatile to the Object existence...
|
||||
--
|
||||
-- * @{#SCHEDULER.New}( Object ): Setup a new SCHEDULER object, which is linked to the Object. When the Object is nillified or destroyed, the SCHEDULER object will also be destroyed and stopped after garbage collection.
|
||||
--
|
||||
-- ZoneObject = ZONE:New( "ZoneName" )
|
||||
-- SchedulerObject = SCHEDULER:New( ZoneObject )
|
||||
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {} )
|
||||
-- MasterObject = SCHEDULER:New( ZoneObject )
|
||||
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {} )
|
||||
-- ...
|
||||
-- ZoneObject = nil
|
||||
-- garbagecollect()
|
||||
--
|
||||
-- The above example creates a new SchedulerObject, but does not schedule anything, and is bound to the existence of ZoneObject, which is a ZONE.
|
||||
-- A separate schedule is created by using the SchedulerObject using the method :Schedule()..., which returns a ScheduleID
|
||||
-- The above example creates a new MasterObject, but does not schedule anything, and is bound to the existence of ZoneObject, which is a ZONE.
|
||||
-- A separate schedule is created by using the MasterObject using the method :Schedule()..., which returns a ScheduleID
|
||||
-- Later in the logic, the ZoneObject is put to nil, and garbage is collected.
|
||||
-- As a result, the ScheduleObject will cancel any planned schedule.
|
||||
-- As a result, the MasterObject will cancel any planned schedule.
|
||||
--
|
||||
-- ### Construct a SCHEDULER object with a persistent schedule.
|
||||
--
|
||||
-- * @{#SCHEDULER.New}( nil, Function, FunctionArguments, Start, ... ): Setup a new persistent SCHEDULER object, and start a new schedule for the Function with the defined FunctionArguments according the Start and sequent parameters.
|
||||
--
|
||||
-- SchedulerObject, SchedulerID = SCHEDULER:New( nil, ScheduleFunction, {} )
|
||||
-- MasterObject, SchedulerID = SCHEDULER:New( nil, ScheduleFunction, {} )
|
||||
--
|
||||
-- The above example creates a new SchedulerObject, and does schedule the first schedule as part of the call.
|
||||
-- Note that 2 variables are returned here: SchedulerObject, ScheduleID...
|
||||
-- The above example creates a new MasterObject, and does schedule the first schedule as part of the call.
|
||||
-- Note that 2 variables are returned here: MasterObject, ScheduleID...
|
||||
--
|
||||
-- ### Construct a SCHEDULER object without a schedule, but volatile to the Object existence...
|
||||
--
|
||||
-- * @{#SCHEDULER.New}( Object, Function, FunctionArguments, Start, ... ): Setup a new SCHEDULER object, linked to Object, and start a new schedule for the Function with the defined FunctionArguments according the Start and sequent parameters.
|
||||
--
|
||||
-- ZoneObject = ZONE:New( "ZoneName" )
|
||||
-- SchedulerObject, SchedulerID = SCHEDULER:New( ZoneObject, ScheduleFunction, {} )
|
||||
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {} )
|
||||
-- MasterObject, SchedulerID = SCHEDULER:New( ZoneObject, ScheduleFunction, {} )
|
||||
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {} )
|
||||
-- ...
|
||||
-- ZoneObject = nil
|
||||
-- garbagecollect()
|
||||
--
|
||||
-- The above example creates a new SchedulerObject, and schedules a method call (ScheduleFunction),
|
||||
-- The above example creates a new MasterObject, and schedules a method call (ScheduleFunction),
|
||||
-- and is bound to the existence of ZoneObject, which is a ZONE object (ZoneObject).
|
||||
-- Both a ScheduleObject and a SchedulerID variable are returned.
|
||||
-- Both a MasterObject and a SchedulerID variable are returned.
|
||||
-- Later in the logic, the ZoneObject is put to nil, and garbage is collected.
|
||||
-- As a result, the ScheduleObject will cancel the planned schedule.
|
||||
-- As a result, the MasterObject will cancel the planned schedule.
|
||||
--
|
||||
-- ## SCHEDULER timer stopping and (re-)starting.
|
||||
--
|
||||
@@ -124,15 +127,15 @@
|
||||
-- * @{#SCHEDULER.Stop}(): Stop the schedules within the SCHEDULER object. If a CallID is provided to :Stop(), then only the schedule referenced by CallID will be stopped.
|
||||
--
|
||||
-- ZoneObject = ZONE:New( "ZoneName" )
|
||||
-- SchedulerObject, SchedulerID = SCHEDULER:New( ZoneObject, ScheduleFunction, {} )
|
||||
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 10 )
|
||||
-- MasterObject, SchedulerID = SCHEDULER:New( ZoneObject, ScheduleFunction, {} )
|
||||
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 10 )
|
||||
-- ...
|
||||
-- SchedulerObject:Stop( SchedulerID )
|
||||
-- MasterObject:Stop( SchedulerID )
|
||||
-- ...
|
||||
-- SchedulerObject:Start( SchedulerID )
|
||||
-- MasterObject:Start( SchedulerID )
|
||||
--
|
||||
-- The above example creates a new SchedulerObject, and does schedule the first schedule as part of the call.
|
||||
-- Note that 2 variables are returned here: SchedulerObject, ScheduleID...
|
||||
-- The above example creates a new MasterObject, and does schedule the first schedule as part of the call.
|
||||
-- Note that 2 variables are returned here: MasterObject, ScheduleID...
|
||||
-- Later in the logic, the repeating schedule with SchedulerID is stopped.
|
||||
-- A bit later, the repeating schedule with SchedulerId is (re)-started.
|
||||
--
|
||||
@@ -144,32 +147,32 @@
|
||||
-- Consider the following code fragment of the SCHEDULER object creation.
|
||||
--
|
||||
-- ZoneObject = ZONE:New( "ZoneName" )
|
||||
-- SchedulerObject = SCHEDULER:New( ZoneObject )
|
||||
-- MasterObject = SCHEDULER:New( ZoneObject )
|
||||
--
|
||||
-- Several parameters can be specified that influence the behaviour of a Schedule.
|
||||
--
|
||||
-- ### A single schedule, immediately executed
|
||||
--
|
||||
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {} )
|
||||
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {} )
|
||||
--
|
||||
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within milleseconds ...
|
||||
--
|
||||
-- ### A single schedule, planned over time
|
||||
--
|
||||
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {}, 10 )
|
||||
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {}, 10 )
|
||||
--
|
||||
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds ...
|
||||
--
|
||||
-- ### A schedule with a repeating time interval, planned over time
|
||||
--
|
||||
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60 )
|
||||
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60 )
|
||||
--
|
||||
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds,
|
||||
-- and repeating 60 every seconds ...
|
||||
--
|
||||
-- ### A schedule with a repeating time interval, planned over time, with time interval randomization
|
||||
--
|
||||
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60, 0.5 )
|
||||
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60, 0.5 )
|
||||
--
|
||||
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds,
|
||||
-- and repeating 60 seconds, with a 50% time interval randomization ...
|
||||
@@ -179,7 +182,7 @@
|
||||
--
|
||||
-- ### A schedule with a repeating time interval, planned over time, with time interval randomization, and stop after a time interval
|
||||
--
|
||||
-- SchedulerID = SchedulerObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60, 0.5, 300 )
|
||||
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {}, 10, 60, 0.5, 300 )
|
||||
--
|
||||
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within 10 seconds,
|
||||
-- The schedule will repeat every 60 seconds.
|
||||
@@ -190,13 +193,15 @@
|
||||
--
|
||||
-- @field #SCHEDULER
|
||||
SCHEDULER = {
|
||||
ClassName = "SCHEDULER",
|
||||
Schedules = {},
|
||||
ClassName = "SCHEDULER",
|
||||
Schedules = {},
|
||||
MasterObject = nil,
|
||||
ShowTrace = nil,
|
||||
}
|
||||
|
||||
--- SCHEDULER constructor.
|
||||
-- @param #SCHEDULER self
|
||||
-- @param #table SchedulerObject Specified for which Moose object the timer is setup. If a value of nil is provided, a scheduler will be setup without an object reference.
|
||||
-- @param #table MasterObject Specified for which Moose object the timer is setup. If a value of nil is provided, a scheduler will be setup without an object reference.
|
||||
-- @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 SchedulerArguments Optional arguments that can be given as part of scheduler. The arguments need to be given as a table { param1, param 2, ... }.
|
||||
-- @param #number Start Specifies the amount of seconds that will be waited before the scheduling is started, and the event function is called.
|
||||
@@ -204,50 +209,51 @@ SCHEDULER = {
|
||||
-- @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.
|
||||
-- @return #SCHEDULER self.
|
||||
-- @return #number The ScheduleID of the planned schedule.
|
||||
function SCHEDULER:New( SchedulerObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop )
|
||||
-- @return #table The ScheduleID of the planned schedule.
|
||||
function SCHEDULER:New( MasterObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop )
|
||||
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #SCHEDULER
|
||||
self:F2( { Start, Repeat, RandomizeFactor, Stop } )
|
||||
|
||||
local ScheduleID = nil
|
||||
|
||||
self.MasterObject = SchedulerObject
|
||||
self.MasterObject = MasterObject
|
||||
self.ShowTrace = false
|
||||
|
||||
if SchedulerFunction then
|
||||
ScheduleID = self:Schedule( SchedulerObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop )
|
||||
ScheduleID = self:Schedule( MasterObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop, 3 )
|
||||
end
|
||||
|
||||
return self, ScheduleID
|
||||
end
|
||||
|
||||
--function SCHEDULER:_Destructor()
|
||||
-- --self:E("_Destructor")
|
||||
--
|
||||
-- _SCHEDULEDISPATCHER:RemoveSchedule( self.CallID )
|
||||
--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 #SCHEDULER self
|
||||
-- @param #table SchedulerObject Specified for which Moose object the timer is setup. If a value of nil is provided, a scheduler will be setup without an object reference.
|
||||
-- @param #table MasterObject Specified for which Moose object the timer is setup. If a value of nil is provided, a scheduler will be setup without an object reference.
|
||||
-- @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 SchedulerArguments Optional arguments that can be given as part of scheduler. The arguments need to be given as a table { param1, param 2, ... }.
|
||||
-- @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 Repeat Specifies the time 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.
|
||||
-- @return #number The ScheduleID of the planned schedule.
|
||||
function SCHEDULER:Schedule( SchedulerObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop )
|
||||
-- @param #number Stop Time interval in seconds after which the scheduler will be stoppe.
|
||||
-- @param #number TraceLevel Trace level [0,3]. Default 3.
|
||||
-- @param Core.Fsm#FSM Fsm Finite state model.
|
||||
-- @return #table The ScheduleID of the planned schedule.
|
||||
function SCHEDULER:Schedule( MasterObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop, TraceLevel, Fsm )
|
||||
self:F2( { Start, Repeat, RandomizeFactor, Stop } )
|
||||
self:T3( { SchedulerArguments } )
|
||||
|
||||
-- Debug info.
|
||||
local ObjectName = "-"
|
||||
if SchedulerObject and SchedulerObject.ClassName and SchedulerObject.ClassID then
|
||||
ObjectName = SchedulerObject.ClassName .. SchedulerObject.ClassID
|
||||
if MasterObject and MasterObject.ClassName and MasterObject.ClassID then
|
||||
ObjectName = MasterObject.ClassName .. MasterObject.ClassID
|
||||
end
|
||||
self:F3( { "Schedule :", ObjectName, tostring( SchedulerObject ), Start, Repeat, RandomizeFactor, Stop } )
|
||||
self.SchedulerObject = SchedulerObject
|
||||
self:F3( { "Schedule :", ObjectName, tostring( MasterObject ), Start, Repeat, RandomizeFactor, Stop } )
|
||||
|
||||
-- Set master object.
|
||||
self.MasterObject = MasterObject
|
||||
|
||||
-- Add schedule.
|
||||
local ScheduleID = _SCHEDULEDISPATCHER:AddSchedule(
|
||||
self,
|
||||
SchedulerFunction,
|
||||
@@ -255,7 +261,9 @@ function SCHEDULER:Schedule( SchedulerObject, SchedulerFunction, SchedulerArgume
|
||||
Start,
|
||||
Repeat,
|
||||
RandomizeFactor,
|
||||
Stop
|
||||
Stop,
|
||||
TraceLevel or 3,
|
||||
Fsm
|
||||
)
|
||||
|
||||
self.Schedules[#self.Schedules+1] = ScheduleID
|
||||
@@ -265,49 +273,47 @@ end
|
||||
|
||||
--- (Re-)Starts the schedules or a specific schedule if a valid ScheduleID is provided.
|
||||
-- @param #SCHEDULER self
|
||||
-- @param #number ScheduleID (optional) The ScheduleID of the planned (repeating) schedule.
|
||||
-- @param #string ScheduleID (Optional) The ScheduleID of the planned (repeating) schedule.
|
||||
function SCHEDULER:Start( ScheduleID )
|
||||
self:F3( { ScheduleID } )
|
||||
|
||||
self:T(string.format("Starting scheduler ID=%s", tostring(ScheduleID)))
|
||||
_SCHEDULEDISPATCHER:Start( self, ScheduleID )
|
||||
end
|
||||
|
||||
--- Stops the schedules or a specific schedule if a valid ScheduleID is provided.
|
||||
-- @param #SCHEDULER self
|
||||
-- @param #number ScheduleID (optional) The ScheduleID of the planned (repeating) schedule.
|
||||
-- @param #string ScheduleID (Optional) The ScheduleID of the planned (repeating) schedule.
|
||||
function SCHEDULER:Stop( ScheduleID )
|
||||
self:F3( { ScheduleID } )
|
||||
|
||||
self:T(string.format("Stopping scheduler ID=%s", tostring(ScheduleID)))
|
||||
_SCHEDULEDISPATCHER:Stop( self, ScheduleID )
|
||||
end
|
||||
|
||||
--- Removes a specific schedule if a valid ScheduleID is provided.
|
||||
-- @param #SCHEDULER self
|
||||
-- @param #number ScheduleID (optional) The ScheduleID of the planned (repeating) schedule.
|
||||
-- @param #string ScheduleID (optional) The ScheduleID of the planned (repeating) schedule.
|
||||
function SCHEDULER:Remove( ScheduleID )
|
||||
self:F3( { ScheduleID } )
|
||||
|
||||
_SCHEDULEDISPATCHER:Remove( self, ScheduleID )
|
||||
self:T(string.format("Removing scheduler ID=%s", tostring(ScheduleID)))
|
||||
_SCHEDULEDISPATCHER:RemoveSchedule( self, ScheduleID )
|
||||
end
|
||||
|
||||
--- Clears all pending schedules.
|
||||
-- @param #SCHEDULER self
|
||||
function SCHEDULER:Clear()
|
||||
self:F3( )
|
||||
|
||||
self:T(string.format("Clearing scheduler"))
|
||||
_SCHEDULEDISPATCHER:Clear( self )
|
||||
end
|
||||
|
||||
--- Show tracing for this scheduler.
|
||||
-- @param #SCHEDULER self
|
||||
function SCHEDULER:ShowTrace()
|
||||
_SCHEDULEDISPATCHER:ShowTrace( self )
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
--- No tracing for this scheduler.
|
||||
-- @param #SCHEDULER self
|
||||
function SCHEDULER:NoTrace()
|
||||
_SCHEDULEDISPATCHER:NoTrace( self )
|
||||
end
|
||||
|
||||
@@ -1,22 +1,35 @@
|
||||
--- **Core** -- SET_ classes define **collections** of objects to perform **bulk actions** and logically **group** objects.
|
||||
--- **Core** - Define collections of objects to perform bulk actions and logically group objects.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- SET_ classes group objects of the same type into a collection, which is either:
|
||||
-- ## Features:
|
||||
--
|
||||
-- * Manually managed using the **:Add...()** or **:Remove...()** methods. The initial SET can be filtered with the **@{#SET_BASE.FilterOnce}()** method
|
||||
-- * Dynamically maintain collections of objects.
|
||||
-- * Manually modify the collection, by adding or removing objects.
|
||||
-- * Collections of different types.
|
||||
-- * Validate the presence of objects in the collection.
|
||||
-- * Perform bulk actions on collection.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- Group objects or data of the same type into a collection, which is either:
|
||||
--
|
||||
-- * Manually managed using the **:Add...()** or **:Remove...()** methods. The initial SET can be filtered with the **@{#SET_BASE.FilterOnce}()** method.
|
||||
-- * Dynamically updated when new objects are created or objects are destroyed using the **@{#SET_BASE.FilterStart}()** method.
|
||||
--
|
||||
-- Various types of SET_ classes are available:
|
||||
--
|
||||
-- * @{#SET_UNIT}: Defines a colleciton of @{Wrapper.Unit}s filtered by filter criteria.
|
||||
-- * @{#SET_GROUP}: Defines a collection of @{Wrapper.Group}s filtered by filter criteria.
|
||||
-- * @{#SET_UNIT}: Defines a colleciton of @{Wrapper.Unit}s filtered by filter criteria.
|
||||
-- * @{#SET_STATIC}: Defines a collection of @{Wrapper.Static}s filtered by filter criteria.
|
||||
-- * @{#SET_CLIENT}: Defines a collection of @{Client}s filterd by filter criteria.
|
||||
-- * @{#SET_AIRBASE}: Defines a collection of @{Wrapper.Airbase}s filtered by filter criteria.
|
||||
-- * @{#SET_CARGO}: Defines a collection of @{Cargo.Cargo}s filtered by filter criteria.
|
||||
-- * @{#SET_ZONE}: Defines a collection of @{Core.Zone}s filtered by filter criteria.
|
||||
--
|
||||
-- These classes are derived from @{#SET_BASE}, which contains the main methods to manage SETs.
|
||||
-- These classes are derived from @{#SET_BASE}, which contains the main methods to manage the collections.
|
||||
--
|
||||
-- A multitude of other methods are available in SET_ classes that allow to:
|
||||
-- A multitude of other methods are available in the individual set classes that allow to:
|
||||
--
|
||||
-- * Validate the presence of objects in the SET.
|
||||
-- * Trigger events when objects in the SET change a zone presence.
|
||||
@@ -112,11 +125,25 @@ do -- SET_BASE
|
||||
self.Index = {}
|
||||
|
||||
self.CallScheduler = SCHEDULER:New( self )
|
||||
|
||||
|
||||
self:SetEventPriority( 2 )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Clear the Objects in the Set.
|
||||
-- @param #SET_BASE self
|
||||
-- @return #SET_BASE self
|
||||
function SET_BASE:Clear()
|
||||
|
||||
for Name, Object in pairs( self.Set ) do
|
||||
self:Remove( Name )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Finds an @{Core.Base#BASE} object based on the object Name.
|
||||
-- @param #SET_BASE self
|
||||
@@ -135,7 +162,7 @@ do -- SET_BASE
|
||||
function SET_BASE:GetSet()
|
||||
self:F2()
|
||||
|
||||
return self.Set
|
||||
return self.Set or {}
|
||||
end
|
||||
|
||||
--- Gets a list of the Names of the Objects in the Set.
|
||||
@@ -314,6 +341,25 @@ do -- SET_BASE
|
||||
return self
|
||||
end
|
||||
|
||||
--- Define the SET iterator **"limit"**.
|
||||
-- @param #SET_BASE self
|
||||
-- @param #number Limit Defines how many objects are evaluated of the set as part of the Some iterators. The default is 1.
|
||||
-- @return #SET_BASE self
|
||||
function SET_BASE:SetSomeIteratorLimit( Limit )
|
||||
|
||||
self.SomeIteratorLimit = Limit or 1
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the SET iterator **"limit"**.
|
||||
-- @param #SET_BASE self
|
||||
-- @return #number Defines how many objects are evaluated of the set as part of the Some iterators.
|
||||
function SET_BASE:GetSomeIteratorLimit()
|
||||
|
||||
return self.SomeIteratorLimit or self:Count()
|
||||
end
|
||||
|
||||
|
||||
--- Filters for the defined collection.
|
||||
-- @param #SET_BASE self
|
||||
@@ -338,7 +384,6 @@ do -- SET_BASE
|
||||
for ObjectName, Object in pairs( self.Database ) do
|
||||
|
||||
if self:IsIncludeObject( Object ) then
|
||||
self:E( { "Adding Object:", ObjectName } )
|
||||
self:Add( ObjectName, Object )
|
||||
end
|
||||
end
|
||||
@@ -396,9 +441,9 @@ do -- SET_BASE
|
||||
for ObjectID, ObjectData in pairs( self.Set ) do
|
||||
if NearestObject == nil then
|
||||
NearestObject = ObjectData
|
||||
ClosestDistance = PointVec2:DistanceFromVec2( ObjectData:GetVec2() )
|
||||
ClosestDistance = PointVec2:DistanceFromPointVec2( ObjectData:GetCoordinate() )
|
||||
else
|
||||
local Distance = PointVec2:DistanceFromVec2( ObjectData:GetVec2() )
|
||||
local Distance = PointVec2:DistanceFromPointVec2( ObjectData:GetCoordinate() )
|
||||
if Distance < ClosestDistance then
|
||||
NearestObject = ObjectData
|
||||
ClosestDistance = Distance
|
||||
@@ -512,6 +557,10 @@ do -- SET_BASE
|
||||
--- Iterate the SET_BASE and derived classes and call an iterator function for the given SET_BASE, providing the Object for each element within the set and optional parameters.
|
||||
-- @param #SET_BASE self
|
||||
-- @param #function IteratorFunction The function that will be called.
|
||||
-- @param #table arg Arguments of the IteratorFunction.
|
||||
-- @param #SET_BASE Set (Optional) The set to use. Default self:GetSet().
|
||||
-- @param #function Function (Optional) A function returning a #boolean true/false. Only if true, the IteratorFunction is called.
|
||||
-- @param #table FunctionArguments (Optional) Function arguments.
|
||||
-- @return #SET_BASE self
|
||||
function SET_BASE:ForEach( IteratorFunction, arg, Set, Function, FunctionArguments )
|
||||
self:F3( arg )
|
||||
@@ -519,6 +568,63 @@ do -- SET_BASE
|
||||
Set = Set or self:GetSet()
|
||||
arg = arg or {}
|
||||
|
||||
local function CoRoutine()
|
||||
local Count = 0
|
||||
for ObjectID, ObjectData in pairs( Set ) do
|
||||
local Object = ObjectData
|
||||
self:T3( Object )
|
||||
if Function then
|
||||
if Function( unpack( FunctionArguments or {} ), Object ) == true then
|
||||
IteratorFunction( Object, unpack( arg ) )
|
||||
end
|
||||
else
|
||||
IteratorFunction( Object, unpack( arg ) )
|
||||
end
|
||||
Count = Count + 1
|
||||
-- if Count % self.YieldInterval == 0 then
|
||||
-- coroutine.yield( false )
|
||||
-- end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- local co = coroutine.create( CoRoutine )
|
||||
local co = CoRoutine
|
||||
|
||||
local function Schedule()
|
||||
|
||||
-- local status, res = coroutine.resume( co )
|
||||
local status, res = co()
|
||||
self:T3( { status, res } )
|
||||
|
||||
if status == false then
|
||||
error( res )
|
||||
end
|
||||
if res == false then
|
||||
return true -- resume next time the loop
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
--self.CallScheduler:Schedule( self, Schedule, {}, self.TimeInterval, self.TimeInterval, 0 )
|
||||
Schedule()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Iterate the SET_BASE and derived classes and call an iterator function for the given SET_BASE, providing the Object for each element within the set and optional parameters.
|
||||
-- @param #SET_BASE self
|
||||
-- @param #function IteratorFunction The function that will be called.
|
||||
-- @return #SET_BASE self
|
||||
function SET_BASE:ForSome( IteratorFunction, arg, Set, Function, FunctionArguments )
|
||||
self:F3( arg )
|
||||
|
||||
Set = Set or self:GetSet()
|
||||
arg = arg or {}
|
||||
|
||||
local Limit = self:GetSomeIteratorLimit()
|
||||
|
||||
local function CoRoutine()
|
||||
local Count = 0
|
||||
for ObjectID, ObjectData in pairs( Set ) do
|
||||
@@ -532,6 +638,9 @@ do -- SET_BASE
|
||||
IteratorFunction( Object, unpack( arg ) )
|
||||
end
|
||||
Count = Count + 1
|
||||
if Count >= Limit then
|
||||
break
|
||||
end
|
||||
-- if Count % self.YieldInterval == 0 then
|
||||
-- coroutine.yield( false )
|
||||
-- end
|
||||
@@ -818,7 +927,40 @@ do -- SET_GROUP
|
||||
|
||||
return AliveSet.Set or {}
|
||||
end
|
||||
|
||||
--- Returns a report of of unit types.
|
||||
-- @param #SET_GROUP self
|
||||
-- @return Core.Report#REPORT A report of the unit types found. The key is the UnitTypeName and the value is the amount of unit types found.
|
||||
function SET_GROUP:GetUnitTypeNames()
|
||||
self:F2()
|
||||
|
||||
local MT = {} -- Message Text
|
||||
local UnitTypes = {}
|
||||
|
||||
local ReportUnitTypes = REPORT:New()
|
||||
|
||||
for GroupID, GroupData in pairs( self:GetSet() ) do
|
||||
local Units = GroupData:GetUnits()
|
||||
for UnitID, UnitData in pairs( Units ) do
|
||||
if UnitData:IsAlive() then
|
||||
local UnitType = UnitData:GetTypeName()
|
||||
|
||||
if not UnitTypes[UnitType] then
|
||||
UnitTypes[UnitType] = 1
|
||||
else
|
||||
UnitTypes[UnitType] = UnitTypes[UnitType] + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for UnitTypeID, UnitType in pairs( UnitTypes ) do
|
||||
ReportUnitTypes:Add( UnitType .. " of " .. UnitTypeID )
|
||||
end
|
||||
|
||||
return ReportUnitTypes
|
||||
end
|
||||
|
||||
--- Add a GROUP to SET_GROUP.
|
||||
-- Note that for each unit in the group that is set, a default cargo bay limit is initialized.
|
||||
-- @param Core.Set#SET_GROUP self
|
||||
@@ -1117,7 +1259,7 @@ do -- SET_GROUP
|
||||
|
||||
--- Iterate the SET_GROUP and call an iterator function for each GROUP object, providing the GROUP and optional parameters.
|
||||
-- @param #SET_GROUP self
|
||||
-- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the SET_GROUP. The function needs to accept a GROUP parameter.
|
||||
-- @param #function IteratorFunction The function that will be called for all GROUP in the SET_GROUP. The function needs to accept a GROUP parameter.
|
||||
-- @return #SET_GROUP self
|
||||
function SET_GROUP:ForEachGroup( IteratorFunction, ... )
|
||||
self:F2( arg )
|
||||
@@ -1127,6 +1269,18 @@ do -- SET_GROUP
|
||||
return self
|
||||
end
|
||||
|
||||
--- Iterate the SET_GROUP and call an iterator function for some GROUP objects, providing the GROUP and optional parameters.
|
||||
-- @param #SET_GROUP self
|
||||
-- @param #function IteratorFunction The function that will be called for some GROUP in the SET_GROUP. The function needs to accept a GROUP parameter.
|
||||
-- @return #SET_GROUP self
|
||||
function SET_GROUP:ForSomeGroup( IteratorFunction, ... )
|
||||
self:F2( arg )
|
||||
|
||||
self:ForSome( IteratorFunction, arg, self:GetSet() )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Iterate the SET_GROUP and call an iterator function for each **alive** GROUP object, providing the GROUP and optional parameters.
|
||||
-- @param #SET_GROUP self
|
||||
-- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the SET_GROUP. The function needs to accept a GROUP parameter.
|
||||
@@ -1139,6 +1293,18 @@ do -- SET_GROUP
|
||||
return self
|
||||
end
|
||||
|
||||
--- Iterate the SET_GROUP and call an iterator function for some **alive** GROUP objects, providing the GROUP and optional parameters.
|
||||
-- @param #SET_GROUP self
|
||||
-- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the SET_GROUP. The function needs to accept a GROUP parameter.
|
||||
-- @return #SET_GROUP self
|
||||
function SET_GROUP:ForSomeGroupAlive( IteratorFunction, ... )
|
||||
self:F2( arg )
|
||||
|
||||
self:ForSome( IteratorFunction, arg, self:GetAliveSet() )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence completely in a @{Zone}, providing the GROUP and optional parameters to the called function.
|
||||
-- @param #SET_GROUP self
|
||||
-- @param Core.Zone#ZONE ZoneObject The Zone to be tested for.
|
||||
@@ -1408,6 +1574,35 @@ do -- SET_GROUP
|
||||
return Count
|
||||
end
|
||||
|
||||
--- Iterate the SET_GROUP and count how many GROUPs and UNITs are alive.
|
||||
-- @param #SET_GROUP self
|
||||
-- @return #number The number of GROUPs alive.
|
||||
-- @return #number The number of UNITs alive.
|
||||
function SET_GROUP:CountAlive()
|
||||
local CountG = 0
|
||||
local CountU = 0
|
||||
|
||||
local Set = self:GetSet()
|
||||
|
||||
for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP
|
||||
if GroupData and GroupData:IsAlive() then
|
||||
|
||||
CountG = CountG + 1
|
||||
|
||||
--Count Units.
|
||||
for _,_unit in pairs(GroupData:GetUnits()) do
|
||||
local unit=_unit --Wrapper.Unit#UNIT
|
||||
if unit and unit:IsAlive() then
|
||||
CountU=CountU+1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return CountG,CountU
|
||||
end
|
||||
|
||||
----- Iterate the SET_GROUP and call an interator function for each **alive** player, providing the Group of the player and optional parameters.
|
||||
---- @param #SET_GROUP self
|
||||
---- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_GROUP. The function needs to accept a GROUP parameter.
|
||||
@@ -1895,7 +2090,7 @@ do -- SET_UNIT
|
||||
if _DATABASE then
|
||||
self:_FilterStart()
|
||||
self:HandleEvent( EVENTS.Birth, self._EventOnBirth )
|
||||
self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrashOr )
|
||||
self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash )
|
||||
self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash )
|
||||
self:HandleEvent( EVENTS.RemoveUnit, self._EventOnDeadOrCrash )
|
||||
end
|
||||
@@ -1991,20 +2186,6 @@ do -- SET_UNIT
|
||||
return IsNotInZone
|
||||
end
|
||||
|
||||
|
||||
--- Check if minimal one element of the SET_UNIT is in the Zone.
|
||||
-- @param #SET_UNIT self
|
||||
-- @param #function IteratorFunction The function that will be called when there is an alive UNIT in the SET_UNIT. The function needs to accept a UNIT parameter.
|
||||
-- @return #SET_UNIT self
|
||||
function SET_UNIT:ForEachUnitInZone( IteratorFunction, ... )
|
||||
self:F2( arg )
|
||||
|
||||
self:ForEach( IteratorFunction, arg, self:GetSet() )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -2020,6 +2201,54 @@ do -- SET_UNIT
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Get the SET of the SET_UNIT **sorted per Threat Level**.
|
||||
--
|
||||
-- @param #SET_UNIT self
|
||||
-- @param #number FromThreatLevel The TreatLevel to start the evaluation **From** (this must be a value between 0 and 10).
|
||||
-- @param #number ToThreatLevel The TreatLevel to stop the evaluation **To** (this must be a value between 0 and 10).
|
||||
-- @return #SET_UNIT self
|
||||
-- @usage
|
||||
--
|
||||
--
|
||||
function SET_UNIT:GetSetPerThreatLevel( FromThreatLevel, ToThreatLevel )
|
||||
self:F2( arg )
|
||||
|
||||
local ThreatLevelSet = {}
|
||||
|
||||
if self:Count() ~= 0 then
|
||||
for UnitName, UnitObject in pairs( self.Set ) do
|
||||
local Unit = UnitObject -- Wrapper.Unit#UNIT
|
||||
|
||||
local ThreatLevel = Unit:GetThreatLevel()
|
||||
ThreatLevelSet[ThreatLevel] = ThreatLevelSet[ThreatLevel] or {}
|
||||
ThreatLevelSet[ThreatLevel].Set = ThreatLevelSet[ThreatLevel].Set or {}
|
||||
ThreatLevelSet[ThreatLevel].Set[UnitName] = UnitObject
|
||||
self:F( { ThreatLevel = ThreatLevel, ThreatLevelSet = ThreatLevelSet[ThreatLevel].Set } )
|
||||
end
|
||||
|
||||
|
||||
local OrderedPerThreatLevelSet = {}
|
||||
|
||||
local ThreatLevelIncrement = FromThreatLevel <= ToThreatLevel and 1 or -1
|
||||
|
||||
|
||||
for ThreatLevel = FromThreatLevel, ToThreatLevel, ThreatLevelIncrement do
|
||||
self:F( { ThreatLevel = ThreatLevel } )
|
||||
local ThreatLevelItem = ThreatLevelSet[ThreatLevel]
|
||||
if ThreatLevelItem then
|
||||
for UnitName, UnitObject in pairs( ThreatLevelItem.Set ) do
|
||||
table.insert( OrderedPerThreatLevelSet, UnitObject )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return OrderedPerThreatLevelSet
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Iterate the SET_UNIT **sorted *per Threat Level** and call an interator function for each **alive** UNIT, providing the UNIT and optional parameters.
|
||||
--
|
||||
-- @param #SET_UNIT self
|
||||
@@ -2380,6 +2609,23 @@ do -- SET_UNIT
|
||||
return GroundUnitCount
|
||||
end
|
||||
|
||||
--- Returns if the @{Set} has air targets.
|
||||
-- @param #SET_UNIT self
|
||||
-- @return #number The amount of air targets in the Set.
|
||||
function SET_UNIT:HasAirUnits()
|
||||
self:F2()
|
||||
|
||||
local AirUnitCount = 0
|
||||
for UnitID, UnitData in pairs( self:GetSet() ) do
|
||||
local UnitTest = UnitData -- Wrapper.Unit#UNIT
|
||||
if UnitTest:IsAir() then
|
||||
AirUnitCount = AirUnitCount + 1
|
||||
end
|
||||
end
|
||||
|
||||
return AirUnitCount
|
||||
end
|
||||
|
||||
--- Returns if the @{Set} has friendly ground units.
|
||||
-- @param #SET_UNIT self
|
||||
-- @return #number The amount of ground targets in the Set.
|
||||
@@ -2431,92 +2677,98 @@ do -- SET_UNIT
|
||||
-- @return #SET_UNIT self
|
||||
function SET_UNIT:IsIncludeObject( MUnit )
|
||||
self:F2( MUnit )
|
||||
local MUnitInclude = true
|
||||
|
||||
local MUnitInclude = false
|
||||
|
||||
if MUnit:IsAlive() ~= nil then
|
||||
|
||||
if self.Filter.Active ~= nil then
|
||||
local MUnitActive = false
|
||||
if self.Filter.Active == false or ( self.Filter.Active == true and MUnit:IsActive() == true ) then
|
||||
MUnitActive = true
|
||||
end
|
||||
MUnitInclude = MUnitInclude and MUnitActive
|
||||
end
|
||||
MUnitInclude = true
|
||||
|
||||
if self.Filter.Coalitions then
|
||||
local MUnitCoalition = false
|
||||
for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do
|
||||
self:F( { "Coalition:", MUnit:GetCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName } )
|
||||
if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == MUnit:GetCoalition() then
|
||||
MUnitCoalition = true
|
||||
if self.Filter.Active ~= nil then
|
||||
local MUnitActive = false
|
||||
if self.Filter.Active == false or ( self.Filter.Active == true and MUnit:IsActive() == true ) then
|
||||
MUnitActive = true
|
||||
end
|
||||
MUnitInclude = MUnitInclude and MUnitActive
|
||||
end
|
||||
MUnitInclude = MUnitInclude and MUnitCoalition
|
||||
end
|
||||
|
||||
if self.Filter.Categories then
|
||||
local MUnitCategory = false
|
||||
for CategoryID, CategoryName in pairs( self.Filter.Categories ) do
|
||||
self:T3( { "Category:", MUnit:GetDesc().category, self.FilterMeta.Categories[CategoryName], CategoryName } )
|
||||
if self.FilterMeta.Categories[CategoryName] and self.FilterMeta.Categories[CategoryName] == MUnit:GetDesc().category then
|
||||
MUnitCategory = true
|
||||
end
|
||||
end
|
||||
MUnitInclude = MUnitInclude and MUnitCategory
|
||||
end
|
||||
|
||||
if self.Filter.Types then
|
||||
local MUnitType = false
|
||||
for TypeID, TypeName in pairs( self.Filter.Types ) do
|
||||
self:T3( { "Type:", MUnit:GetTypeName(), TypeName } )
|
||||
if TypeName == MUnit:GetTypeName() then
|
||||
MUnitType = true
|
||||
end
|
||||
end
|
||||
MUnitInclude = MUnitInclude and MUnitType
|
||||
end
|
||||
|
||||
if self.Filter.Countries then
|
||||
local MUnitCountry = false
|
||||
for CountryID, CountryName in pairs( self.Filter.Countries ) do
|
||||
self:T3( { "Country:", MUnit:GetCountry(), CountryName } )
|
||||
if country.id[CountryName] == MUnit:GetCountry() then
|
||||
MUnitCountry = true
|
||||
end
|
||||
end
|
||||
MUnitInclude = MUnitInclude and MUnitCountry
|
||||
end
|
||||
|
||||
if self.Filter.UnitPrefixes then
|
||||
local MUnitPrefix = false
|
||||
for UnitPrefixId, UnitPrefix in pairs( self.Filter.UnitPrefixes ) do
|
||||
self:T3( { "Prefix:", string.find( MUnit:GetName(), UnitPrefix, 1 ), UnitPrefix } )
|
||||
if string.find( MUnit:GetName(), UnitPrefix, 1 ) then
|
||||
MUnitPrefix = true
|
||||
end
|
||||
end
|
||||
MUnitInclude = MUnitInclude and MUnitPrefix
|
||||
end
|
||||
|
||||
if self.Filter.RadarTypes then
|
||||
local MUnitRadar = false
|
||||
for RadarTypeID, RadarType in pairs( self.Filter.RadarTypes ) do
|
||||
self:T3( { "Radar:", RadarType } )
|
||||
if MUnit:HasSensors( Unit.SensorType.RADAR, RadarType ) == true then
|
||||
if MUnit:GetRadar() == true then -- This call is necessary to evaluate the SEAD capability.
|
||||
self:T3( "RADAR Found" )
|
||||
if self.Filter.Coalitions then
|
||||
local MUnitCoalition = false
|
||||
for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do
|
||||
self:F( { "Coalition:", MUnit:GetCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName } )
|
||||
if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == MUnit:GetCoalition() then
|
||||
MUnitCoalition = true
|
||||
end
|
||||
MUnitRadar = true
|
||||
end
|
||||
MUnitInclude = MUnitInclude and MUnitCoalition
|
||||
end
|
||||
MUnitInclude = MUnitInclude and MUnitRadar
|
||||
end
|
||||
|
||||
if self.Filter.SEAD then
|
||||
local MUnitSEAD = false
|
||||
if MUnit:HasSEAD() == true then
|
||||
self:T3( "SEAD Found" )
|
||||
MUnitSEAD = true
|
||||
|
||||
if self.Filter.Categories then
|
||||
local MUnitCategory = false
|
||||
for CategoryID, CategoryName in pairs( self.Filter.Categories ) do
|
||||
self:T3( { "Category:", MUnit:GetDesc().category, self.FilterMeta.Categories[CategoryName], CategoryName } )
|
||||
if self.FilterMeta.Categories[CategoryName] and self.FilterMeta.Categories[CategoryName] == MUnit:GetDesc().category then
|
||||
MUnitCategory = true
|
||||
end
|
||||
end
|
||||
MUnitInclude = MUnitInclude and MUnitCategory
|
||||
end
|
||||
|
||||
if self.Filter.Types then
|
||||
local MUnitType = false
|
||||
for TypeID, TypeName in pairs( self.Filter.Types ) do
|
||||
self:T3( { "Type:", MUnit:GetTypeName(), TypeName } )
|
||||
if TypeName == MUnit:GetTypeName() then
|
||||
MUnitType = true
|
||||
end
|
||||
end
|
||||
MUnitInclude = MUnitInclude and MUnitType
|
||||
end
|
||||
|
||||
if self.Filter.Countries then
|
||||
local MUnitCountry = false
|
||||
for CountryID, CountryName in pairs( self.Filter.Countries ) do
|
||||
self:T3( { "Country:", MUnit:GetCountry(), CountryName } )
|
||||
if country.id[CountryName] == MUnit:GetCountry() then
|
||||
MUnitCountry = true
|
||||
end
|
||||
end
|
||||
MUnitInclude = MUnitInclude and MUnitCountry
|
||||
end
|
||||
|
||||
if self.Filter.UnitPrefixes then
|
||||
local MUnitPrefix = false
|
||||
for UnitPrefixId, UnitPrefix in pairs( self.Filter.UnitPrefixes ) do
|
||||
self:T3( { "Prefix:", string.find( MUnit:GetName(), UnitPrefix, 1 ), UnitPrefix } )
|
||||
if string.find( MUnit:GetName(), UnitPrefix, 1 ) then
|
||||
MUnitPrefix = true
|
||||
end
|
||||
end
|
||||
MUnitInclude = MUnitInclude and MUnitPrefix
|
||||
end
|
||||
|
||||
if self.Filter.RadarTypes then
|
||||
local MUnitRadar = false
|
||||
for RadarTypeID, RadarType in pairs( self.Filter.RadarTypes ) do
|
||||
self:T3( { "Radar:", RadarType } )
|
||||
if MUnit:HasSensors( Unit.SensorType.RADAR, RadarType ) == true then
|
||||
if MUnit:GetRadar() == true then -- This call is necessary to evaluate the SEAD capability.
|
||||
self:T3( "RADAR Found" )
|
||||
end
|
||||
MUnitRadar = true
|
||||
end
|
||||
end
|
||||
MUnitInclude = MUnitInclude and MUnitRadar
|
||||
end
|
||||
|
||||
if self.Filter.SEAD then
|
||||
local MUnitSEAD = false
|
||||
if MUnit:HasSEAD() == true then
|
||||
self:T3( "SEAD Found" )
|
||||
MUnitSEAD = true
|
||||
end
|
||||
MUnitInclude = MUnitInclude and MUnitSEAD
|
||||
end
|
||||
MUnitInclude = MUnitInclude and MUnitSEAD
|
||||
end
|
||||
|
||||
self:T2( MUnitInclude )
|
||||
@@ -5230,4 +5482,324 @@ do -- SET_ZONE
|
||||
return nil
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
do -- SET_ZONE_GOAL
|
||||
|
||||
--- @type SET_ZONE_GOAL
|
||||
-- @extends Core.Set#SET_BASE
|
||||
|
||||
--- Mission designers can use the @{Core.Set#SET_ZONE_GOAL} class to build sets of zones of various types.
|
||||
--
|
||||
-- ## SET_ZONE_GOAL constructor
|
||||
--
|
||||
-- Create a new SET_ZONE_GOAL object with the @{#SET_ZONE_GOAL.New} method:
|
||||
--
|
||||
-- * @{#SET_ZONE_GOAL.New}: Creates a new SET_ZONE_GOAL object.
|
||||
--
|
||||
-- ## Add or Remove ZONEs from SET_ZONE_GOAL
|
||||
--
|
||||
-- ZONEs can be added and removed using the @{Core.Set#SET_ZONE_GOAL.AddZonesByName} and @{Core.Set#SET_ZONE_GOAL.RemoveZonesByName} respectively.
|
||||
-- These methods take a single ZONE name or an array of ZONE names to be added or removed from SET_ZONE_GOAL.
|
||||
--
|
||||
-- ## SET_ZONE_GOAL filter criteria
|
||||
--
|
||||
-- You can set filter criteria to build the collection of zones in SET_ZONE_GOAL.
|
||||
-- Filter criteria are defined by:
|
||||
--
|
||||
-- * @{#SET_ZONE_GOAL.FilterPrefixes}: Builds the SET_ZONE_GOAL with the zones having a certain text pattern of prefix.
|
||||
--
|
||||
-- Once the filter criteria have been set for the SET_ZONE_GOAL, you can start filtering using:
|
||||
--
|
||||
-- * @{#SET_ZONE_GOAL.FilterStart}: Starts the filtering of the zones within the SET_ZONE_GOAL.
|
||||
--
|
||||
-- ## SET_ZONE_GOAL iterators
|
||||
--
|
||||
-- Once the filters have been defined and the SET_ZONE_GOAL has been built, you can iterate the SET_ZONE_GOAL with the available iterator methods.
|
||||
-- The iterator methods will walk the SET_ZONE_GOAL set, and call for each airbase within the set a function that you provide.
|
||||
-- The following iterator methods are currently available within the SET_ZONE_GOAL:
|
||||
--
|
||||
-- * @{#SET_ZONE_GOAL.ForEachZone}: Calls a function for each zone it finds within the SET_ZONE_GOAL.
|
||||
--
|
||||
-- ===
|
||||
-- @field #SET_ZONE_GOAL SET_ZONE_GOAL
|
||||
SET_ZONE_GOAL = {
|
||||
ClassName = "SET_ZONE_GOAL",
|
||||
Zones = {},
|
||||
Filter = {
|
||||
Prefixes = nil,
|
||||
},
|
||||
FilterMeta = {
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
--- Creates a new SET_ZONE_GOAL object, building a set of zones.
|
||||
-- @param #SET_ZONE_GOAL self
|
||||
-- @return #SET_ZONE_GOAL self
|
||||
-- @usage
|
||||
-- -- Define a new SET_ZONE_GOAL Object. The DatabaseSet will contain a reference to all Zones.
|
||||
-- DatabaseSet = SET_ZONE_GOAL:New()
|
||||
function SET_ZONE_GOAL:New()
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.ZONES_GOAL ) )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add ZONEs to SET_ZONE_GOAL.
|
||||
-- @param Core.Set#SET_ZONE_GOAL self
|
||||
-- @param Core.Zone#ZONE_BASE Zone A ZONE_BASE object.
|
||||
-- @return self
|
||||
function SET_ZONE_GOAL:AddZone( Zone )
|
||||
|
||||
self:Add( Zone:GetName(), Zone )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Remove ZONEs from SET_ZONE_GOAL.
|
||||
-- @param Core.Set#SET_ZONE_GOAL self
|
||||
-- @param Core.Zone#ZONE_BASE RemoveZoneNames A single name or an array of ZONE_BASE names.
|
||||
-- @return self
|
||||
function SET_ZONE_GOAL:RemoveZonesByName( RemoveZoneNames )
|
||||
|
||||
local RemoveZoneNamesArray = ( type( RemoveZoneNames ) == "table" ) and RemoveZoneNames or { RemoveZoneNames }
|
||||
|
||||
for RemoveZoneID, RemoveZoneName in pairs( RemoveZoneNamesArray ) do
|
||||
self:Remove( RemoveZoneName )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Finds a Zone based on the Zone Name.
|
||||
-- @param #SET_ZONE_GOAL self
|
||||
-- @param #string ZoneName
|
||||
-- @return Core.Zone#ZONE_BASE The found Zone.
|
||||
function SET_ZONE_GOAL:FindZone( ZoneName )
|
||||
|
||||
local ZoneFound = self.Set[ZoneName]
|
||||
return ZoneFound
|
||||
end
|
||||
|
||||
|
||||
--- Get a random zone from the set.
|
||||
-- @param #SET_ZONE_GOAL self
|
||||
-- @return Core.Zone#ZONE_BASE The random Zone.
|
||||
-- @return #nil if no zone in the collection.
|
||||
function SET_ZONE_GOAL:GetRandomZone()
|
||||
|
||||
if self:Count() ~= 0 then
|
||||
|
||||
local Index = self.Index
|
||||
local ZoneFound = nil -- Core.Zone#ZONE_BASE
|
||||
|
||||
-- Loop until a zone has been found.
|
||||
-- The :GetZoneMaybe() call will evaluate the probability for the zone to be selected.
|
||||
-- If the zone is not selected, then nil is returned by :GetZoneMaybe() and the loop continues!
|
||||
while not ZoneFound do
|
||||
local ZoneRandom = math.random( 1, #Index )
|
||||
ZoneFound = self.Set[Index[ZoneRandom]]:GetZoneMaybe()
|
||||
end
|
||||
|
||||
return ZoneFound
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Set a zone probability.
|
||||
-- @param #SET_ZONE_GOAL self
|
||||
-- @param #string ZoneName The name of the zone.
|
||||
function SET_ZONE_GOAL:SetZoneProbability( ZoneName, ZoneProbability )
|
||||
local Zone = self:FindZone( ZoneName )
|
||||
Zone:SetZoneProbability( ZoneProbability )
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
--- Builds a set of zones of defined zone prefixes.
|
||||
-- All the zones starting with the given prefixes will be included within the set.
|
||||
-- @param #SET_ZONE_GOAL self
|
||||
-- @param #string Prefixes The prefix of which the zone name starts with.
|
||||
-- @return #SET_ZONE_GOAL self
|
||||
function SET_ZONE_GOAL:FilterPrefixes( Prefixes )
|
||||
if not self.Filter.Prefixes then
|
||||
self.Filter.Prefixes = {}
|
||||
end
|
||||
if type( Prefixes ) ~= "table" then
|
||||
Prefixes = { Prefixes }
|
||||
end
|
||||
for PrefixID, Prefix in pairs( Prefixes ) do
|
||||
self.Filter.Prefixes[Prefix] = Prefix
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Starts the filtering.
|
||||
-- @param #SET_ZONE_GOAL self
|
||||
-- @return #SET_ZONE_GOAL self
|
||||
function SET_ZONE_GOAL:FilterStart()
|
||||
|
||||
if _DATABASE then
|
||||
|
||||
-- We initialize the first set.
|
||||
for ObjectName, Object in pairs( self.Database ) do
|
||||
if self:IsIncludeObject( Object ) then
|
||||
self:Add( ObjectName, Object )
|
||||
else
|
||||
self:RemoveZonesByName( ObjectName )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self:HandleEvent( EVENTS.NewZoneGoal )
|
||||
self:HandleEvent( EVENTS.DeleteZoneGoal )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Stops the filtering for the defined collection.
|
||||
-- @param #SET_ZONE_GOAL self
|
||||
-- @return #SET_ZONE_GOAL self
|
||||
function SET_ZONE_GOAL:FilterStop()
|
||||
|
||||
self:UnHandleEvent( EVENTS.NewZoneGoal )
|
||||
self:UnHandleEvent( EVENTS.DeleteZoneGoal )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Handles the Database to check on an event (birth) that the Object was added in the Database.
|
||||
-- This is required, because sometimes the _DATABASE birth event gets called later than the SET_BASE birth event!
|
||||
-- @param #SET_ZONE_GOAL self
|
||||
-- @param Core.Event#EVENTDATA Event
|
||||
-- @return #string The name of the AIRBASE
|
||||
-- @return #table The AIRBASE
|
||||
function SET_ZONE_GOAL:AddInDatabase( Event )
|
||||
self:F3( { Event } )
|
||||
|
||||
return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName]
|
||||
end
|
||||
|
||||
--- Handles the Database to check on any event that Object exists in the Database.
|
||||
-- This is required, because sometimes the _DATABASE event gets called later than the SET_BASE event or vise versa!
|
||||
-- @param #SET_ZONE_GOAL self
|
||||
-- @param Core.Event#EVENTDATA Event
|
||||
-- @return #string The name of the AIRBASE
|
||||
-- @return #table The AIRBASE
|
||||
function SET_ZONE_GOAL:FindInDatabase( Event )
|
||||
self:F3( { Event } )
|
||||
|
||||
return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName]
|
||||
end
|
||||
|
||||
--- Iterate the SET_ZONE_GOAL and call an interator function for each ZONE, providing the ZONE and optional parameters.
|
||||
-- @param #SET_ZONE_GOAL self
|
||||
-- @param #function IteratorFunction The function that will be called when there is an alive ZONE in the SET_ZONE_GOAL. The function needs to accept a AIRBASE parameter.
|
||||
-- @return #SET_ZONE_GOAL self
|
||||
function SET_ZONE_GOAL:ForEachZone( IteratorFunction, ... )
|
||||
self:F2( arg )
|
||||
|
||||
self:ForEach( IteratorFunction, arg, self:GetSet() )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
---
|
||||
-- @param #SET_ZONE_GOAL self
|
||||
-- @param Core.Zone#ZONE_BASE MZone
|
||||
-- @return #SET_ZONE_GOAL self
|
||||
function SET_ZONE_GOAL:IsIncludeObject( MZone )
|
||||
self:F2( MZone )
|
||||
|
||||
local MZoneInclude = true
|
||||
|
||||
if MZone then
|
||||
local MZoneName = MZone:GetName()
|
||||
|
||||
if self.Filter.Prefixes then
|
||||
local MZonePrefix = false
|
||||
for ZonePrefixId, ZonePrefix in pairs( self.Filter.Prefixes ) do
|
||||
self:T3( { "Prefix:", string.find( MZoneName, ZonePrefix, 1 ), ZonePrefix } )
|
||||
if string.find( MZoneName, ZonePrefix, 1 ) then
|
||||
MZonePrefix = true
|
||||
end
|
||||
end
|
||||
self:T( { "Evaluated Prefix", MZonePrefix } )
|
||||
MZoneInclude = MZoneInclude and MZonePrefix
|
||||
end
|
||||
end
|
||||
|
||||
self:T2( MZoneInclude )
|
||||
return MZoneInclude
|
||||
end
|
||||
|
||||
--- Handles the OnEventNewZone event for the Set.
|
||||
-- @param #SET_ZONE_GOAL self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function SET_ZONE_GOAL:OnEventNewZoneGoal( EventData )
|
||||
|
||||
self:I( { "New Zone Capture Coalition", EventData } )
|
||||
self:I( { "Zone Capture Coalition", EventData.ZoneGoal } )
|
||||
|
||||
if EventData.ZoneGoal then
|
||||
if EventData.ZoneGoal and self:IsIncludeObject( EventData.ZoneGoal ) then
|
||||
self:I( { "Adding Zone Capture Coalition", EventData.ZoneGoal.ZoneName, EventData.ZoneGoal } )
|
||||
self:Add( EventData.ZoneGoal.ZoneName , EventData.ZoneGoal )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Handles the OnDead or OnCrash event for alive units set.
|
||||
-- @param #SET_ZONE_GOAL self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function SET_ZONE_GOAL:OnEventDeleteZoneGoal( EventData ) --R2.1
|
||||
self:F3( { EventData } )
|
||||
|
||||
if EventData.ZoneGoal then
|
||||
local Zone = _DATABASE:FindZone( EventData.ZoneGoal.ZoneName )
|
||||
if Zone and Zone.ZoneName then
|
||||
|
||||
-- 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_ZONE_GOALs.
|
||||
-- To prevent this from happening, the Zone object has a flag NoDestroy.
|
||||
-- When true, the SET_ZONE_GOAL won't Remove the Zone object from the set.
|
||||
-- This flag is switched off after the event handlers have been called in the EVENT class.
|
||||
self:F( { ZoneNoDestroy=Zone.NoDestroy } )
|
||||
if Zone.NoDestroy then
|
||||
else
|
||||
self:Remove( Zone.ZoneName )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Validate if a coordinate is in one of the zones in the set.
|
||||
-- Returns the ZONE object where the coordiante is located.
|
||||
-- If zones overlap, the first zone that validates the test is returned.
|
||||
-- @param #SET_ZONE_GOAL self
|
||||
-- @param Core.Point#COORDINATE Coordinate The coordinate to be searched.
|
||||
-- @return Core.Zone#ZONE_BASE The zone that validates the coordinate location.
|
||||
-- @return #nil No zone has been found.
|
||||
function SET_ZONE_GOAL:IsCoordinateInZone( Coordinate )
|
||||
|
||||
for _, Zone in pairs( self:GetSet() ) do
|
||||
local Zone = Zone -- Core.Zone#ZONE_BASE
|
||||
if Zone:IsCoordinateInZone( Coordinate ) then
|
||||
return Zone
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,14 @@
|
||||
--- **Core** -- Spawn dynamically new STATICs in your missions.
|
||||
--- **Core** - Spawn new statics in your running missions.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- SPAWNSTATIC spawns static structures in your missions dynamically. See below the SPAWNSTATIC class documentation.
|
||||
-- ## Features:
|
||||
--
|
||||
-- * Spawn new statics from a static already defined using the mission editor.
|
||||
-- * Spawn new statics from a given template.
|
||||
-- * Spawn new statics from a given type.
|
||||
-- * Spawn with a custom heading and location.
|
||||
-- * Spawn within a zone.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -189,21 +195,42 @@ function SPAWNSTATIC:SpawnFromPointVec2( PointVec2, Heading, NewName ) --R2.1
|
||||
end
|
||||
|
||||
|
||||
--- Respawns the original @{Static}.
|
||||
--- Creates a new @{Static} from a COORDINATE.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param Core.Point#COORDINATE Coordinate The 3D coordinate where to spawn the static.
|
||||
-- @param #number Heading (Optional) Heading The heading of the static, which is a number in degrees from 0 to 360. Default is 0 degrees.
|
||||
-- @param #string NewName (Optional) The name of the new static.
|
||||
-- @return #SPAWNSTATIC
|
||||
function SPAWNSTATIC:ReSpawn()
|
||||
function SPAWNSTATIC:SpawnFromCoordinate(Coordinate, Heading, NewName) --R2.4
|
||||
self:F( { PointVec2, Heading, NewName } )
|
||||
|
||||
local StaticTemplate, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate( self.SpawnTemplatePrefix )
|
||||
|
||||
if StaticTemplate then
|
||||
|
||||
|
||||
Heading=Heading or 0
|
||||
|
||||
local StaticUnitTemplate = StaticTemplate.units[1]
|
||||
|
||||
StaticUnitTemplate.x = Coordinate.x
|
||||
StaticUnitTemplate.y = Coordinate.z
|
||||
StaticUnitTemplate.alt = Coordinate.y
|
||||
|
||||
StaticTemplate.route = nil
|
||||
StaticTemplate.groupId = nil
|
||||
|
||||
StaticTemplate.name = NewName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex )
|
||||
StaticUnitTemplate.name = StaticTemplate.name
|
||||
StaticUnitTemplate.heading = ( Heading / 180 ) * math.pi
|
||||
|
||||
_DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, CategoryID, CountryID)
|
||||
|
||||
self:F({StaticTemplate = StaticTemplate})
|
||||
|
||||
local Static = coalition.addStaticObject( self.CountryID or CountryID, StaticTemplate.units[1] )
|
||||
|
||||
self.SpawnIndex = self.SpawnIndex + 1
|
||||
|
||||
return _DATABASE:FindStatic(Static:getName())
|
||||
end
|
||||
|
||||
@@ -211,6 +238,36 @@ function SPAWNSTATIC:ReSpawn()
|
||||
end
|
||||
|
||||
|
||||
--- Respawns the original @{Static}.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param #number delay Delay before respawn in seconds.
|
||||
-- @return #SPAWNSTATIC
|
||||
function SPAWNSTATIC:ReSpawn(delay)
|
||||
|
||||
if delay and delay>0 then
|
||||
self:ScheduleOnce(delay, SPAWNSTATIC.ReSpawn, self)
|
||||
else
|
||||
|
||||
local StaticTemplate, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate( self.SpawnTemplatePrefix )
|
||||
|
||||
if StaticTemplate then
|
||||
|
||||
local StaticUnitTemplate = StaticTemplate.units[1]
|
||||
StaticTemplate.route = nil
|
||||
StaticTemplate.groupId = nil
|
||||
|
||||
local Static = coalition.addStaticObject( self.CountryID or CountryID, StaticTemplate.units[1] )
|
||||
|
||||
return _DATABASE:FindStatic(Static:getName())
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
return self
|
||||
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.
|
||||
@@ -242,7 +299,7 @@ end
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param Core.Zone#ZONE_BASE Zone The Zone where to spawn the static.
|
||||
-- @param #number Heading The heading of the static, which is a number in degrees from 0 to 360.
|
||||
-- @param #string (optional) The name of the new static.
|
||||
-- @param #string NewName (optional) The name of the new static.
|
||||
-- @return #SPAWNSTATIC
|
||||
function SPAWNSTATIC:SpawnFromZone( Zone, Heading, NewName ) --R2.1
|
||||
self:F( { Zone, Heading, NewName } )
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
--- **Core** -- Management of SPOT logistics, that can be transported from and to transportation carriers.
|
||||
--- **Core** - Management of spotting logistics, that can be activated and deactivated upon command.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- SPOT implements the DCS Spot class functionality, but adds additional luxury to be able to:
|
||||
--
|
||||
-- * Spot for a defined duration.
|
||||
-- * wiggle the spot at the target.
|
||||
-- * Updates of laer spot position every 0.2 seconds for moving targets.
|
||||
-- * Wiggle the spot at the target.
|
||||
-- * Provide a @{Wrapper.Unit} as a target, instead of a point.
|
||||
-- * Implement a status machine, LaseOn, LaseOff.
|
||||
--
|
||||
@@ -49,7 +50,8 @@ do
|
||||
--- Implements the target spotting or marking functionality, but adds additional luxury to be able to:
|
||||
--
|
||||
-- * Mark targets for a defined duration.
|
||||
-- * wiggle the spot at the target.
|
||||
-- * Updates of laer spot position every 0.2 seconds for moving targets.
|
||||
-- * Wiggle the spot at the target.
|
||||
-- * Provide a @{Wrapper.Unit} as a target, instead of a point.
|
||||
-- * Implement a status machine, LaseOn, LaseOff.
|
||||
--
|
||||
@@ -85,9 +87,7 @@ do
|
||||
|
||||
--- SPOT Constructor.
|
||||
-- @param #SPOT self
|
||||
-- @param Wrapper.Unit#UNIT Recce
|
||||
-- @param #number LaserCode
|
||||
-- @param #number Duration
|
||||
-- @param Wrapper.Unit#UNIT Recce Unit that is lasing
|
||||
-- @return #SPOT
|
||||
function SPOT:New( Recce )
|
||||
|
||||
@@ -115,12 +115,50 @@ do
|
||||
--- LaseOn Trigger for SPOT
|
||||
-- @function [parent=#SPOT] LaseOn
|
||||
-- @param #SPOT self
|
||||
-- @param Wrapper.Positionable#POSITIONABLE Target
|
||||
-- @param #number LaserCode Laser code.
|
||||
-- @param #number Duration Duration of lasing in seconds.
|
||||
|
||||
--- LaseOn Asynchronous Trigger for SPOT
|
||||
-- @function [parent=#SPOT] __LaseOn
|
||||
-- @param #SPOT self
|
||||
-- @param #number Delay
|
||||
-- @param Wrapper.Positionable#POSITIONABLE Target
|
||||
-- @param #number LaserCode Laser code.
|
||||
-- @param #number Duration Duration of lasing in seconds.
|
||||
|
||||
self:AddTransition( "Off", "LaseOnCoordinate", "On" )
|
||||
|
||||
--- LaseOnCoordinate Handler OnBefore for SPOT.
|
||||
-- @function [parent=#SPOT] OnBeforeLaseOnCoordinate
|
||||
-- @param #SPOT self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- LaseOnCoordinate Handler OnAfter for SPOT.
|
||||
-- @function [parent=#SPOT] OnAfterLaseOnCoordinate
|
||||
-- @param #SPOT self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
--- LaseOnCoordinate Trigger for SPOT.
|
||||
-- @function [parent=#SPOT] LaseOnCoordinate
|
||||
-- @param #SPOT self
|
||||
-- @param Core.Point#COORDINATE Coordinate The coordinate to lase.
|
||||
-- @param #number LaserCode Laser code.
|
||||
-- @param #number Duration Duration of lasing in seconds.
|
||||
|
||||
--- LaseOn Asynchronous Trigger for SPOT
|
||||
-- @function [parent=#SPOT] __LaseOn
|
||||
-- @param #SPOT self
|
||||
-- @param #number Delay
|
||||
-- @param Wrapper.Positionable#POSITIONABLE Target
|
||||
-- @param #number LaserCode Laser code.
|
||||
-- @param #number Duration Duration of lasing in seconds.
|
||||
|
||||
|
||||
|
||||
self:AddTransition( "On", "Lasing", "On" )
|
||||
@@ -189,13 +227,14 @@ do
|
||||
return self
|
||||
end
|
||||
|
||||
--- @param #SPOT self
|
||||
--- On after LaseOn event. Activates the laser spot.
|
||||
-- @param #SPOT self
|
||||
-- @param From
|
||||
-- @param Event
|
||||
-- @param To
|
||||
-- @param Wrapper.Positionable#POSITIONABLE Target
|
||||
-- @param #number LaserCode
|
||||
-- @param #number Duration
|
||||
-- @param Wrapper.Positionable#POSITIONABLE Target Unit that is being lased.
|
||||
-- @param #number LaserCode Laser code.
|
||||
-- @param #number Duration Duration of lasing in seconds.
|
||||
function SPOT:onafterLaseOn( From, Event, To, Target, LaserCode, Duration )
|
||||
self:F( { "LaseOn", Target, LaserCode, Duration } )
|
||||
|
||||
@@ -221,6 +260,40 @@ do
|
||||
|
||||
self:__Lasing( -1 )
|
||||
end
|
||||
|
||||
|
||||
--- On after LaseOnCoordinate event. Activates the laser spot.
|
||||
-- @param #SPOT self
|
||||
-- @param From
|
||||
-- @param Event
|
||||
-- @param To
|
||||
-- @param Core.Point#COORDINATE Coordinate The coordinate at which the laser is pointing.
|
||||
-- @param #number LaserCode Laser code.
|
||||
-- @param #number Duration Duration of lasing in seconds.
|
||||
function SPOT:onafterLaseOnCoordinate(From, Event, To, Coordinate, LaserCode, Duration)
|
||||
self:F( { "LaseOnCoordinate", Coordinate, LaserCode, Duration } )
|
||||
|
||||
local function StopLase( self )
|
||||
self:LaseOff()
|
||||
end
|
||||
|
||||
self.Target = nil
|
||||
self.TargetCoord=Coordinate
|
||||
self.LaserCode = LaserCode
|
||||
|
||||
self.Lasing = true
|
||||
|
||||
local RecceDcsUnit = self.Recce:GetDCSObject()
|
||||
|
||||
self.SpotIR = Spot.createInfraRed( RecceDcsUnit, { x = 0, y = 1, z = 0 }, Coordinate:GetVec3() )
|
||||
self.SpotLaser = Spot.createLaser( RecceDcsUnit, { x = 0, y = 1, z = 0 }, Coordinate:GetVec3(), LaserCode )
|
||||
|
||||
if Duration then
|
||||
self.ScheduleID = self.LaseScheduler:Schedule( self, StopLase, {self}, Duration )
|
||||
end
|
||||
|
||||
self:__Lasing(-1)
|
||||
end
|
||||
|
||||
--- @param #SPOT self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
@@ -241,10 +314,20 @@ do
|
||||
-- @param To
|
||||
function SPOT:onafterLasing( From, Event, To )
|
||||
|
||||
if self.Target:IsAlive() then
|
||||
if self.Target and self.Target:IsAlive() then
|
||||
self.SpotIR:setPoint( self.Target:GetPointVec3():AddY(1):AddY(math.random(-100,100)/100):AddX(math.random(-100,100)/100):GetVec3() )
|
||||
self.SpotLaser:setPoint( self.Target:GetPointVec3():AddY(1):GetVec3() )
|
||||
self:__Lasing( -0.2 )
|
||||
elseif self.TargetCoord then
|
||||
|
||||
-- Wiggle the IR spot a bit.
|
||||
local irvec3={x=self.TargetCoord.x+math.random(-100,100)/100, y=self.TargetCoord.y+math.random(-100,100)/100, z=self.TargetCoord.z} --#DCS.Vec3
|
||||
local lsvec3={x=self.TargetCoord.x, y=self.TargetCoord.y, z=self.TargetCoord.z} --#DCS.Vec3
|
||||
|
||||
self.SpotIR:setPoint(irvec3)
|
||||
self.SpotLaser:setPoint(lsvec3)
|
||||
|
||||
self:__Lasing(-0.25)
|
||||
else
|
||||
self:F( { "Target is not alive", self.Target:IsAlive() } )
|
||||
end
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
--- **Core (WIP)** -- Manage user flags.
|
||||
--- **Core** - Manage user flags to interact with the mission editor trigger system and server side scripts.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- Management of DCS User Flags.
|
||||
-- ## Features:
|
||||
--
|
||||
-- * Set or get DCS user flags within running missions.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -17,18 +19,21 @@
|
||||
do -- UserFlag
|
||||
|
||||
--- @type USERFLAG
|
||||
-- @field #string ClassName Name of the class
|
||||
-- @field #string UserFlagName Name of the flag.
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
|
||||
--- Management of DCS User Flags.
|
||||
--
|
||||
-- ## USERFLAG constructor
|
||||
-- # 1. USERFLAG constructor
|
||||
--
|
||||
-- * @{#USERFLAG.New}(): Creates a new USERFLAG object.
|
||||
--
|
||||
-- @field #USERFLAG
|
||||
USERFLAG = {
|
||||
ClassName = "USERFLAG",
|
||||
ClassName = "USERFLAG",
|
||||
UserFlagName = nil,
|
||||
}
|
||||
|
||||
--- USERFLAG Constructor.
|
||||
@@ -44,20 +49,29 @@ do -- UserFlag
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the userflag name.
|
||||
-- @param #USERFLAG self
|
||||
-- @return #string Name of the user flag.
|
||||
function USERFLAG:GetName()
|
||||
return self.UserFlagName
|
||||
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.
|
||||
-- @param #number Delay Delay in seconds, before the flag is set.
|
||||
-- @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
|
||||
function USERFLAG:Set( Number, Delay ) --R2.3
|
||||
|
||||
self:F( { Number = Number } )
|
||||
|
||||
trigger.action.setUserFlag( self.UserFlagName, Number )
|
||||
if Delay and Delay>0 then
|
||||
self:ScheduleOnce(Delay, USERFLAG.Set, self, Number)
|
||||
else
|
||||
trigger.action.setUserFlag( self.UserFlagName, Number )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -70,7 +84,7 @@ do -- UserFlag
|
||||
-- local BlueVictory = USERFLAG:New( "VictoryBlue" )
|
||||
-- local BlueVictoryValue = BlueVictory:Get() -- Get the UserFlag VictoryBlue value.
|
||||
--
|
||||
function USERFLAG:Get( Number ) --R2.3
|
||||
function USERFLAG:Get() --R2.3
|
||||
|
||||
return trigger.misc.getUserFlag( self.UserFlagName )
|
||||
end
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
--- **Core (WIP)** -- Manage user sound.
|
||||
--- **Core** - Manage user sound.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
-- * Play sounds wihtin running missions.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- Management of DCS User Sound.
|
||||
--
|
||||
-- ===
|
||||
@@ -112,15 +118,21 @@ do -- UserSound
|
||||
--- Play the usersound to the given @{Wrapper.Group}.
|
||||
-- @param #USERSOUND self
|
||||
-- @param Wrapper.Group#GROUP Group The @{Wrapper.Group} to play the usersound to.
|
||||
-- @param #number Delay (Optional) Delay in seconds, before the sound is played. Default 0.
|
||||
-- @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 )
|
||||
function USERSOUND:ToGroup( Group, Delay ) --R2.3
|
||||
|
||||
Delay=Delay or 0
|
||||
if Delay>0 then
|
||||
SCHEDULER:New(nil, USERSOUND.ToGroup,{self, Group}, Delay)
|
||||
else
|
||||
trigger.action.outSoundForGroup( Group:GetID(), self.UserSoundFileName )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
--- **Core** -- VELOCITY models a speed, which can be expressed in various formats according the Settings.
|
||||
--- **Core** - Models a velocity or speed, which can be expressed in various formats according the settings.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
-- * Convert velocity in various metric systems.
|
||||
-- * Set the velocity.
|
||||
-- * Create a text in a specific format of a velocity.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions:
|
||||
--
|
||||
|
||||
@@ -1,7 +1,24 @@
|
||||
--- **Core** -- ZONE classes define **zones** within your mission of **various forms**, with **various capabilities**.
|
||||
--- **Core** - Define zones within your mission of various forms, with various capabilities.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
-- * Create radius zones.
|
||||
-- * Create trigger zones.
|
||||
-- * Create polygon zones.
|
||||
-- * Create moving zones around a unit.
|
||||
-- * Create moving zones around a group.
|
||||
-- * Provide the zone behaviour. Some zones are static, while others are moveable.
|
||||
-- * Enquiry if a coordinate is within a zone.
|
||||
-- * Smoke zones.
|
||||
-- * Set a zone probability to control zone selection.
|
||||
-- * Get zone coordinates.
|
||||
-- * Get zone properties.
|
||||
-- * Get zone bounding box.
|
||||
-- * Set/get zone name.
|
||||
--
|
||||
--
|
||||
-- There are essentially two core functions that zones accomodate:
|
||||
--
|
||||
-- * Test if an object is within the zone boundaries.
|
||||
@@ -39,7 +56,7 @@
|
||||
--- @type ZONE_BASE
|
||||
-- @field #string ZoneName Name of the zone.
|
||||
-- @field #number ZoneProbability A value between 0 and 1. 0 = 0% and 1 = 100% probability.
|
||||
-- @extends Core.Base#BASE
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
|
||||
--- This class is an abstract BASE class for derived classes, and is not meant to be instantiated.
|
||||
@@ -103,7 +120,7 @@ ZONE_BASE = {
|
||||
-- @param #string ZoneName Name of the zone.
|
||||
-- @return #ZONE_BASE self
|
||||
function ZONE_BASE:New( ZoneName )
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
local self = BASE:Inherit( self, FSM:New() )
|
||||
self:F( ZoneName )
|
||||
|
||||
self.ZoneName = ZoneName
|
||||
@@ -425,6 +442,33 @@ function ZONE_RADIUS:New( ZoneName, Vec2, Radius )
|
||||
return self
|
||||
end
|
||||
|
||||
--- Mark the zone with markers on the F10 map.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param #number Points (Optional) The amount of points in the circle. Default 360.
|
||||
-- @return #ZONE_RADIUS self
|
||||
function ZONE_RADIUS:MarkZone(Points)
|
||||
|
||||
local Point = {}
|
||||
local Vec2 = self:GetVec2()
|
||||
|
||||
Points = Points and Points or 360
|
||||
|
||||
local Angle
|
||||
local RadialBase = math.pi*2
|
||||
|
||||
for Angle = 0, 360, (360 / Points ) do
|
||||
|
||||
local Radial = Angle * RadialBase / 360
|
||||
|
||||
Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius()
|
||||
Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius()
|
||||
|
||||
COORDINATE:NewFromVec2(Point):MarkToAll(self:GetName())
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Bounds the zone with tires.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param #number Points (optional) The amount of points in the circle. Default 360.
|
||||
@@ -518,7 +562,7 @@ function ZONE_RADIUS:FlareZone( FlareColor, Points, Azimuth, AddHeight )
|
||||
local Vec2 = self:GetVec2()
|
||||
|
||||
AddHeight = AddHeight or 0
|
||||
|
||||
|
||||
Points = Points and Points or 360
|
||||
|
||||
local Angle
|
||||
@@ -601,6 +645,9 @@ 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:
|
||||
--
|
||||
@@ -611,12 +658,12 @@ end
|
||||
-- * @{ZONE_RADIUS.IsNoneInZone}(): Scan if the zone is empty.
|
||||
-- @{#ZONE_RADIUS.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param ObjectCategories
|
||||
-- @param Coalition
|
||||
-- @param ObjectCategories An array of categories of the objects to find in the zone.
|
||||
-- @param UnitCategories An array of unit categories of the objects to find in the zone.
|
||||
-- @usage
|
||||
-- self.Zone:Scan()
|
||||
-- local IsAttacked = self.Zone:IsSomeInZoneOfCoalition( self.Coalition )
|
||||
function ZONE_RADIUS:Scan( ObjectCategories )
|
||||
function ZONE_RADIUS:Scan( ObjectCategories, UnitCategories )
|
||||
|
||||
self.ScanData = {}
|
||||
self.ScanData.Coalitions = {}
|
||||
@@ -638,15 +685,48 @@ function ZONE_RADIUS:Scan( ObjectCategories )
|
||||
|
||||
local function EvaluateZone( ZoneObject )
|
||||
--if ZoneObject:isExist() then --FF: isExist always returns false for SCENERY objects since DCS 2.2 and still in DCS 2.5
|
||||
if ZoneObject then
|
||||
if ZoneObject 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 name=ZoneObject:getName()
|
||||
--env.info(string.format("Zone object %s", tostring(name)))
|
||||
--self:E(ZoneObject)
|
||||
|
||||
if ( ObjectCategory == Object.Category.UNIT and ZoneObject:isExist() and ZoneObject:isActive() ) or (ObjectCategory == Object.Category.STATIC and ZoneObject:isExist()) then
|
||||
|
||||
local CoalitionDCSUnit = ZoneObject:getCoalition()
|
||||
self.ScanData.Coalitions[CoalitionDCSUnit] = true
|
||||
self.ScanData.Units[ZoneObject] = ZoneObject
|
||||
self:F2( { Name = ZoneObject:getName(), Coalition = CoalitionDCSUnit } )
|
||||
|
||||
local Include = false
|
||||
if not UnitCategories then
|
||||
-- Anythink found is included.
|
||||
Include = true
|
||||
else
|
||||
-- Check if found object is in specified categories.
|
||||
local CategoryDCSUnit = ZoneObject:getDesc().category
|
||||
|
||||
for UnitCategoryID, UnitCategory in pairs( UnitCategories ) do
|
||||
if UnitCategory == CategoryDCSUnit then
|
||||
Include = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if Include then
|
||||
|
||||
local CoalitionDCSUnit = ZoneObject:getCoalition()
|
||||
|
||||
-- This coalition is inside the zone.
|
||||
self.ScanData.Coalitions[CoalitionDCSUnit] = true
|
||||
|
||||
self.ScanData.Units[ZoneObject] = ZoneObject
|
||||
|
||||
self:F2( { Name = ZoneObject:getName(), Coalition = CoalitionDCSUnit } )
|
||||
end
|
||||
end
|
||||
|
||||
if ObjectCategory == Object.Category.SCENERY then
|
||||
local SceneryType = ZoneObject:getTypeName()
|
||||
local SceneryName = ZoneObject:getName()
|
||||
@@ -654,21 +734,57 @@ function ZONE_RADIUS:Scan( ObjectCategories )
|
||||
self.ScanData.Scenery[SceneryType][SceneryName] = SCENERY:Register( SceneryName, ZoneObject )
|
||||
self:F2( { SCENERY = self.ScanData.Scenery[SceneryType][SceneryName] } )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- Search objects.
|
||||
world.searchObjects( ObjectCategories, SphereSearch, EvaluateZone )
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Count the number of different coalitions inside the zone.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @return #table Table of DCS units and DCS statics inside the zone.
|
||||
function ZONE_RADIUS:GetScannedUnits()
|
||||
|
||||
return self.ScanData.Units
|
||||
end
|
||||
|
||||
|
||||
--- Get a set of scanned units.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @return Core.Set#SET_UNIT Set of units and statics inside the zone.
|
||||
function ZONE_RADIUS:GetScannedSetUnit()
|
||||
|
||||
local SetUnit = SET_UNIT:New()
|
||||
|
||||
if self.ScanData then
|
||||
for ObjectID, UnitObject in pairs( self.ScanData.Units ) do
|
||||
local UnitObject = UnitObject -- DCS#Unit
|
||||
if UnitObject:isExist() then
|
||||
local FoundUnit = UNIT:FindByName( UnitObject:getName() )
|
||||
if FoundUnit then
|
||||
SetUnit:AddUnit( FoundUnit )
|
||||
else
|
||||
local FoundStatic = STATIC:FindByName( UnitObject:getName() )
|
||||
if FoundStatic then
|
||||
SetUnit:AddUnit( FoundStatic )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return SetUnit
|
||||
end
|
||||
|
||||
|
||||
--- Count the number of different coalitions inside the zone.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @return #number Counted coalitions.
|
||||
function ZONE_RADIUS:CountScannedCoalitions()
|
||||
|
||||
local Count = 0
|
||||
@@ -676,14 +792,25 @@ function ZONE_RADIUS:CountScannedCoalitions()
|
||||
for CoalitionID, Coalition in pairs( self.ScanData.Coalitions ) do
|
||||
Count = Count + 1
|
||||
end
|
||||
|
||||
return Count
|
||||
end
|
||||
|
||||
--- Check if a certain coalition is inside a scanned zone.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param #number Coalition The coalition id, e.g. coalition.side.BLUE.
|
||||
-- @return #boolean If true, the coalition is inside the zone.
|
||||
function ZONE_RADIUS:CheckScannedCoalition( Coalition )
|
||||
if Coalition then
|
||||
return self.ScanData.Coalitions[Coalition]
|
||||
end
|
||||
return nil
|
||||
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 nil if there are none to 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
|
||||
-- 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 )
|
||||
@@ -708,20 +835,27 @@ function ZONE_RADIUS:GetScannedCoalition( Coalition )
|
||||
end
|
||||
|
||||
|
||||
--- Get scanned scenery type
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @return #table Table of DCS scenery type objects.
|
||||
function ZONE_RADIUS:GetScannedSceneryType( SceneryType )
|
||||
return self.ScanData.Scenery[SceneryType]
|
||||
end
|
||||
|
||||
|
||||
--- Get scanned scenery table
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @return #table Table of DCS scenery objects.
|
||||
function ZONE_RADIUS:GetScannedScenery()
|
||||
return self.ScanData.Scenery
|
||||
end
|
||||
|
||||
|
||||
--- Is All in Zone of Coalition?
|
||||
-- Check if only the specifed coalition is inside the zone and noone else.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param Coalition
|
||||
-- @return #boolean
|
||||
-- @param #number Coalition Coalition ID of the coalition which is checked to be the only one in the zone.
|
||||
-- @return #boolean True, if **only** that coalition is inside the zone and no one else.
|
||||
-- @usage
|
||||
-- self.Zone:Scan()
|
||||
-- local IsGuarded = self.Zone:IsAllInZoneOfCoalition( self.Coalition )
|
||||
@@ -733,11 +867,12 @@ end
|
||||
|
||||
|
||||
--- Is All in Zone of Other Coalition?
|
||||
-- Check if only one coalition is inside the zone and the specified coalition is not the one.
|
||||
-- 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
|
||||
-- @param #number Coalition Coalition ID of the coalition which is not supposed to be in the zone.
|
||||
-- @return #boolean True, if and only if only one coalition is inside the zone and the specified coalition is not it.
|
||||
-- @usage
|
||||
-- self.Zone:Scan()
|
||||
-- local IsCaptured = self.Zone:IsAllInZoneOfOtherCoalition( self.Coalition )
|
||||
@@ -749,11 +884,12 @@ end
|
||||
|
||||
|
||||
--- Is Some in Zone of Coalition?
|
||||
-- Check if more than one coaltion is inside the zone and the specifed coalition is one of them.
|
||||
-- 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
|
||||
-- @param #number Coalition ID of the coaliton which is checked to be inside the zone.
|
||||
-- @return #boolean True if more than one coalition is inside the zone and the specified coalition is one of them.
|
||||
-- @usage
|
||||
-- self.Zone:Scan()
|
||||
-- local IsAttacked = self.Zone:IsSomeInZoneOfCoalition( self.Coalition )
|
||||
@@ -817,7 +953,6 @@ function ZONE_RADIUS:SearchZone( EvaluateFunction, ObjectCategories )
|
||||
|
||||
local function EvaluateZone( ZoneDCSUnit )
|
||||
|
||||
env.info( ZoneDCSUnit:getName() )
|
||||
|
||||
local ZoneUnit = UNIT:Find( ZoneDCSUnit )
|
||||
|
||||
@@ -1306,7 +1441,7 @@ end
|
||||
function ZONE_POLYGON_BASE:Flush()
|
||||
self:F2()
|
||||
|
||||
self:E( { Polygon = self.ZoneName, Coordinates = self._.Polygon } )
|
||||
self:F( { Polygon = self.ZoneName, Coordinates = self._.Polygon } )
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -1363,16 +1498,15 @@ end
|
||||
--- Smokes the zone boundaries in a color.
|
||||
-- @param #ZONE_POLYGON_BASE self
|
||||
-- @param Utilities.Utils#SMOKECOLOR SmokeColor The smoke color.
|
||||
-- @param #number Segments (Optional) Number of segments within boundary line. Default 10.
|
||||
-- @return #ZONE_POLYGON_BASE self
|
||||
function ZONE_POLYGON_BASE:SmokeZone( SmokeColor )
|
||||
function ZONE_POLYGON_BASE:SmokeZone( SmokeColor, Segments )
|
||||
self:F2( SmokeColor )
|
||||
|
||||
local i
|
||||
local j
|
||||
local Segments = 10
|
||||
Segments=Segments or 10
|
||||
|
||||
i = 1
|
||||
j = #self._.Polygon
|
||||
local i=1
|
||||
local j=#self._.Polygon
|
||||
|
||||
while i <= #self._.Polygon do
|
||||
self:T( { i, j, self._.Polygon[i], self._.Polygon[j] } )
|
||||
@@ -1393,6 +1527,42 @@ function ZONE_POLYGON_BASE:SmokeZone( SmokeColor )
|
||||
end
|
||||
|
||||
|
||||
--- Flare the zone boundaries in a color.
|
||||
-- @param #ZONE_POLYGON_BASE self
|
||||
-- @param Utilities.Utils#FLARECOLOR FlareColor The flare color.
|
||||
-- @param #number Segments (Optional) Number of segments within boundary line. Default 10.
|
||||
-- @param DCS#Azimuth Azimuth (optional) Azimuth The azimuth of the flare.
|
||||
-- @param #number AddHeight (optional) The height to be added for the smoke.
|
||||
-- @return #ZONE_POLYGON_BASE self
|
||||
function ZONE_POLYGON_BASE:FlareZone( FlareColor, Segments, Azimuth, AddHeight )
|
||||
self:F2(FlareColor)
|
||||
|
||||
Segments=Segments or 10
|
||||
|
||||
AddHeight = AddHeight or 0
|
||||
|
||||
local i=1
|
||||
local j=#self._.Polygon
|
||||
|
||||
while i <= #self._.Polygon do
|
||||
self:T( { i, j, self._.Polygon[i], self._.Polygon[j] } )
|
||||
|
||||
local DeltaX = self._.Polygon[j].x - self._.Polygon[i].x
|
||||
local DeltaY = self._.Polygon[j].y - self._.Polygon[i].y
|
||||
|
||||
for Segment = 0, Segments do -- We divide each line in 5 segments and smoke a point on the line.
|
||||
local PointX = self._.Polygon[i].x + ( Segment * DeltaX / Segments )
|
||||
local PointY = self._.Polygon[i].y + ( Segment * DeltaY / Segments )
|
||||
POINT_VEC2:New( PointX, PointY, AddHeight ):Flare(FlareColor, Azimuth)
|
||||
end
|
||||
j = i
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
--- Returns if a location is within the zone.
|
||||
|
||||
203
Moose Development/Moose/Core/Zone_Detection.lua
Normal file
203
Moose Development/Moose/Core/Zone_Detection.lua
Normal file
@@ -0,0 +1,203 @@
|
||||
|
||||
--- The ZONE_DETECTION class, defined by a zone name, a detection object and a radius.
|
||||
-- @type ZONE_DETECTION
|
||||
-- @field DCS#Vec2 Vec2 The current location of the zone.
|
||||
-- @field DCS#Distance Radius The radius of the zone.
|
||||
-- @extends #ZONE_BASE
|
||||
|
||||
--- The ZONE_DETECTION class defined by a zone name, a location and a radius.
|
||||
-- This class implements the inherited functions from Core.Zone#ZONE_BASE taking into account the own zone format and properties.
|
||||
--
|
||||
-- ## ZONE_DETECTION constructor
|
||||
--
|
||||
-- * @{#ZONE_DETECTION.New}(): Constructor.
|
||||
--
|
||||
-- @field #ZONE_DETECTION
|
||||
ZONE_DETECTION = {
|
||||
ClassName="ZONE_DETECTION",
|
||||
}
|
||||
|
||||
--- Constructor of @{#ZONE_DETECTION}, taking the zone name, the zone location and a radius.
|
||||
-- @param #ZONE_DETECTION self
|
||||
-- @param #string ZoneName Name of the zone.
|
||||
-- @param Functional.Detection#DETECTION_BASE Detection The detection object defining the locations of the central detections.
|
||||
-- @param DCS#Distance Radius The radius around the detections defining the combined zone.
|
||||
-- @return #ZONE_DETECTION self
|
||||
function ZONE_DETECTION:New( ZoneName, Detection, Radius )
|
||||
local self = BASE:Inherit( self, ZONE_BASE:New( ZoneName ) ) -- #ZONE_DETECTION
|
||||
self:F( { ZoneName, Detection, Radius } )
|
||||
|
||||
self.Detection = Detection
|
||||
self.Radius = Radius
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Bounds the zone with tires.
|
||||
-- @param #ZONE_DETECTION self
|
||||
-- @param #number Points (optional) The amount of points in the circle. Default 360.
|
||||
-- @param DCS#country.id CountryID The country id of the tire objects, e.g. country.id.USA for blue or country.id.RUSSIA for red.
|
||||
-- @param #boolean UnBound (Optional) If true the tyres will be destroyed.
|
||||
-- @return #ZONE_DETECTION self
|
||||
function ZONE_DETECTION:BoundZone( Points, CountryID, UnBound )
|
||||
|
||||
local Point = {}
|
||||
local Vec2 = self:GetVec2()
|
||||
|
||||
Points = Points and Points or 360
|
||||
|
||||
local Angle
|
||||
local RadialBase = math.pi*2
|
||||
|
||||
--
|
||||
for Angle = 0, 360, (360 / Points ) do
|
||||
local Radial = Angle * RadialBase / 360
|
||||
Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius()
|
||||
Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius()
|
||||
|
||||
local CountryName = _DATABASE.COUNTRY_NAME[CountryID]
|
||||
|
||||
local Tire = {
|
||||
["country"] = CountryName,
|
||||
["category"] = "Fortifications",
|
||||
["canCargo"] = false,
|
||||
["shape_name"] = "H-tyre_B_WF",
|
||||
["type"] = "Black_Tyre_WF",
|
||||
--["unitId"] = Angle + 10000,
|
||||
["y"] = Point.y,
|
||||
["x"] = Point.x,
|
||||
["name"] = string.format( "%s-Tire #%0d", self:GetName(), Angle ),
|
||||
["heading"] = 0,
|
||||
} -- end of ["group"]
|
||||
|
||||
local Group = coalition.addStaticObject( CountryID, Tire )
|
||||
if UnBound and UnBound == true then
|
||||
Group:destroy()
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Smokes the zone boundaries in a color.
|
||||
-- @param #ZONE_DETECTION 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_DETECTION self
|
||||
function ZONE_DETECTION: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
|
||||
|
||||
local Angle
|
||||
local RadialBase = math.pi*2
|
||||
|
||||
for Angle = 0, 360, 360 / Points do
|
||||
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, AddHeight ):Smoke( SmokeColor )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Flares the zone boundaries in a color.
|
||||
-- @param #ZONE_DETECTION self
|
||||
-- @param Utilities.Utils#FLARECOLOR FlareColor The flare color.
|
||||
-- @param #number Points (optional) The amount of points in the circle.
|
||||
-- @param DCS#Azimuth Azimuth (optional) Azimuth The azimuth of the flare.
|
||||
-- @param #number AddHeight (optional) The height to be added for the smoke.
|
||||
-- @return #ZONE_DETECTION self
|
||||
function ZONE_DETECTION: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
|
||||
local RadialBase = math.pi*2
|
||||
|
||||
for Angle = 0, 360, 360 / Points do
|
||||
local Radial = Angle * RadialBase / 360
|
||||
Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius()
|
||||
Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius()
|
||||
POINT_VEC2:New( Point.x, Point.y, AddHeight ):Flare( FlareColor, Azimuth )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Returns the radius around the detected locations defining the combine zone.
|
||||
-- @param #ZONE_DETECTION self
|
||||
-- @return DCS#Distance The radius.
|
||||
function ZONE_DETECTION:GetRadius()
|
||||
self:F2( self.ZoneName )
|
||||
|
||||
self:T2( { self.Radius } )
|
||||
|
||||
return self.Radius
|
||||
end
|
||||
|
||||
--- Sets the radius around the detected locations defining the combine zone.
|
||||
-- @param #ZONE_DETECTION self
|
||||
-- @param DCS#Distance Radius The radius.
|
||||
-- @return #ZONE_DETECTION self
|
||||
function ZONE_DETECTION:SetRadius( Radius )
|
||||
self:F2( self.ZoneName )
|
||||
|
||||
self.Radius = Radius
|
||||
self:T2( { self.Radius } )
|
||||
|
||||
return self.Radius
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Returns if a location is within the zone.
|
||||
-- @param #ZONE_DETECTION self
|
||||
-- @param DCS#Vec2 Vec2 The location to test.
|
||||
-- @return #boolean true if the location is within the zone.
|
||||
function ZONE_DETECTION:IsVec2InZone( Vec2 )
|
||||
self:F2( Vec2 )
|
||||
|
||||
local Coordinates = self.Detection:GetDetectedItemCoordinates() -- This returns a list of coordinates that define the (central) locations of the detections.
|
||||
|
||||
for CoordinateID, Coordinate in pairs( Coordinates ) do
|
||||
local ZoneVec2 = Coordinate:GetVec2()
|
||||
if ZoneVec2 then
|
||||
if (( Vec2.x - ZoneVec2.x )^2 + ( Vec2.y - ZoneVec2.y ) ^2 ) ^ 0.5 <= self:GetRadius() then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
--- Returns if a point is within the zone.
|
||||
-- @param #ZONE_DETECTION self
|
||||
-- @param DCS#Vec3 Vec3 The point to test.
|
||||
-- @return #boolean true if the point is within the zone.
|
||||
function ZONE_DETECTION:IsVec3InZone( Vec3 )
|
||||
self:F2( Vec3 )
|
||||
|
||||
local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } )
|
||||
|
||||
return InZone
|
||||
end
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
--- DCS API prototypes
|
||||
-- See [https://wiki.hoggitworld.com/view/Simulator_Scripting_Engine_Documentation](https://wiki.hoggitworld.com/view/Simulator_Scripting_Engine_Documentation)
|
||||
-- for further explanation and examples.
|
||||
--- **DCS API** Prototypes
|
||||
--
|
||||
-- See the [Simulator Scripting Engine Documentation](https://wiki.hoggitworld.com/view/Simulator_Scripting_Engine_Documentation) on Hoggit for further explanation and examples.
|
||||
--
|
||||
-- @module DCS
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
@@ -46,9 +47,13 @@ do -- world
|
||||
-- @field S_EVENT_PLAYER_COMMENT
|
||||
-- @field S_EVENT_SHOOTING_START [https://wiki.hoggitworld.com/view/DCS_event_shooting_start](https://wiki.hoggitworld.com/view/DCS_event_shooting_start)
|
||||
-- @field S_EVENT_SHOOTING_END [https://wiki.hoggitworld.com/view/DCS_event_shooting_end](https://wiki.hoggitworld.com/view/DCS_event_shooting_end)
|
||||
-- @field S_EVENT_MARK ADDED [https://wiki.hoggitworld.com/view/DCS_event_mark_added](https://wiki.hoggitworld.com/view/DCS_event_mark_added)
|
||||
-- @field S_EVENT_MARK CHANGE [https://wiki.hoggitworld.com/view/DCS_event_mark_change](https://wiki.hoggitworld.com/view/DCS_event_mark_change)
|
||||
-- @field S_EVENT_MARK REMOVE [https://wiki.hoggitworld.com/view/DCS_event_mark_remove](https://wiki.hoggitworld.com/view/DCS_event_mark_remove)
|
||||
-- @field S_EVENT_MARK ADDED [https://wiki.hoggitworld.com/view/DCS_event_mark_added](https://wiki.hoggitworld.com/view/DCS_event_mark_added) DCS>=2.5.1
|
||||
-- @field S_EVENT_MARK CHANGE [https://wiki.hoggitworld.com/view/DCS_event_mark_change](https://wiki.hoggitworld.com/view/DCS_event_mark_change) DCS>=2.5.1
|
||||
-- @field S_EVENT_MARK REMOVE [https://wiki.hoggitworld.com/view/DCS_event_mark_remove](https://wiki.hoggitworld.com/view/DCS_event_mark_remove) DCS>=2.5.1
|
||||
-- @field S_EVENT_KILL [https://wiki.hoggitworld.com/view/DCS_event_kill](https://wiki.hoggitworld.com/view/DCS_event_kill) DCS>=2.5.6
|
||||
-- @field S_EVENT_SCORE [https://wiki.hoggitworld.com/view/DCS_event_score](https://wiki.hoggitworld.com/view/DCS_event_score) DCS>=2.5.6
|
||||
-- @field S_EVENT_UNIT_LOST [https://wiki.hoggitworld.com/view/DCS_event_unit_lost](https://wiki.hoggitworld.com/view/DCS_event_unit_lost) DCS>=2.5.6
|
||||
-- @field S_EVENT_LANDING_AFTER_EJECTION [https://wiki.hoggitworld.com/view/DCS_event_landing_after_ejection](https://wiki.hoggitworld.com/view/DCS_event_landing_after_ejection) DCS>=2.5.6
|
||||
-- @field S_EVENT_MAX
|
||||
|
||||
--- The birthplace enumerator is used to define where an aircraft or helicopter has spawned in association with birth events.
|
||||
@@ -325,9 +330,27 @@ end -- coalition
|
||||
do -- Types
|
||||
|
||||
--- @type Desc
|
||||
-- @field #TypeName typeName type name
|
||||
-- @field #string displayName localized display name
|
||||
-- @field #table attributes object type attributes
|
||||
-- @field #number speedMax0 Max speed in meters/second at zero altitude.
|
||||
-- @field #number massEmpty Empty mass in kg.
|
||||
-- @field #number tankerType Type of refueling system: 0=boom, 1=probe.
|
||||
-- @field #number range Range in km(?).
|
||||
-- @field #table box Bounding box.
|
||||
-- @field #number Hmax Max height in meters.
|
||||
-- @field #number Kmax ?
|
||||
-- @field #number speedMax10K Max speed in meters/second at 10k altitude.
|
||||
-- @field #number NyMin ?
|
||||
-- @field #number NyMax ?
|
||||
-- @field #number fuelMassMax Max fuel mass in kg.
|
||||
-- @field #number speedMax10K Max speed in meters/second.
|
||||
-- @field #number massMax Max mass of unit.
|
||||
-- @field #number RCS ?
|
||||
-- @field #number life Life points.
|
||||
-- @field #number VyMax Max vertical velocity in m/s.
|
||||
-- @field #number Kab ?
|
||||
-- @field #table attributes Table of attributes.
|
||||
-- @field #TypeName typeName Type Name.
|
||||
-- @field #string displayName Localized display name.
|
||||
-- @field #number category Unit category.
|
||||
|
||||
--- A distance type
|
||||
-- @type Distance
|
||||
@@ -427,9 +450,15 @@ do -- Types
|
||||
-- @type TaskArray
|
||||
-- @list <#Task>
|
||||
|
||||
---
|
||||
--@type WaypointAir
|
||||
--@field #boolean lateActivated
|
||||
--@field #boolean uncontrolled
|
||||
|
||||
end --
|
||||
|
||||
|
||||
|
||||
do -- Object
|
||||
|
||||
--- [DCS Class Object](https://wiki.hoggitworld.com/view/DCS_Class_Object)
|
||||
@@ -527,6 +556,126 @@ do -- CoalitionObject
|
||||
end -- CoalitionObject
|
||||
|
||||
|
||||
do -- Weapon
|
||||
|
||||
--- [DCS Class Weapon](https://wiki.hoggitworld.com/view/DCS_Class_Weapon)
|
||||
-- @type Weapon
|
||||
-- @extends #CoalitionObject
|
||||
-- @field #Weapon.flag flag enum stores weapon flags. Some of them are combination of another flags.
|
||||
-- @field #Weapon.Category Category enum that stores weapon categories.
|
||||
-- @field #Weapon.GuidanceType GuidanceType enum that stores guidance methods. Available only for guided weapon (Weapon.Category.MISSILE and some Weapon.Category.BOMB).
|
||||
-- @field #Weapon.MissileCategory MissileCategory enum that stores missile category. Available only for missiles (Weapon.Category.MISSILE).
|
||||
-- @field #Weapon.WarheadType WarheadType enum that stores warhead types.
|
||||
-- @field #Weapon.Desc Desc The descriptor of a weapon.
|
||||
|
||||
--- enum stores weapon flags. Some of them are combination of another flags.
|
||||
-- @type Weapon.flag
|
||||
-- @field LGB
|
||||
-- @field TvGB
|
||||
-- @field SNSGB
|
||||
-- @field HEBomb
|
||||
-- @field Penetrator
|
||||
-- @field NapalmBomb
|
||||
-- @field FAEBomb
|
||||
-- @field ClusterBomb
|
||||
-- @field Dispencer
|
||||
-- @field CandleBomb
|
||||
-- @field ParachuteBomb
|
||||
-- @field GuidedBomb = LGB + TvGB + SNSGB
|
||||
-- @field AnyUnguidedBomb = HEBomb + Penetrator + NapalmBomb + FAEBomb + ClusterBomb + Dispencer + CandleBomb + ParachuteBomb
|
||||
-- @field AnyBomb = GuidedBomb + AnyUnguidedBomb
|
||||
-- @field LightRocket
|
||||
-- @field MarkerRocket
|
||||
-- @field CandleRocket
|
||||
-- @field HeavyRocket
|
||||
-- @field AnyRocket = LightRocket + HeavyRocket + MarkerRocket + CandleRocket
|
||||
-- @field AntiRadarMissile
|
||||
-- @field AntiShipMissile
|
||||
-- @field AntiTankMissile
|
||||
-- @field FireAndForgetASM
|
||||
-- @field LaserASM
|
||||
-- @field TeleASM
|
||||
-- @field CruiseMissile
|
||||
-- @field GuidedASM = LaserASM + TeleASM
|
||||
-- @field TacticASM = GuidedASM + FireAndForgetASM
|
||||
-- @field AnyASM = AntiRadarMissile + AntiShipMissile + AntiTankMissile + FireAndForgetASM + GuidedASM + CruiseMissile
|
||||
-- @field SRAAM
|
||||
-- @field MRAAM
|
||||
-- @field LRAAM
|
||||
-- @field IR_AAM
|
||||
-- @field SAR_AAM
|
||||
-- @field AR_AAM
|
||||
-- @field AnyAAM = IR_AAM + SAR_AAM + AR_AAM + SRAAM + MRAAM + LRAAM
|
||||
-- @field AnyMissile = AnyASM + AnyAAM
|
||||
-- @field AnyAutonomousMissile = IR_AAM + AntiRadarMissile + AntiShipMissile + FireAndForgetASM + CruiseMissile
|
||||
-- @field GUN_POD
|
||||
-- @field BuiltInCannon
|
||||
-- @field Cannons = GUN_POD + BuiltInCannon
|
||||
-- @field AnyAGWeapon = BuiltInCannon + GUN_POD + AnyBomb + AnyRocket + AnyASM
|
||||
-- @field AnyAAWeapon = BuiltInCannon + GUN_POD + AnyAAM
|
||||
-- @field UnguidedWeapon = Cannons + BuiltInCannon + GUN_POD + AnyUnguidedBomb + AnyRocket
|
||||
-- @field GuidedWeapon = GuidedBomb + AnyASM + AnyAAM
|
||||
-- @field AnyWeapon = AnyBomb + AnyRocket + AnyMissile + Cannons
|
||||
-- @field MarkerWeapon = MarkerRocket + CandleRocket + CandleBomb
|
||||
-- @field ArmWeapon = AnyWeapon - MarkerWeapon
|
||||
|
||||
--- Weapon.Category enum that stores weapon categories.
|
||||
-- @type Weapon.Category
|
||||
-- @field SHELL
|
||||
-- @field MISSILE
|
||||
-- @field ROCKET
|
||||
-- @field BOMB
|
||||
|
||||
|
||||
--- Weapon.GuidanceType enum that stores guidance methods. Available only for guided weapon (Weapon.Category.MISSILE and some Weapon.Category.BOMB).
|
||||
-- @type Weapon.GuidanceType
|
||||
-- @field INS
|
||||
-- @field IR
|
||||
-- @field RADAR_ACTIVE
|
||||
-- @field RADAR_SEMI_ACTIVE
|
||||
-- @field RADAR_PASSIVE
|
||||
-- @field TV
|
||||
-- @field LASER
|
||||
-- @field TELE
|
||||
|
||||
|
||||
--- Weapon.MissileCategory enum that stores missile category. Available only for missiles (Weapon.Category.MISSILE).
|
||||
-- @type Weapon.MissileCategory
|
||||
-- @field AAM
|
||||
-- @field SAM
|
||||
-- @field BM
|
||||
-- @field ANTI_SHIP
|
||||
-- @field CRUISE
|
||||
-- @field OTHER
|
||||
|
||||
--- Weapon.WarheadType enum that stores warhead types.
|
||||
-- @type Weapon.WarheadType
|
||||
-- @field AP
|
||||
-- @field HE
|
||||
-- @field SHAPED_EXPLOSIVE
|
||||
|
||||
--- Returns the unit that launched the weapon.
|
||||
-- @function [parent=#Weapon] getLauncher
|
||||
-- @param #Weapon self
|
||||
-- @return #Unit
|
||||
|
||||
--- returns target of the guided weapon. Unguided weapons and guided weapon that is targeted at the point on the ground will return nil.
|
||||
-- @function [parent=#Weapon] getTarget
|
||||
-- @param #Weapon self
|
||||
-- @return #Object
|
||||
|
||||
--- returns weapon descriptor. Descriptor type depends on weapon category.
|
||||
-- @function [parent=#Weapon] getDesc
|
||||
-- @param #Weapon self
|
||||
-- @return #Weapon.Desc
|
||||
|
||||
|
||||
|
||||
Weapon = {} --#Weapon
|
||||
|
||||
end -- Weapon
|
||||
|
||||
|
||||
do -- Airbase
|
||||
|
||||
--- [DCS Class Airbase](https://wiki.hoggitworld.com/view/DCS_Class_Airbase)
|
||||
@@ -1082,6 +1231,7 @@ do -- AI
|
||||
-- @field TAKEOFF
|
||||
-- @field TAKEOFF_PARKING
|
||||
-- @field TURNING_POINT
|
||||
-- @field TAKEOFF_PARKING_HOT
|
||||
-- @field LAND
|
||||
|
||||
--- @type AI.Task.TurnMethod
|
||||
@@ -1118,8 +1268,8 @@ do -- AI
|
||||
--- @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
|
||||
@@ -1128,7 +1278,34 @@ do -- AI
|
||||
-- @field FLARE_USING
|
||||
-- @field FORMATION
|
||||
-- @field RTB_ON_BINGO
|
||||
-- @field SILENCE
|
||||
-- @field SILENCE
|
||||
-- @field RTB_ON_OUT_OF_AMMO
|
||||
-- @field ECM_USING
|
||||
-- @field PROHIBIT_AA
|
||||
-- @field PROHIBIT_JETT
|
||||
-- @field PROHIBIT_AB
|
||||
-- @field PROHIBIT_AG
|
||||
-- @field MISSILE_ATTACK
|
||||
-- @field PROHIBIT_WP_PASS_REPORT
|
||||
|
||||
--- @type AI.Option.Air.id.FORMATION
|
||||
-- @field LINE_ABREAST
|
||||
-- @field TRAIL
|
||||
-- @field WEDGE
|
||||
-- @field ECHELON_RIGHT
|
||||
-- @field ECHELON_LEFT
|
||||
-- @field FINGER_FOUR
|
||||
-- @field SPREAD_FOUR
|
||||
-- @field WW2_BOMBER_ELEMENT
|
||||
-- @field WW2_BOMBER_ELEMENT_HEIGHT
|
||||
-- @field WW2_FIGHTER_VIC
|
||||
-- @field HEL_WEDGE
|
||||
-- @field HEL_ECHELON
|
||||
-- @field HEL_FRONT
|
||||
-- @field HEL_COLUMN
|
||||
-- @field COMBAT_BOX
|
||||
-- @field JAVELIN_DOWN
|
||||
|
||||
|
||||
--- @type AI.Option.Air.val
|
||||
-- @field #AI.Option.Air.val.ROE ROE
|
||||
@@ -1161,12 +1338,27 @@ do -- AI
|
||||
-- @field AGAINST_FIRED_MISSILE
|
||||
-- @field WHEN_FLYING_IN_SAM_WEZ
|
||||
-- @field WHEN_FLYING_NEAR_ENEMIES
|
||||
|
||||
--- @type AI.Option.Air.val.ECM_USING
|
||||
-- @field NEVER_USE
|
||||
-- @field USE_IF_ONLY_LOCK_BY_RADAR
|
||||
-- @field USE_IF_DETECTED_LOCK_BY_RADAR
|
||||
-- @field ALWAYS_USE
|
||||
|
||||
--- @type AI.Option.Air.val.MISSILE_ATTACK
|
||||
-- @field MAX_RANGE
|
||||
-- @field NEZ_RANGE
|
||||
-- @field HALF_WAY_RMAX_NEZ
|
||||
-- @field TARGET_THREAT_EST
|
||||
-- @field RANDOM_RANGE
|
||||
|
||||
|
||||
--- @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}
|
||||
-- @field ENGAGE_AIR_WEAPONS
|
||||
|
||||
--- @type AI.Option.Ground.val
|
||||
-- @field #AI.Option.Ground.val.ROE ROE
|
||||
@@ -1196,7 +1388,4 @@ do -- AI
|
||||
|
||||
AI = {} --#AI
|
||||
|
||||
end -- AI
|
||||
|
||||
|
||||
|
||||
end -- AI
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,54 @@
|
||||
--- **Functional** -- The CLEANUP_AIRBASE class keeps an area clean of crashing or colliding airplanes. It also prevents airplanes from firing within this area.
|
||||
--
|
||||
--- **Functional** -- Keep airbases clean of crashing or colliding airplanes, and kill missiles when being fired at airbases.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
--
|
||||
-- * Try to keep the airbase clean and operational.
|
||||
-- * Prevent airplanes from crashing.
|
||||
-- * Clean up obstructing airplanes from the runway that are standing still for a period of time.
|
||||
-- * Prevent airplanes firing missiles within the airbase zone.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Missions:
|
||||
--
|
||||
-- [CLA - CleanUp Airbase](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/CLA%20-%20CleanUp%20Airbase)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- 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.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- @module Functional.CleanUp
|
||||
-- @image CleanUp_Airbases.JPG
|
||||
|
||||
@@ -18,53 +60,29 @@
|
||||
-- @extends #CLEANUP_AIRBASE.__
|
||||
|
||||
--- 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
|
||||
--
|
||||
--
|
||||
-- # 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 )
|
||||
--
|
||||
-- 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
|
||||
--
|
||||
--
|
||||
-- # 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.
|
||||
--
|
||||
--
|
||||
-- # 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",
|
||||
@@ -88,11 +106,11 @@ CLEANUP_AIRBASE.__.Airbases = {}
|
||||
-- or
|
||||
-- CleanUpTbilisi = CLEANUP_AIRBASE:New( AIRBASE.Caucasus.Tbilisi )
|
||||
-- CleanUpKutaisi = CLEANUP_AIRBASE:New( AIRBASE.Caucasus.Kutaisi )
|
||||
function CLEANUP_AIRBASE:New( AirbaseNames )
|
||||
function CLEANUP_AIRBASE:New( AirbaseNames )
|
||||
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #CLEANUP_AIRBASE
|
||||
self:F( { AirbaseNames } )
|
||||
|
||||
|
||||
if type( AirbaseNames ) == 'table' then
|
||||
for AirbaseID, AirbaseName in pairs( AirbaseNames ) do
|
||||
self:AddAirbase( AirbaseName )
|
||||
@@ -101,9 +119,9 @@ function CLEANUP_AIRBASE:New( AirbaseNames )
|
||||
local AirbaseName = AirbaseNames
|
||||
self:AddAirbase( AirbaseName )
|
||||
end
|
||||
|
||||
|
||||
self:HandleEvent( EVENTS.Birth, self.__.OnEventBirth )
|
||||
|
||||
|
||||
self.__.CleanUpScheduler = SCHEDULER:New( self, self.__.CleanUpSchedule, {}, 1, self.TimeInterval )
|
||||
|
||||
self:HandleEvent( EVENTS.EngineShutdown , self.__.EventAddForCleanUp )
|
||||
@@ -112,7 +130,21 @@ function CLEANUP_AIRBASE:New( AirbaseNames )
|
||||
self:HandleEvent( EVENTS.PilotDead, self.__.OnEventCrash )
|
||||
self:HandleEvent( EVENTS.Dead, self.__.OnEventCrash )
|
||||
self:HandleEvent( EVENTS.Crash, self.__.OnEventCrash )
|
||||
|
||||
|
||||
for UnitName, Unit in pairs( _DATABASE.UNITS ) do
|
||||
local Unit = Unit -- Wrapper.Unit#UNIT
|
||||
if Unit:IsAlive() ~= nil then
|
||||
if self:IsInAirbase( Unit:GetVec2() ) then
|
||||
self:F( { UnitName = UnitName } )
|
||||
self.CleanUpList[UnitName] = {}
|
||||
self.CleanUpList[UnitName].CleanUpUnit = Unit
|
||||
self.CleanUpList[UnitName].CleanUpGroup = Unit:GetGroup()
|
||||
self.CleanUpList[UnitName].CleanUpGroupName = Unit:GetGroup():GetName()
|
||||
self.CleanUpList[UnitName].CleanUpUnitName = Unit:GetName()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -123,7 +155,7 @@ end
|
||||
function CLEANUP_AIRBASE:AddAirbase( AirbaseName )
|
||||
self.__.Airbases[AirbaseName] = AIRBASE:FindByName( AirbaseName )
|
||||
self:F({"Airbase:", AirbaseName, self.__.Airbases[AirbaseName]:GetDesc()})
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -165,7 +197,7 @@ function CLEANUP_AIRBASE.__:IsInAirbase( Vec2 )
|
||||
break;
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return InAirbase
|
||||
end
|
||||
|
||||
@@ -201,7 +233,7 @@ end
|
||||
-- @param DCS#Weapon MissileObject
|
||||
function CLEANUP_AIRBASE.__:DestroyMissile( MissileObject )
|
||||
self:F( { MissileObject } )
|
||||
|
||||
|
||||
if MissileObject and MissileObject:isExist() then
|
||||
MissileObject:destroy()
|
||||
self:T( "MissileObject Destroyed")
|
||||
@@ -213,11 +245,15 @@ end
|
||||
function CLEANUP_AIRBASE.__:OnEventBirth( EventData )
|
||||
self:F( { EventData } )
|
||||
|
||||
self.CleanUpList[EventData.IniDCSUnitName] = {}
|
||||
self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnit = EventData.IniUnit
|
||||
self.CleanUpList[EventData.IniDCSUnitName].CleanUpGroup = EventData.IniGroup
|
||||
self.CleanUpList[EventData.IniDCSUnitName].CleanUpGroupName = EventData.IniDCSGroupName
|
||||
self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnitName = EventData.IniDCSUnitName
|
||||
if EventData and EventData.IniUnit and EventData.IniUnit:IsAlive() ~= nil then
|
||||
if self:IsInAirbase( EventData.IniUnit:GetVec2() ) then
|
||||
self.CleanUpList[EventData.IniDCSUnitName] = {}
|
||||
self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnit = EventData.IniUnit
|
||||
self.CleanUpList[EventData.IniDCSUnitName].CleanUpGroup = EventData.IniGroup
|
||||
self.CleanUpList[EventData.IniDCSUnitName].CleanUpGroupName = EventData.IniDCSGroupName
|
||||
self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnitName = EventData.IniDCSUnitName
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -231,7 +267,7 @@ function CLEANUP_AIRBASE.__:OnEventCrash( Event )
|
||||
|
||||
--TODO: DCS BUG - This stuff is not working due to a DCS bug. Burning units cannot be destroyed.
|
||||
-- self:T("before getGroup")
|
||||
-- local _grp = Unit.getGroup(event.initiator)-- Identify the group that fired
|
||||
-- local _grp = Unit.getGroup(event.initiator)-- Identify the group that fired
|
||||
-- self:T("after getGroup")
|
||||
-- _grp:destroy()
|
||||
-- self:T("after deactivateGroup")
|
||||
@@ -244,7 +280,7 @@ function CLEANUP_AIRBASE.__:OnEventCrash( Event )
|
||||
self.CleanUpList[Event.IniDCSUnitName].CleanUpGroupName = Event.IniDCSGroupName
|
||||
self.CleanUpList[Event.IniDCSUnitName].CleanUpUnitName = Event.IniDCSUnitName
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- Detects if a unit shoots a missile.
|
||||
@@ -298,16 +334,16 @@ function CLEANUP_AIRBASE.__:AddForCleanUp( CleanUpUnit, CleanUpUnitName )
|
||||
self.CleanUpList[CleanUpUnitName] = {}
|
||||
self.CleanUpList[CleanUpUnitName].CleanUpUnit = CleanUpUnit
|
||||
self.CleanUpList[CleanUpUnitName].CleanUpUnitName = CleanUpUnitName
|
||||
|
||||
|
||||
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: ", CleanUpGroup:GetName(), CleanUpUnitName } )
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- 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.
|
||||
@@ -333,7 +369,7 @@ function CLEANUP_AIRBASE.__:EventAddForCleanUp( Event )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -344,50 +380,55 @@ function CLEANUP_AIRBASE.__:CleanUpSchedule()
|
||||
local CleanUpCount = 0
|
||||
for CleanUpUnitName, CleanUpListData in pairs( self.CleanUpList ) do
|
||||
CleanUpCount = CleanUpCount + 1
|
||||
|
||||
|
||||
local CleanUpUnit = CleanUpListData.CleanUpUnit -- Wrapper.Unit#UNIT
|
||||
local CleanUpGroupName = CleanUpListData.CleanUpGroupName
|
||||
|
||||
if CleanUpUnit:IsAlive() ~= nil then
|
||||
|
||||
if _DATABASE:GetStatusGroup( CleanUpGroupName ) ~= "ReSpawn" then
|
||||
if self:IsInAirbase( CleanUpUnit:GetVec2() ) then
|
||||
|
||||
local CleanUpCoordinate = CleanUpUnit:GetCoordinate()
|
||||
if _DATABASE:GetStatusGroup( CleanUpGroupName ) ~= "ReSpawn" then
|
||||
|
||||
self:T( { "CleanUp Scheduler", CleanUpUnitName } )
|
||||
if CleanUpUnit:GetLife() <= CleanUpUnit:GetLife0() * 0.95 then
|
||||
if CleanUpUnit:IsAboveRunway() then
|
||||
if CleanUpUnit:InAir() then
|
||||
local CleanUpCoordinate = CleanUpUnit:GetCoordinate()
|
||||
|
||||
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 )
|
||||
end
|
||||
else
|
||||
self:T( { "CleanUp Scheduler", "Destroy " .. CleanUpUnitName .. " because on runway and damaged." } )
|
||||
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: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 )
|
||||
end
|
||||
end
|
||||
else
|
||||
CleanUpListData.CleanUpTime = timer.getTime()
|
||||
CleanUpListData.CleanUpMoved = true
|
||||
end
|
||||
end
|
||||
|
||||
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 )
|
||||
end
|
||||
else
|
||||
self:T( { "CleanUp Scheduler", "Destroy " .. CleanUpUnitName .. " because on runway and damaged." } )
|
||||
self:DestroyUnit( CleanUpUnit )
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Clean Units which are waiting for a very long time in the CleanUpZone.
|
||||
if CleanUpUnit and not CleanUpUnit:GetPlayerName() 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 )
|
||||
end
|
||||
end
|
||||
else
|
||||
CleanUpListData.CleanUpTime = timer.getTime()
|
||||
CleanUpListData.CleanUpMoved = true
|
||||
end
|
||||
end
|
||||
else
|
||||
-- not anymore in an airbase zone, remove from cleanup list.
|
||||
self.CleanUpList[CleanUpUnitName] = nil
|
||||
end
|
||||
else
|
||||
-- Do nothing ...
|
||||
self.CleanUpList[CleanUpUnitName] = nil
|
||||
@@ -398,7 +439,6 @@ function CLEANUP_AIRBASE.__:CleanUpSchedule()
|
||||
end
|
||||
end
|
||||
self:T(CleanUpCount)
|
||||
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
@@ -2,32 +2,181 @@
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- Orchestrate the designation of potential targets executed by a Recce group,
|
||||
-- and communicates these to a dedicated attacking group of players,
|
||||
-- so that following a dynamically generated menu system,
|
||||
-- each detected set of potential targets can be lased or smoked...
|
||||
-- ## Features:
|
||||
--
|
||||
-- Targets can be:
|
||||
-- * Faciliate the communication of detected targets to players.
|
||||
-- * Designate targets using lasers, through a menu system.
|
||||
-- * Designate targets using smoking, through a menu system.
|
||||
-- * Designate targets using illumination, through a menu system.
|
||||
-- * Auto lase targets.
|
||||
-- * Refresh detection upon specified time intervals.
|
||||
-- * Prioritization on threat levels.
|
||||
-- * Reporting system of threats.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Missions:
|
||||
--
|
||||
-- [DES - Designation](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/DES%20-%20Designation)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- Targets detected by recce will be communicated to a group of attacking players.
|
||||
-- A menu system is made available that allows to:
|
||||
--
|
||||
-- * **Lased** for a period of time.
|
||||
-- * **Smoked**. Artillery or airplanes with Illuminatino ordonance need to be present. (WIP, but early demo ready.)
|
||||
-- * **Illuminated** through an illumination bomb. Artillery or airplanes with Illuminatino ordonance need to be present. (WIP, but early demo ready.
|
||||
--
|
||||
-- ===
|
||||
-- The following terminology is being used throughout this document:
|
||||
--
|
||||
-- # **AUTHORS and CONTRIBUTIONS**
|
||||
-- * The **DesignateObject** is the object of the DESIGNATE class, which is this class explained in the document.
|
||||
-- * The **DetectionObject** is the object of a DETECTION_ class (DETECTION_TYPES, DETECTION_AREAS, DETECTION_UNITS), which is executing the detection and grouping of Targets into _DetectionItems_.
|
||||
-- * **TargetGroups** is the list of detected target groupings by the _DetectionObject_. Each _TargetGroup_ contains a _TargetSet_.
|
||||
-- * **TargetGroup** is one element of the __TargetGroups__ list, and contains a _TargetSet_.
|
||||
-- * The **TargetSet** is a SET_UNITS collection of _Targets_, that have been detected by the _DetectionObject_.
|
||||
-- * A **Target** is a detected UNIT object by the _DetectionObject_.
|
||||
-- * A **Threat Level** is a number from 0 to 10 that is calculated based on the threat of the Target in an Air to Ground battle scenario.
|
||||
-- * The **RecceSet** is a SET_GROUP collection that contains the **RecceGroups**.
|
||||
-- * A **RecceGroup** is a GROUP object containing the **Recces**.
|
||||
-- * A **Recce** is a UNIT object executing the reconnaissance as part the _DetectionObject_. A Recce can be of any UNIT type.
|
||||
-- * An **AttackGroup** is a GROUP object that contain _Players_.
|
||||
-- * A **Player** is an active CLIENT object containing a human player.
|
||||
-- * A **Designate Menu** is the menu that is dynamically created during the designation process for each _AttackGroup_.
|
||||
--
|
||||
-- # Player Manual
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- A typical mission setup would require Recce (a @{Set} of Recce) to be detecting potential targets.
|
||||
-- The DetectionObject will group the detected targets based on the detection method being used.
|
||||
-- Possible detection methods could be by Area, by Type or by Unit.
|
||||
-- Each grouping will result in a **TargetGroup**, for terminology and clarity we will use this term throughout the document.
|
||||
--
|
||||
-- **Recce** require to have Line of Sight (LOS) towards the targets.
|
||||
-- The **Recce** will report any detected targets to the Players (on the picture Observers).
|
||||
-- When targets are detected, a menu will be made available that allows those **TargetGroups** to be designated.
|
||||
-- Designation can be done by Lasing, Smoking and Illumination.
|
||||
-- Smoking is useful during the day, while illumination is recommended to be used during the night.
|
||||
-- Smoking can designate specific targets, but not very precise, while lasing is very accurate and allows to
|
||||
-- players to attack the targets using laser guided bombs or rockets.
|
||||
-- Illumination will lighten up the Target Area.
|
||||
--
|
||||
-- **Recce** can be ground based or airborne. Airborne **Recce** (AFAC) can be really useful to designate a large amount of targets
|
||||
-- in a wide open area, as airborne **Recce** has a large LOS.
|
||||
-- However, ground based **Recce** are very useful to smoke or illuminate targets, as they can be much closer
|
||||
-- to the Target Area.
|
||||
--
|
||||
-- It is recommended to make the **Recce** invisible and immortal using the Mission Editor in DCS World.
|
||||
-- This will ensure that the detection process won't be interrupted and that targets can be designated.
|
||||
-- However, you don't have to, so to simulate a more real-word situation or simulation, **Recce can also be destroyed**!
|
||||
--
|
||||
-- ## 1. Player View (Observer)
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- The RecceSet is continuously detecting for potential Targets,
|
||||
-- executing its task as part of the DetectionObject.
|
||||
-- Once Targets have been detected, the DesignateObject will trigger the **Detect Event**.
|
||||
--
|
||||
-- In order to prevent an overflow in the DesignateObject of detected targets,
|
||||
-- there is a maximum amount of TargetGroups
|
||||
-- that can be put in **scope** of the DesignateObject.
|
||||
-- We call this the **MaximumDesignations** term.
|
||||
--
|
||||
-- ## 2. Designate Menu
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- For each detected TargetGroup, there is:
|
||||
--
|
||||
-- * A **Designate Menu** are created and continuously refreshed, containing the **DesignationID** and the **Designation Status**.
|
||||
-- * The RecceGroups are reporting to each AttackGroup, sending **Messages** containing the Threat Level and the TargetSet composition.
|
||||
--
|
||||
-- A Player can then select an action from the **Designate Menu**.
|
||||
-- The Designation Status is shown between the ( ).
|
||||
--
|
||||
-- It indicates for each TargetGroup the current active designation action applied:
|
||||
--
|
||||
-- * An "I" for Illumnation designation.
|
||||
-- * An "S" for Smoking designation.
|
||||
-- * An "L" for Lasing designation.
|
||||
--
|
||||
-- Note that multiple designation methods can be active at the same time!
|
||||
-- Note the **Auto Lase** option. When switched on, the available **Recce** will lase
|
||||
-- Targets when detected.
|
||||
--
|
||||
-- Targets are designated per **Threat Level**.
|
||||
-- The most threatening targets from an Air to Ground perspective, are designated first!
|
||||
-- This is for all designation methods.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Each Designate Menu has a sub menu structure, which allows specific actions to be triggered:
|
||||
--
|
||||
-- * Lase Targets using a specific laser code.
|
||||
-- * Smoke Targets using a specific smoke color.
|
||||
-- * Illuminate areas.
|
||||
--
|
||||
-- ## 3. Lasing Targets
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Lasing targets is done as expected. Each available Recce can lase only ONE target through!
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Lasing can be done for specific laser codes. The Su-25T requires laser code 1113, while the A-10A requires laser code 1680.
|
||||
-- For those, specific menu options can be made available for players to lase with these codes.
|
||||
-- Auto Lase (as explained above), will ensure continuous lasing of available targets.
|
||||
-- The status report shows which targets are being designated.
|
||||
--
|
||||
-- The following logic is executed when a TargetGroup is selected to be *lased* from the Designation Menu:
|
||||
--
|
||||
-- * The RecceSet is searched for any Recce that is within *designation distance* from a Target in the TargetGroup that is currently not being designated.
|
||||
-- * If there is a Recce found that is currently no designating a target, and is within designation distance from the Target, then that Target will be designated.
|
||||
-- * During designation, any Recce that does not have Line of Sight (LOS) and is not within disignation distance from the Target, will stop designating the Target, and a report is given.
|
||||
-- * When a Recce is designating a Target, and that Target is destroyed, then the Recce will stop designating the Target, and will report the event.
|
||||
-- * When a Recce is designating a Target, and that Recce is destroyed, then the Recce will be removed from the RecceSet and designation will stop without reporting.
|
||||
-- * When all RecceGroups are destroyed from the RecceSet, then the DesignationObject will stop functioning, and nothing will be reported.
|
||||
--
|
||||
-- In this way, DESIGNATE assists players to designate ground targets for a coordinated attack!
|
||||
--
|
||||
-- ## 4. Illuminating Targets
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Illumination bombs are fired between 500 and 700 meters altitude and will burn about 2 minutes, while slowly decending.
|
||||
-- Each available recce within range will fire an illumination bomb.
|
||||
-- Illumination bombs can be fired in while lasing targets.
|
||||
-- When illumination bombs are fired, it will take about 2 minutes until a sequent bomb run can be requested using the menus.
|
||||
--
|
||||
-- ## 5. Smoking Targets
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Smoke will fire for 5 minutes.
|
||||
-- Each available recce within range will smoke a target.
|
||||
-- Smoking can be requested while lasing targets.
|
||||
-- Smoke will appear "around" the targets, because of accuracy limitations.
|
||||
--
|
||||
--
|
||||
-- Have FUN!
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### 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 Functional.Designate
|
||||
-- @image Designation.JPG
|
||||
|
||||
@@ -38,161 +187,20 @@ do -- DESIGNATE
|
||||
|
||||
--- Manage the designation of detected targets.
|
||||
--
|
||||
-- Targets detected by recce will be communicated to a group of attacking players.
|
||||
-- A menu system is made available that allows to:
|
||||
--
|
||||
-- * **Lased** for a period of time.
|
||||
-- * **Smoked**. Artillery or airplanes with Illuminatino ordonance need to be present. (WIP, but early demo ready.)
|
||||
-- * **Illuminated** through an illumination bomb. Artillery or airplanes with Illuminatino ordonance need to be present. (WIP, but early demo ready.
|
||||
--
|
||||
-- The following terminology is being used throughout this document:
|
||||
--
|
||||
-- * The **DesignateObject** is the object of the DESIGNATE class, which is this class explained in the document.
|
||||
-- * The **DetectionObject** is the object of a DETECTION_ class (DETECTION_TYPES, DETECTION_AREAS, DETECTION_UNITS), which is executing the detection and grouping of Targets into _DetectionItems_.
|
||||
-- * **TargetGroups** is the list of detected target groupings by the _DetectionObject_. Each _TargetGroup_ contains a _TargetSet_.
|
||||
-- * **TargetGroup** is one element of the __TargetGroups__ list, and contains a _TargetSet_.
|
||||
-- * The **TargetSet** is a SET_UNITS collection of _Targets_, that have been detected by the _DetectionObject_.
|
||||
-- * A **Target** is a detected UNIT object by the _DetectionObject_.
|
||||
-- * A **Threat Level** is a number from 0 to 10 that is calculated based on the threat of the Target in an Air to Ground battle scenario.
|
||||
-- * The **RecceSet** is a SET_GROUP collection that contains the **RecceGroups**.
|
||||
-- * A **RecceGroup** is a GROUP object containing the **Recces**.
|
||||
-- * A **Recce** is a UNIT object executing the reconnaissance as part the _DetectionObject_. A Recce can be of any UNIT type.
|
||||
-- * An **AttackGroup** is a GROUP object that contain _Players_.
|
||||
-- * A **Player** is an active CLIENT object containing a human player.
|
||||
-- * A **Designate Menu** is the menu that is dynamically created during the designation process for each _AttackGroup_.
|
||||
--
|
||||
-- ## 0. Player Manual
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- A typical mission setup would require Recce (a @{Set} of Recce) to be detecting potential targets.
|
||||
-- The DetectionObject will group the detected targets based on the detection method being used.
|
||||
-- Possible detection methods could be by Area, by Type or by Unit.
|
||||
-- Each grouping will result in a **TargetGroup**, for terminology and clarity we will use this term throughout the document.
|
||||
--
|
||||
-- **Recce** require to have Line of Sight (LOS) towards the targets.
|
||||
-- The **Recce** will report any detected targets to the Players (on the picture Observers).
|
||||
-- When targets are detected, a menu will be made available that allows those **TargetGroups** to be designated.
|
||||
-- Designation can be done by Lasing, Smoking and Illumination.
|
||||
-- Smoking is useful during the day, while illumination is recommended to be used during the night.
|
||||
-- Smoking can designate specific targets, but not very precise, while lasing is very accurate and allows to
|
||||
-- players to attack the targets using laser guided bombs or rockets.
|
||||
-- Illumination will lighten up the Target Area.
|
||||
--
|
||||
-- **Recce** can be ground based or airborne. Airborne **Recce** (AFAC) can be really useful to designate a large amount of targets
|
||||
-- in a wide open area, as airborne **Recce** has a large LOS.
|
||||
-- However, ground based **Recce** are very useful to smoke or illuminate targets, as they can be much closer
|
||||
-- to the Target Area.
|
||||
--
|
||||
-- It is recommended to make the **Recce** invisible and immortal using the Mission Editor in DCS World.
|
||||
-- This will ensure that the detection process won't be interrupted and that targets can be designated.
|
||||
-- However, you don't have to, so to simulate a more real-word situation or simulation, **Recce can also be destroyed**!
|
||||
--
|
||||
-- ### 0.1. Player View (Observer)
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- The RecceSet is continuously detecting for potential Targets,
|
||||
-- executing its task as part of the DetectionObject.
|
||||
-- Once Targets have been detected, the DesignateObject will trigger the **Detect Event**.
|
||||
--
|
||||
-- In order to prevent an overflow in the DesignateObject of detected targets,
|
||||
-- there is a maximum amount of TargetGroups
|
||||
-- that can be put in **scope** of the DesignateObject.
|
||||
-- We call this the **MaximumDesignations** term.
|
||||
--
|
||||
-- ### 0.2. Designate Menu
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- For each detected TargetGroup, there is:
|
||||
--
|
||||
-- * A **Designate Menu** are created and continuously refreshed, containing the **DesignationID** and the **Designation Status**.
|
||||
-- * The RecceGroups are reporting to each AttackGroup, sending **Messages** containing the Threat Level and the TargetSet composition.
|
||||
--
|
||||
-- A Player can then select an action from the **Designate Menu**.
|
||||
-- The Designation Status is shown between the ( ).
|
||||
--
|
||||
-- It indicates for each TargetGroup the current active designation action applied:
|
||||
--
|
||||
-- * An "I" for Illumnation designation.
|
||||
-- * An "S" for Smoking designation.
|
||||
-- * An "L" for Lasing designation.
|
||||
--
|
||||
-- Note that multiple designation methods can be active at the same time!
|
||||
-- Note the **Auto Lase** option. When switched on, the available **Recce** will lase
|
||||
-- Targets when detected.
|
||||
--
|
||||
-- Targets are designated per **Threat Level**.
|
||||
-- The most threatening targets from an Air to Ground perspective, are designated first!
|
||||
-- This is for all designation methods.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Each Designate Menu has a sub menu structure, which allows specific actions to be triggered:
|
||||
--
|
||||
-- * Lase Targets using a specific laser code.
|
||||
-- * Smoke Targets using a specific smoke color.
|
||||
-- * Illuminate areas.
|
||||
--
|
||||
-- ### 0.3. Lasing Targets
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Lasing targets is done as expected. Each available Recce can lase only ONE target through!
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Lasing can be done for specific laser codes. The Su-25T requires laser code 1113, while the A-10A requires laser code 1680.
|
||||
-- For those, specific menu options can be made available for players to lase with these codes.
|
||||
-- Auto Lase (as explained above), will ensure continuous lasing of available targets.
|
||||
-- The status report shows which targets are being designated.
|
||||
--
|
||||
-- The following logic is executed when a TargetGroup is selected to be *lased* from the Designation Menu:
|
||||
--
|
||||
-- * The RecceSet is searched for any Recce that is within *designation distance* from a Target in the TargetGroup that is currently not being designated.
|
||||
-- * If there is a Recce found that is currently no designating a target, and is within designation distance from the Target, then that Target will be designated.
|
||||
-- * During designation, any Recce that does not have Line of Sight (LOS) and is not within disignation distance from the Target, will stop designating the Target, and a report is given.
|
||||
-- * When a Recce is designating a Target, and that Target is destroyed, then the Recce will stop designating the Target, and will report the event.
|
||||
-- * When a Recce is designating a Target, and that Recce is destroyed, then the Recce will be removed from the RecceSet and designation will stop without reporting.
|
||||
-- * When all RecceGroups are destroyed from the RecceSet, then the DesignationObject will stop functioning, and nothing will be reported.
|
||||
--
|
||||
-- In this way, DESIGNATE assists players to designate ground targets for a coordinated attack!
|
||||
--
|
||||
-- ### 0.4. Illuminating Targets
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Illumination bombs are fired between 500 and 700 meters altitude and will burn about 2 minutes, while slowly decending.
|
||||
-- Each available recce within range will fire an illumination bomb.
|
||||
-- Illumination bombs can be fired in while lasing targets.
|
||||
-- When illumination bombs are fired, it will take about 2 minutes until a sequent bomb run can be requested using the menus.
|
||||
--
|
||||
-- ### 0.5. Smoking Targets
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Smoke will fire for 5 minutes.
|
||||
-- Each available recce within range will smoke a target.
|
||||
-- Smoking can be requested while lasing targets.
|
||||
-- Smoke will appear "around" the targets, because of accuracy limitations.
|
||||
--
|
||||
--
|
||||
-- Have FUN!
|
||||
--
|
||||
-- ## 1. DESIGNATE constructor
|
||||
-- # 1. DESIGNATE constructor
|
||||
--
|
||||
-- * @{#DESIGNATE.New}(): Creates a new DESIGNATE object.
|
||||
--
|
||||
-- ## 2. DESIGNATE is a FSM
|
||||
-- # 2. DESIGNATE is a FSM
|
||||
--
|
||||
-- Designate is a finite state machine, which allows for controlled transitions of states.
|
||||
--
|
||||
-- ### 2.1 DESIGNATE States
|
||||
-- ## 2.1 DESIGNATE States
|
||||
--
|
||||
-- * **Designating** ( Group ): The designation process.
|
||||
--
|
||||
-- ### 2.2 DESIGNATE Events
|
||||
-- ## 2.2 DESIGNATE Events
|
||||
--
|
||||
-- * **@{#DESIGNATE.Detect}**: Detect targets.
|
||||
-- * **@{#DESIGNATE.LaseOn}**: Lase the targets with the specified Index.
|
||||
@@ -200,7 +208,7 @@ do -- DESIGNATE
|
||||
-- * **@{#DESIGNATE.Smoke}**: Smoke the targets with the specified Index.
|
||||
-- * **@{#DESIGNATE.Status}**: Report designation status.
|
||||
--
|
||||
-- ## 3. Maximum Designations
|
||||
-- # 3. Maximum Designations
|
||||
--
|
||||
-- In order to prevent an overflow of designations due to many Detected Targets, there is a
|
||||
-- Maximum Designations scope that is set in the DesignationObject.
|
||||
@@ -208,9 +216,9 @@ do -- DESIGNATE
|
||||
-- The method @{#DESIGNATE.SetMaximumDesignations}() will put a limit on the amount of designations put in scope of the DesignationObject.
|
||||
-- Using the menu system, the player can "forget" a designation, so that gradually a new designation can be put in scope when detected.
|
||||
--
|
||||
-- ## 4. Laser codes
|
||||
-- # 4. Laser codes
|
||||
--
|
||||
-- ### 4.1. Set possible laser codes
|
||||
-- ## 4.1. Set possible laser codes
|
||||
--
|
||||
-- An array of laser codes can be provided, that will be used by the DESIGNATE when lasing.
|
||||
-- The laser code is communicated by the Recce when it is lasing a larget.
|
||||
@@ -228,11 +236,11 @@ do -- DESIGNATE
|
||||
--
|
||||
-- The above sets a collection of possible laser codes that can be assigned. **Note the { } notation!**
|
||||
--
|
||||
-- ### 4.2. Auto generate laser codes
|
||||
-- ## 4.2. Auto generate laser codes
|
||||
--
|
||||
-- Use the method @{#DESIGNATE.GenerateLaserCodes}() to generate all possible laser codes. Logic implemented and advised by Ciribob!
|
||||
--
|
||||
-- ### 4.3. Add specific lase codes to the lase menu
|
||||
-- ## 4.3. Add specific lase codes to the lase menu
|
||||
--
|
||||
-- Certain plane types can only drop laser guided ordonnance when targets are lased with specific laser codes.
|
||||
-- The SU-25T needs targets to be lased using laser code 1113.
|
||||
@@ -241,7 +249,7 @@ do -- DESIGNATE
|
||||
-- The method @{#DESIGNATE.AddMenuLaserCode}() to allow a player to lase a target using a specific laser code.
|
||||
-- Remove such a lase menu option using @{#DESIGNATE.RemoveMenuLaserCode}().
|
||||
--
|
||||
-- ## 5. Autolase to automatically lase detected targets.
|
||||
-- # 5. Autolase to automatically lase detected targets.
|
||||
--
|
||||
-- DetectionItems can be auto lased once detected by Recces. As such, there is almost no action required from the Players using the Designate Menu.
|
||||
-- The **auto lase** function can be activated through the Designation Menu.
|
||||
@@ -252,7 +260,7 @@ do -- DESIGNATE
|
||||
--
|
||||
-- Activate the auto lasing.
|
||||
--
|
||||
-- ## 6. Target prioritization on threat level
|
||||
-- # 6. Target prioritization on threat level
|
||||
--
|
||||
-- Targets can be detected of different types in one DetectionItem. Depending on the type of the Target, a different threat level applies in an Air to Ground combat context.
|
||||
-- SAMs are of a higher threat than normal tanks. So, if the Target type was recognized, the Recces will select those targets that form the biggest threat first,
|
||||
@@ -265,12 +273,12 @@ do -- DESIGNATE
|
||||
--
|
||||
-- The example will activate the threat level prioritization for this the Designate object. Threats will be marked based on the threat level of the Target.
|
||||
--
|
||||
-- ## 6. Designate Menu Location for a Mission
|
||||
-- # 7. Designate Menu Location for a Mission
|
||||
--
|
||||
-- You can make DESIGNATE work for a @{Tasking.Mission#MISSION} object. In this way, the designate menu will not appear in the root of the radio menu, but in the menu of the Mission.
|
||||
-- Use the method @{#DESIGNATE.SetMission}() to set the @{Mission} object for the designate function.
|
||||
--
|
||||
-- ## 7. Status Report
|
||||
-- # 8. Status Report
|
||||
--
|
||||
-- A status report is available that displays the current Targets detected, grouped per DetectionItem, and a list of which Targets are currently being marked.
|
||||
--
|
||||
@@ -278,9 +286,9 @@ do -- DESIGNATE
|
||||
-- * The status report can be automatically flashed by selecting "Status" -> "Flash Status On".
|
||||
-- * The automatic flashing of the status report can be deactivated by selecting "Status" -> "Flash Status Off".
|
||||
-- * The flashing of the status menu is disabled by default.
|
||||
-- * The method @{#DESIGNATE.FlashStatusMenu}() can be used to enable or disable to flashing of the status menu.
|
||||
-- * The method @{#DESIGNATE.SetFlashStatusMenu}() can be used to enable or disable to flashing of the status menu.
|
||||
--
|
||||
-- Designate:FlashStatusMenu( true )
|
||||
-- Designate:SetFlashStatusMenu( true )
|
||||
--
|
||||
-- The example will activate the flashing of the status menu for this Designate object.
|
||||
--
|
||||
@@ -461,12 +469,12 @@ do -- DESIGNATE
|
||||
self.CC = CC
|
||||
self.Detection = Detection
|
||||
self.AttackSet = AttackSet
|
||||
self.RecceSet = Detection:GetDetectionSetGroup()
|
||||
self.RecceSet = Detection:GetDetectionSet()
|
||||
self.Recces = {}
|
||||
self.Designating = {}
|
||||
self:SetDesignateName()
|
||||
|
||||
self.LaseDuration = 60
|
||||
self:SetLaseDuration() -- Default is 120 seconds.
|
||||
|
||||
self:SetFlashStatusMenu( false )
|
||||
self:SetFlashDetectionMessages( true )
|
||||
@@ -669,6 +677,14 @@ do -- DESIGNATE
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set the lase duration for designations.
|
||||
-- @param #DESIGNATE self
|
||||
-- @param #number LaseDuration The time in seconds a lase will continue to hold on target. The default is 120 seconds.
|
||||
-- @return #DESIGNATE
|
||||
function DESIGNATE:SetLaseDuration( LaseDuration )
|
||||
self.LaseDuration = LaseDuration or 120
|
||||
return self
|
||||
end
|
||||
|
||||
--- Generate an array of possible laser codes.
|
||||
-- Each new lase will select a code from this table.
|
||||
@@ -992,9 +1008,9 @@ do -- DESIGNATE
|
||||
if string.find( Designating, "L", 1, true ) == nil then
|
||||
MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, "Search other target", DetectedMenu, self.MenuForget, self, DesignateIndex ):SetTime( MenuTime ):SetTag( self.DesignateName )
|
||||
for LaserCode, MenuText in pairs( self.MenuLaserCodes ) do
|
||||
MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, string.format( MenuText, LaserCode ), DetectedMenu, self.MenuLaseCode, self, DesignateIndex, 60, LaserCode ):SetTime( MenuTime ):SetTag( self.DesignateName )
|
||||
MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, string.format( MenuText, LaserCode ), DetectedMenu, self.MenuLaseCode, self, DesignateIndex, self.LaseDuration, LaserCode ):SetTime( MenuTime ):SetTag( self.DesignateName )
|
||||
end
|
||||
MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, "Lase with random laser code(s)", DetectedMenu, self.MenuLaseOn, self, DesignateIndex, 60 ):SetTime( MenuTime ):SetTag( self.DesignateName )
|
||||
MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, "Lase with random laser code(s)", DetectedMenu, self.MenuLaseOn, self, DesignateIndex, self.LaseDuration ):SetTime( MenuTime ):SetTag( self.DesignateName )
|
||||
else
|
||||
MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, "Stop lasing", DetectedMenu, self.MenuLaseOff, self, DesignateIndex ):SetTime( MenuTime ):SetTag( self.DesignateName )
|
||||
end
|
||||
@@ -1152,10 +1168,10 @@ do -- DESIGNATE
|
||||
|
||||
if string.find( self.Designating[Index], "L", 1, true ) == nil then
|
||||
self.Designating[Index] = self.Designating[Index] .. "L"
|
||||
self.LaseStart = timer.getTime()
|
||||
self.LaseDuration = Duration
|
||||
self:Lasing( Index, Duration, LaserCode )
|
||||
end
|
||||
self.LaseStart = timer.getTime()
|
||||
self.LaseDuration = Duration
|
||||
self:Lasing( Index, Duration, LaserCode )
|
||||
end
|
||||
|
||||
|
||||
@@ -1166,7 +1182,7 @@ do -- DESIGNATE
|
||||
|
||||
|
||||
local DetectedItem = self.Detection:GetDetectedItemByIndex( Index )
|
||||
local TargetSetUnit = self.Detection:GetDetectedSet( DetectedItem )
|
||||
local TargetSetUnit = self.Detection:GetDetectedItemSet( DetectedItem )
|
||||
|
||||
local MarkingCount = 0
|
||||
local MarkedTypes = {}
|
||||
@@ -1314,7 +1330,7 @@ do -- DESIGNATE
|
||||
local MarkedLaserCodesText = ReportLaserCodes:Text(', ')
|
||||
self.CC:GetPositionable():MessageToSetGroup( "Marking " .. MarkingCount .. " x " .. MarkedTypesText .. ", code " .. MarkedLaserCodesText .. ".", 5, self.AttackSet, self.DesignateName )
|
||||
|
||||
self:__Lasing( -30, Index, Duration, LaserCodeRequested )
|
||||
self:__Lasing( -self.LaseDuration, Index, Duration, LaserCodeRequested )
|
||||
|
||||
self:SetDesignateMenu()
|
||||
|
||||
@@ -1336,7 +1352,7 @@ do -- DESIGNATE
|
||||
end
|
||||
|
||||
local DetectedItem = self.Detection:GetDetectedItemByIndex( Index )
|
||||
local TargetSetUnit = self.Detection:GetDetectedSet( DetectedItem )
|
||||
local TargetSetUnit = self.Detection:GetDetectedItemSet( DetectedItem )
|
||||
|
||||
local Recces = self.Recces
|
||||
|
||||
@@ -1361,7 +1377,7 @@ do -- DESIGNATE
|
||||
function DESIGNATE:onafterSmoke( From, Event, To, Index, Color )
|
||||
|
||||
local DetectedItem = self.Detection:GetDetectedItemByIndex( Index )
|
||||
local TargetSetUnit = self.Detection:GetDetectedSet( DetectedItem )
|
||||
local TargetSetUnit = self.Detection:GetDetectedItemSet( DetectedItem )
|
||||
local TargetSetUnitCount = TargetSetUnit:Count()
|
||||
|
||||
local MarkedCount = 0
|
||||
@@ -1377,7 +1393,7 @@ do -- DESIGNATE
|
||||
self:F( "Smoking ..." )
|
||||
|
||||
local RecceGroup = self.RecceSet:FindNearestGroupFromPointVec2(SmokeUnit:GetPointVec2())
|
||||
local RecceUnit = RecceGroup:GetUnit( 1 )
|
||||
local RecceUnit = RecceGroup:GetUnit( 1 ) -- Wrapper.Unit#UNIT
|
||||
|
||||
if RecceUnit then
|
||||
|
||||
@@ -1406,7 +1422,7 @@ do -- DESIGNATE
|
||||
function DESIGNATE:onafterIlluminate( From, Event, To, Index )
|
||||
|
||||
local DetectedItem = self.Detection:GetDetectedItemByIndex( Index )
|
||||
local TargetSetUnit = self.Detection:GetDetectedSet( DetectedItem )
|
||||
local TargetSetUnit = self.Detection:GetDetectedItemSet( DetectedItem )
|
||||
local TargetUnit = TargetSetUnit:GetFirst()
|
||||
|
||||
if TargetUnit then
|
||||
|
||||
@@ -2,18 +2,25 @@
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
-- * Detection of targets by recce units.
|
||||
-- * Group detected targets per unit, type or area (zone).
|
||||
-- * Keep persistency of detected targets, if when detection is lost.
|
||||
-- * Provide an indication of detected targets.
|
||||
-- * Report detected targets.
|
||||
-- * Refresh detection upon specified time intervals.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Missions:
|
||||
--
|
||||
-- [DET - Detection](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/DET%20-%20Detection)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- Facilitate the detection of enemy units within the battle zone executed by FACs (Forward Air Controllers) or RECCEs (Reconnassance Units).
|
||||
-- DETECTION uses the in-built detection capabilities of DCS World, but adds new functionalities.
|
||||
--
|
||||
-- Find the DETECTION classes documentation further in this document in the globals section.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/DET%20-%20Detection)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### [YouTube Playlist](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl3Cf5jpI6BS0sBOVWK__tji)
|
||||
-- It uses the in-built detection capabilities of DCS World, but adds new functionalities.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -25,13 +32,11 @@
|
||||
--
|
||||
-- * FlightControl : Analysis, Design, Programming, Testing
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module Functional.Detection
|
||||
-- @image Detection.JPG
|
||||
|
||||
----BASE:TraceClass("DETECTION_BASE")
|
||||
----BASE:TraceClass("DETECTION_AREAS")
|
||||
----BASE:TraceClass("DETECTION_UNITS")
|
||||
----BASE:TraceClass("DETECTION_TYPES")
|
||||
|
||||
do -- DETECTION_BASE
|
||||
|
||||
@@ -286,23 +291,40 @@ do -- DETECTION_BASE
|
||||
--- @type DETECTION_BASE.DetectedItems
|
||||
-- @list <#DETECTION_BASE.DetectedItem>
|
||||
|
||||
--- @type DETECTION_BASE.DetectedItem
|
||||
--- Detected item data structrue.
|
||||
-- @type DETECTION_BASE.DetectedItem
|
||||
-- @field #boolean IsDetected Indicates if the DetectedItem has been detected or not.
|
||||
-- @field Core.Set#SET_UNIT Set
|
||||
-- @field Core.Set#SET_UNIT Set -- The Set of Units in the detected area.
|
||||
-- @field Core.Zone#ZONE_UNIT Zone -- The Zone of the detected area.
|
||||
-- @field #boolean Changed Documents if the detected area has changes.
|
||||
-- @field Core.Set#SET_UNIT Set The Set of Units in the detected area.
|
||||
-- @field Core.Zone#ZONE_UNIT Zone The Zone of the detected area.
|
||||
-- @field #boolean Changed Documents if the detected area has changed.
|
||||
-- @field #table Changes A list of the changes reported on the detected area. (It is up to the user of the detected area to consume those changes).
|
||||
-- @field #number ID -- The identifier of the detected area.
|
||||
-- @field #number ID The identifier of the detected area.
|
||||
-- @field #boolean FriendliesNearBy Indicates if there are friendlies within the detected area.
|
||||
-- @field Wrapper.Unit#UNIT NearestFAC The nearest FAC near the Area.
|
||||
-- @field Core.Point#COORDINATE Coordinate The last known coordinate of the DetectedItem.
|
||||
-- @field Core.Point#COORDINATE InterceptCoord Intercept coordiante.
|
||||
-- @field #number DistanceRecce Distance in meters of the Recce.
|
||||
-- @field #number Index Detected item key. Could also be a string.
|
||||
-- @field #string ItemID ItemPrefix .. "." .. self.DetectedItemMax.
|
||||
-- @field #boolean Locked Lock detected item.
|
||||
-- @field #table PlayersNearBy Table of nearby players.
|
||||
-- @field #table FriendliesDistance Table of distances to friendly units.
|
||||
-- @field #string TypeName Type name of the detected unit.
|
||||
-- @field #string CategoryName Catetory name of the detected unit.
|
||||
-- @field #string Name Name of the detected object.
|
||||
-- @field #boolean IsVisible If true, detected object is visible.
|
||||
-- @field #number LastTime Last time the detected item was seen.
|
||||
-- @field DCS#Vec3 LastPos Last known position of the detected item.
|
||||
-- @field DCS#Vec3 LastVelocity Last recorded 3D velocity vector of the detected item.
|
||||
-- @field #boolean KnowType Type of detected item is known.
|
||||
-- @field #boolean KnowDistance Distance to the detected item is known.
|
||||
-- @field #number Distance Distance to the detected item.
|
||||
|
||||
--- DETECTION constructor.
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role.
|
||||
-- @param Core.Set#SET_GROUP DetectionSet The @{Set} of @{Group}s that is used to detect the units.
|
||||
-- @return #DETECTION_BASE self
|
||||
function DETECTION_BASE:New( DetectionSetGroup )
|
||||
function DETECTION_BASE:New( DetectionSet )
|
||||
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, FSM:New() ) -- #DETECTION_BASE
|
||||
@@ -311,7 +333,7 @@ do -- DETECTION_BASE
|
||||
self.DetectedItemMax = 0
|
||||
self.DetectedItems = {}
|
||||
|
||||
self.DetectionSetGroup = DetectionSetGroup
|
||||
self.DetectionSet = DetectionSet
|
||||
|
||||
self.RefreshTimeInterval = 30
|
||||
|
||||
@@ -393,7 +415,7 @@ do -- DETECTION_BASE
|
||||
-- @param #string To The To State string.
|
||||
|
||||
self:AddTransition( "Detecting", "Detect", "Detecting" )
|
||||
self:AddTransition( "Detecting", "DetectionGroup", "Detecting" )
|
||||
self:AddTransition( "Detecting", "Detection", "Detecting" )
|
||||
|
||||
--- OnBefore Transition Handler for Event Detect.
|
||||
-- @function [parent=#DETECTION_BASE] OnBeforeDetect
|
||||
@@ -436,15 +458,18 @@ do -- DETECTION_BASE
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @param #table Units Table of detected units.
|
||||
|
||||
--- Synchronous Event Trigger for Event Detected.
|
||||
-- @function [parent=#DETECTION_BASE] Detected
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param #table Units Table of detected units.
|
||||
|
||||
--- Asynchronous Event Trigger for Event Detected.
|
||||
-- @function [parent=#DETECTION_BASE] __Detected
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
-- @param #table Units Table of detected units.
|
||||
|
||||
self:AddTransition( "Detecting", "DetectedItem", "Detecting" )
|
||||
|
||||
@@ -535,14 +560,40 @@ do -- DETECTION_BASE
|
||||
self.DetectedObjects[DetectionObjectName].Distance = 10000000
|
||||
|
||||
end
|
||||
for DetectionGroupID, DetectionGroupData in pairs( self.DetectionSetGroup:GetSet() ) do
|
||||
--self:F( { DetectionGroupData } )
|
||||
self:F( { DetectionGroup = DetectionGroupData:GetName() } )
|
||||
self:__DetectionGroup( DetectDelay, DetectionGroupData, DetectionTimeStamp ) -- Process each detection asynchronously.
|
||||
self.DetectionCount = self.DetectionCount + 1
|
||||
DetectDelay = DetectDelay + 1
|
||||
end
|
||||
|
||||
-- Count alive(!) groups only. Solves issue #1173 https://github.com/FlightControl-Master/MOOSE/issues/1173
|
||||
self.DetectionCount = self:CountAliveRecce()
|
||||
|
||||
local DetectionInterval = self.DetectionCount / ( self.RefreshTimeInterval - 1 )
|
||||
|
||||
self:ForEachAliveRecce(
|
||||
function( DetectionGroup )
|
||||
self:__Detection( DetectDelay, DetectionGroup, DetectionTimeStamp ) -- Process each detection asynchronously.
|
||||
DetectDelay = DetectDelay + DetectionInterval
|
||||
end
|
||||
)
|
||||
|
||||
self:__Detect( -self.RefreshTimeInterval )
|
||||
|
||||
end
|
||||
|
||||
--- @param #DETECTION_BASE self
|
||||
-- @param #number The amount of alive recce.
|
||||
function DETECTION_BASE:CountAliveRecce()
|
||||
|
||||
return self.DetectionSet:CountAlive()
|
||||
|
||||
end
|
||||
|
||||
--- @param #DETECTION_BASE self
|
||||
function DETECTION_BASE:ForEachAliveRecce( IteratorFunction, ... )
|
||||
self:F2( arg )
|
||||
|
||||
self.DetectionSet:ForEachGroupAlive( IteratorFunction, arg )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- @param #DETECTION_BASE self
|
||||
-- @param #string From The From State string.
|
||||
@@ -550,7 +601,7 @@ do -- DETECTION_BASE
|
||||
-- @param #string To The To State string.
|
||||
-- @param Wrapper.Group#GROUP DetectionGroup The Group detecting.
|
||||
-- @param #number DetectionTimeStamp Time stamp of detection event.
|
||||
function DETECTION_BASE:onafterDetectionGroup( From, Event, To, DetectionGroup, DetectionTimeStamp )
|
||||
function DETECTION_BASE:onafterDetection( From, Event, To, Detection, DetectionTimeStamp )
|
||||
|
||||
--self:F( { DetectedObjects = self.DetectedObjects } )
|
||||
|
||||
@@ -558,16 +609,16 @@ do -- DETECTION_BASE
|
||||
|
||||
local HasDetectedObjects = false
|
||||
|
||||
if DetectionGroup:IsAlive() then
|
||||
if Detection and Detection:IsAlive() then
|
||||
|
||||
--self:T( { "DetectionGroup is Alive", DetectionGroup:GetName() } )
|
||||
|
||||
local DetectionGroupName = DetectionGroup:GetName()
|
||||
local DetectionUnit = DetectionGroup:GetUnit(1)
|
||||
local DetectionGroupName = Detection:GetName()
|
||||
local DetectionUnit = Detection:GetUnit(1)
|
||||
|
||||
local DetectedUnits = {}
|
||||
|
||||
local DetectedTargets = DetectionGroup:GetDetectedTargets(
|
||||
local DetectedTargets = Detection:GetDetectedTargets(
|
||||
self.DetectVisual,
|
||||
self.DetectOptical,
|
||||
self.DetectRadar,
|
||||
@@ -619,7 +670,7 @@ do -- DETECTION_BASE
|
||||
|
||||
local DetectedObjectVec3 = DetectedObject:getPoint()
|
||||
local DetectedObjectVec2 = { x = DetectedObjectVec3.x, y = DetectedObjectVec3.z }
|
||||
local DetectionGroupVec3 = DetectionGroup:GetVec3()
|
||||
local DetectionGroupVec3 = Detection:GetVec3()
|
||||
local DetectionGroupVec2 = { x = DetectionGroupVec3.x, y = DetectionGroupVec3.z }
|
||||
|
||||
local Distance = ( ( DetectedObjectVec3.x - DetectionGroupVec3.x )^2 +
|
||||
@@ -783,23 +834,27 @@ do -- DETECTION_BASE
|
||||
-- IsDetected = false!
|
||||
-- This is used in A2A_TASK_DISPATCHER to initiate fighter sweeping! The TASK_A2A_INTERCEPT tasks will be replaced with TASK_A2A_SWEEP tasks.
|
||||
for DetectedObjectName, DetectedObject in pairs( self.DetectedObjects ) do
|
||||
if self.DetectedObjects[DetectedObjectName].IsDetected == true and self.DetectedObjects[DetectedObjectName].DetectionTimeStamp + 60 <= DetectionTimeStamp then
|
||||
if self.DetectedObjects[DetectedObjectName].IsDetected == true and self.DetectedObjects[DetectedObjectName].DetectionTimeStamp + 300 <= DetectionTimeStamp then
|
||||
self.DetectedObjects[DetectedObjectName].IsDetected = false
|
||||
end
|
||||
end
|
||||
|
||||
self:CreateDetectionItems() -- Polymorphic call to Create/Update the DetectionItems list for the DETECTION_ class grouping method.
|
||||
|
||||
for DetectedItemID, DetectedItem in pairs( self.DetectedItems ) do
|
||||
|
||||
self:UpdateDetectedItemDetection( DetectedItem )
|
||||
|
||||
self:CleanDetectionItem( DetectedItem, DetectedItemID ) -- Any DetectionItem that has a Set with zero elements in it, must be removed from the DetectionItems list.
|
||||
|
||||
if DetectedItem then
|
||||
self:__DetectedItem( 0.1, DetectedItem )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
self:__Detect( self.RefreshTimeInterval )
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -807,7 +862,7 @@ do -- DETECTION_BASE
|
||||
|
||||
do -- DetectionItems Creation
|
||||
|
||||
-- Clean the DetectedItem table.
|
||||
--- Clean the DetectedItem table.
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @return #DETECTION_BASE
|
||||
function DETECTION_BASE:CleanDetectionItem( DetectedItem, DetectedItemID )
|
||||
@@ -833,7 +888,7 @@ do -- DETECTION_BASE
|
||||
local DetectedItems = self:GetDetectedItems()
|
||||
|
||||
for DetectedItemIndex, DetectedItem in pairs( DetectedItems ) do
|
||||
local DetectedSet = self:GetDetectedSet( DetectedItem )
|
||||
local DetectedSet = self:GetDetectedItemSet( DetectedItem )
|
||||
if DetectedSet then
|
||||
DetectedSet:RemoveUnitsByName( UnitName )
|
||||
end
|
||||
@@ -1007,7 +1062,7 @@ do -- DETECTION_BASE
|
||||
--- Set the parameters to calculate to optimal intercept point.
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param #boolean Intercept Intercept is true if an intercept point is calculated. Intercept is false if it is disabled. The default Intercept is false.
|
||||
-- @param #number IntereptDelay If Intercept is true, then InterceptDelay is the average time it takes to get airplanes airborne.
|
||||
-- @param #number InterceptDelay If Intercept is true, then InterceptDelay is the average time it takes to get airplanes airborne.
|
||||
-- @return #DETECTION_BASE self
|
||||
function DETECTION_BASE:SetIntercept( Intercept, InterceptDelay )
|
||||
self:F2()
|
||||
@@ -1224,17 +1279,17 @@ do -- DETECTION_BASE
|
||||
|
||||
--- Returns if there are friendlies nearby the FAC units ...
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param DetectedItem
|
||||
-- @param #DETECTION_BASE.DetectedItem DetectedItem
|
||||
-- @param DCS#Unit.Category Category The category of the unit.
|
||||
-- @return #boolean true if there are friendlies nearby
|
||||
function DETECTION_BASE:IsFriendliesNearBy( DetectedItem, Category )
|
||||
--self:F( { "FriendliesNearBy Test", DetectedItem.FriendliesNearBy } )
|
||||
-- self:F( { "FriendliesNearBy Test", DetectedItem.FriendliesNearBy } )
|
||||
return ( DetectedItem.FriendliesNearBy and DetectedItem.FriendliesNearBy[Category] ~= nil ) or false
|
||||
end
|
||||
|
||||
--- Returns friendly units nearby the FAC units ...
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param DetectedItem
|
||||
-- @param #DETECTION_BASE.DetectedItem DetectedItem
|
||||
-- @param DCS#Unit.Category Category The category of the unit.
|
||||
-- @return #map<#string,Wrapper.Unit#UNIT> The map of Friendly UNITs.
|
||||
function DETECTION_BASE:GetFriendliesNearBy( DetectedItem, Category )
|
||||
@@ -1244,6 +1299,7 @@ do -- DETECTION_BASE
|
||||
|
||||
--- Returns if there are friendlies nearby the intercept ...
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param #DETECTION_BASE.DetectedItem DetectedItem
|
||||
-- @return #boolean trhe if there are friendlies near the intercept.
|
||||
function DETECTION_BASE:IsFriendliesNearIntercept( DetectedItem )
|
||||
|
||||
@@ -1252,6 +1308,7 @@ do -- DETECTION_BASE
|
||||
|
||||
--- Returns friendly units nearby the intercept point ...
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param #DETECTION_BASE.DetectedItem DetectedItem The detected item.
|
||||
-- @return #map<#string,Wrapper.Unit#UNIT> The map of Friendly UNITs.
|
||||
function DETECTION_BASE:GetFriendliesNearIntercept( DetectedItem )
|
||||
|
||||
@@ -1260,7 +1317,8 @@ do -- DETECTION_BASE
|
||||
|
||||
--- Returns the distance used to identify friendlies near the deteted item ...
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @return #number The distance.
|
||||
-- @param #DETECTION_BASE.DetectedItem DetectedItem The detected item.
|
||||
-- @return #table A table of distances to friendlies.
|
||||
function DETECTION_BASE:GetFriendliesDistance( DetectedItem )
|
||||
|
||||
return DetectedItem.FriendliesDistance
|
||||
@@ -1268,6 +1326,7 @@ do -- DETECTION_BASE
|
||||
|
||||
--- Returns if there are friendlies nearby the FAC units ...
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param #DETECTION_BASE.DetectedItem DetectedItem
|
||||
-- @return #boolean trhe if there are friendlies nearby
|
||||
function DETECTION_BASE:IsPlayersNearBy( DetectedItem )
|
||||
|
||||
@@ -1276,6 +1335,7 @@ do -- DETECTION_BASE
|
||||
|
||||
--- Returns friendly units nearby the FAC units ...
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param #DETECTION_BASE.DetectedItem DetectedItem The detected item.
|
||||
-- @return #map<#string,Wrapper.Unit#UNIT> The map of Friendly UNITs.
|
||||
function DETECTION_BASE:GetPlayersNearBy( DetectedItem )
|
||||
|
||||
@@ -1284,10 +1344,11 @@ do -- DETECTION_BASE
|
||||
|
||||
--- Background worker function to determine if there are friendlies nearby ...
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param #table TargetData
|
||||
function DETECTION_BASE:ReportFriendliesNearBy( TargetData )
|
||||
--self:F( { "Search Friendlies", DetectedItem = TargetData.DetectedItem } )
|
||||
|
||||
local DetectedItem = TargetData.DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem
|
||||
local DetectedItem = TargetData.DetectedItem --#DETECTION_BASE.DetectedItem
|
||||
local DetectedSet = TargetData.DetectedItem.Set
|
||||
local DetectedUnit = DetectedSet:GetFirst() -- Wrapper.Unit#UNIT
|
||||
|
||||
@@ -1371,33 +1432,37 @@ do -- DETECTION_BASE
|
||||
world.searchObjects( Object.Category.UNIT, SphereSearch, FindNearByFriendlies, TargetData )
|
||||
|
||||
DetectedItem.PlayersNearBy = nil
|
||||
local DetectionZone = ZONE_UNIT:New( "DetectionPlayers", DetectedUnit, self.FriendliesRange )
|
||||
|
||||
_DATABASE:ForEachPlayer(
|
||||
--- @param Wrapper.Unit#UNIT PlayerUnit
|
||||
function( PlayerUnitName )
|
||||
local PlayerUnit = UNIT:FindByName( PlayerUnitName )
|
||||
|
||||
if PlayerUnit and PlayerUnit:IsInZone(DetectionZone) then
|
||||
-- Fix for issue https://github.com/FlightControl-Master/MOOSE/issues/1225
|
||||
if PlayerUnit and PlayerUnit:IsAlive() then
|
||||
local coord=PlayerUnit:GetCoordinate()
|
||||
|
||||
if coord and coord:IsInRadius( DetectedUnitCoord, self.FriendliesRange ) then
|
||||
|
||||
local PlayerUnitCategory = PlayerUnit:GetDesc().category
|
||||
|
||||
if ( not self.FriendliesCategory ) or ( self.FriendliesCategory and ( self.FriendliesCategory == PlayerUnitCategory ) ) then
|
||||
|
||||
local PlayerUnitName = PlayerUnit:GetName()
|
||||
local PlayerUnitCategory = PlayerUnit:GetDesc().category
|
||||
|
||||
DetectedItem.PlayersNearBy = DetectedItem.PlayersNearBy or {}
|
||||
DetectedItem.PlayersNearBy[PlayerUnitName] = PlayerUnit
|
||||
|
||||
-- Friendlies are sorted per unit category.
|
||||
DetectedItem.FriendliesNearBy = DetectedItem.FriendliesNearBy or {}
|
||||
DetectedItem.FriendliesNearBy[PlayerUnitCategory] = DetectedItem.FriendliesNearBy[PlayerUnitCategory] or {}
|
||||
DetectedItem.FriendliesNearBy[PlayerUnitCategory][PlayerUnitName] = PlayerUnit
|
||||
|
||||
local Distance = DetectedUnitCoord:Get2DDistance( PlayerUnit:GetCoordinate() )
|
||||
DetectedItem.FriendliesDistance = DetectedItem.FriendliesDistance or {}
|
||||
DetectedItem.FriendliesDistance[Distance] = PlayerUnit
|
||||
|
||||
if ( not self.FriendliesCategory ) or ( self.FriendliesCategory and ( self.FriendliesCategory == PlayerUnitCategory ) ) then
|
||||
|
||||
local PlayerUnitName = PlayerUnit:GetName()
|
||||
|
||||
DetectedItem.PlayersNearBy = DetectedItem.PlayersNearBy or {}
|
||||
DetectedItem.PlayersNearBy[PlayerUnitName] = PlayerUnit
|
||||
|
||||
-- Friendlies are sorted per unit category.
|
||||
DetectedItem.FriendliesNearBy = DetectedItem.FriendliesNearBy or {}
|
||||
DetectedItem.FriendliesNearBy[PlayerUnitCategory] = DetectedItem.FriendliesNearBy[PlayerUnitCategory] or {}
|
||||
DetectedItem.FriendliesNearBy[PlayerUnitCategory][PlayerUnitName] = PlayerUnit
|
||||
|
||||
local Distance = DetectedUnitCoord:Get2DDistance( PlayerUnit:GetCoordinate() )
|
||||
DetectedItem.FriendliesDistance = DetectedItem.FriendliesDistance or {}
|
||||
DetectedItem.FriendliesDistance[Distance] = PlayerUnit
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1509,31 +1574,31 @@ do -- DETECTION_BASE
|
||||
--- Adds a new DetectedItem to the DetectedItems list.
|
||||
-- The DetectedItem is a table and contains a SET_UNIT in the field Set.
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param ItemPrefix
|
||||
-- @param DetectedItemKey The key of the DetectedItem.
|
||||
-- @param #string ItemPrefix Prefix of detected item.
|
||||
-- @param #number DetectedItemKey The key of the DetectedItem. Default self.DetectedItemMax. Could also be a string in principle.
|
||||
-- @param Core.Set#SET_UNIT Set (optional) The Set of Units to be added.
|
||||
-- @return #DETECTION_BASE.DetectedItem
|
||||
function DETECTION_BASE:AddDetectedItem( ItemPrefix, DetectedItemKey, Set )
|
||||
|
||||
local DetectedItem = {}
|
||||
local DetectedItem = {} --#DETECTION_BASE.DetectedItem
|
||||
self.DetectedItemCount = self.DetectedItemCount + 1
|
||||
self.DetectedItemMax = self.DetectedItemMax + 1
|
||||
|
||||
if DetectedItemKey then
|
||||
self.DetectedItems[DetectedItemKey] = DetectedItem
|
||||
else
|
||||
self.DetectedItems[self.DetectedItemMax] = DetectedItem
|
||||
end
|
||||
|
||||
self.DetectedItemsByIndex[self.DetectedItemMax] = DetectedItem
|
||||
|
||||
DetectedItemKey = DetectedItemKey or self.DetectedItemMax
|
||||
self.DetectedItems[DetectedItemKey] = DetectedItem
|
||||
self.DetectedItemsByIndex[DetectedItemKey] = DetectedItem
|
||||
DetectedItem.Index = DetectedItemKey
|
||||
|
||||
DetectedItem.Set = Set or SET_UNIT:New():FilterDeads():FilterCrashes()
|
||||
DetectedItem.Index = DetectedItemKey or self.DetectedItemMax
|
||||
DetectedItem.ItemID = ItemPrefix .. "." .. self.DetectedItemMax
|
||||
DetectedItem.ID = self.DetectedItemMax
|
||||
DetectedItem.Removed = false
|
||||
|
||||
if self.Locking then
|
||||
self:LockDetectedItem( DetectedItem )
|
||||
end
|
||||
|
||||
return DetectedItem
|
||||
end
|
||||
|
||||
@@ -1544,9 +1609,11 @@ do -- DETECTION_BASE
|
||||
-- @param Core.Set#SET_UNIT Set (optional) The Set of Units to be added.
|
||||
-- @param Core.Zone#ZONE_UNIT Zone (optional) The Zone to be added where the Units are located.
|
||||
-- @return #DETECTION_BASE.DetectedItem
|
||||
function DETECTION_BASE:AddDetectedItemZone( DetectedItemKey, Set, Zone )
|
||||
function DETECTION_BASE:AddDetectedItemZone( ItemPrefix, DetectedItemKey, Set, Zone )
|
||||
|
||||
local DetectedItem = self:AddDetectedItem( "AREA", DetectedItemKey, Set )
|
||||
self:F( { ItemPrefix, DetectedItemKey, Set, Zone } )
|
||||
|
||||
local DetectedItem = self:AddDetectedItem( ItemPrefix, DetectedItemKey, Set )
|
||||
|
||||
DetectedItem.Zone = Zone
|
||||
|
||||
@@ -1619,7 +1686,7 @@ do -- DETECTION_BASE
|
||||
-- @return #DETECTION_BASE.DetectedItem
|
||||
function DETECTION_BASE:GetDetectedItemByIndex( Index )
|
||||
|
||||
self:F( { DetectedItemsByIndex = self.DetectedItemsByIndex } )
|
||||
self:F( { self.DetectedItemsByIndex } )
|
||||
|
||||
local DetectedItem = self.DetectedItemsByIndex[Index]
|
||||
if DetectedItem then
|
||||
@@ -1656,7 +1723,7 @@ do -- DETECTION_BASE
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param #DETECTION_BASE.DetectedItem DetectedItem
|
||||
-- @return Core.Set#SET_UNIT DetectedSet
|
||||
function DETECTION_BASE:GetDetectedSet( DetectedItem )
|
||||
function DETECTION_BASE:GetDetectedItemSet( DetectedItem )
|
||||
|
||||
local DetectedSetUnit = DetectedItem and DetectedItem.Set
|
||||
if DetectedSetUnit then
|
||||
@@ -1692,6 +1759,7 @@ do -- DETECTION_BASE
|
||||
|
||||
--- Checks if there is at least one UNIT detected in the Set of the the DetectedItem.
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param #DETECTION_BASE.DetectedItem DetectedItem
|
||||
-- @return #boolean true if at least one UNIT is detected from the DetectedSet, false if no UNIT was detected from the DetectedSet.
|
||||
function DETECTION_BASE:IsDetectedItemDetected( DetectedItem )
|
||||
|
||||
@@ -1719,6 +1787,68 @@ do -- DETECTION_BASE
|
||||
|
||||
end
|
||||
|
||||
--- Lock the detected items when created and lock all existing detected items.
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @return #DETECTION_BASE
|
||||
function DETECTION_BASE:LockDetectedItems()
|
||||
|
||||
for DetectedItemID, DetectedItem in pairs( self.DetectedItems ) do
|
||||
self:LockDetectedItem( DetectedItem )
|
||||
end
|
||||
self.Locking = true
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Unlock the detected items when created and unlock all existing detected items.
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @return #DETECTION_BASE
|
||||
function DETECTION_BASE:UnlockDetectedItems()
|
||||
|
||||
for DetectedItemID, DetectedItem in pairs( self.DetectedItems ) do
|
||||
self:UnlockDetectedItem( DetectedItem )
|
||||
end
|
||||
self.Locking = nil
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Validate if the detected item is locked.
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem.
|
||||
-- @return #boolean
|
||||
function DETECTION_BASE:IsDetectedItemLocked( DetectedItem )
|
||||
|
||||
return self.Locking and DetectedItem.Locked == true
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Lock a detected item.
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem.
|
||||
-- @return #DETECTION_BASE
|
||||
function DETECTION_BASE:LockDetectedItem( DetectedItem )
|
||||
|
||||
DetectedItem.Locked = true
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Unlock a detected item.
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem.
|
||||
-- @return #DETECTION_BASE
|
||||
function DETECTION_BASE:UnlockDetectedItem( DetectedItem )
|
||||
|
||||
DetectedItem.Locked = nil
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
--- Set the detected item coordinate.
|
||||
-- @param #DETECTION_BASE self
|
||||
@@ -1754,6 +1884,20 @@ do -- DETECTION_BASE
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Get a list of the detected item coordinates.
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @return #table A table of Core.Point#COORDINATE
|
||||
function DETECTION_BASE:GetDetectedItemCoordinates()
|
||||
|
||||
local Coordinates = {}
|
||||
|
||||
for DetectedItemID, DetectedItem in pairs( self:GetDetectedItems() ) do
|
||||
Coordinates[DetectedItem] = self:GetDetectedItemCoordinate( DetectedItem )
|
||||
end
|
||||
|
||||
return Coordinates
|
||||
end
|
||||
|
||||
--- Set the detected item threatlevel.
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param #DETECTION_BASE.DetectedItem The DetectedItem to calculate the threatlevel for.
|
||||
@@ -1805,13 +1949,13 @@ do -- DETECTION_BASE
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Get the detection Groups.
|
||||
--- Get the Detection Set.
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @return Core.Set#SET_GROUP
|
||||
function DETECTION_BASE:GetDetectionSetGroup()
|
||||
-- @return #DETECTION_BASE self
|
||||
function DETECTION_BASE:GetDetectionSet()
|
||||
|
||||
local DetectionSetGroup = self.DetectionSetGroup
|
||||
return DetectionSetGroup
|
||||
local DetectionSet = self.DetectionSet
|
||||
return DetectionSet
|
||||
end
|
||||
|
||||
--- Find the nearest Recce of the DetectedItem.
|
||||
@@ -1823,7 +1967,7 @@ do -- DETECTION_BASE
|
||||
local NearestRecce = nil
|
||||
local DistanceRecce = 1000000000 -- Units are not further than 1000000 km away from an area :-)
|
||||
|
||||
for RecceGroupName, RecceGroup in pairs( self.DetectionSetGroup:GetSet() ) do
|
||||
for RecceGroupName, RecceGroup in pairs( self.DetectionSet:GetSet() ) do
|
||||
if RecceGroup and RecceGroup:IsAlive() then
|
||||
for RecceUnit, RecceUnit in pairs( RecceGroup:GetUnits() ) do
|
||||
if RecceUnit:IsActive() then
|
||||
@@ -1942,7 +2086,8 @@ do -- DETECTION_UNITS
|
||||
function DETECTION_UNITS:CreateDetectionItems()
|
||||
-- Loop the current detected items, and check if each object still exists and is detected.
|
||||
|
||||
for DetectedItemKey, DetectedItem in pairs( self.DetectedItems ) do
|
||||
for DetectedItemKey, _DetectedItem in pairs( self.DetectedItems ) do
|
||||
local DetectedItem=_DetectedItem --#DETECTION_BASE.DetectedItem
|
||||
|
||||
local DetectedItemSet = DetectedItem.Set -- Core.Set#SET_UNIT
|
||||
|
||||
@@ -2028,7 +2173,7 @@ do -- DETECTION_UNITS
|
||||
local DetectedFirstUnitCoord = DetectedFirstUnit:GetCoordinate()
|
||||
self:SetDetectedItemCoordinate( DetectedItem, DetectedFirstUnitCoord, DetectedFirstUnit )
|
||||
|
||||
self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table
|
||||
self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSet } ) -- Fill the Friendlies table
|
||||
self:SetDetectedItemThreatLevel( DetectedItem )
|
||||
self:NearestRecce( DetectedItem )
|
||||
|
||||
@@ -2263,7 +2408,7 @@ do -- DETECTION_TYPES
|
||||
local DetectedUnitCoord = DetectedFirstUnit:GetCoordinate()
|
||||
self:SetDetectedItemCoordinate( DetectedItem, DetectedUnitCoord, DetectedFirstUnit )
|
||||
|
||||
self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table
|
||||
self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSet } ) -- Fill the Friendlies table
|
||||
self:SetDetectedItemThreatLevel( DetectedItem )
|
||||
self:NearestRecce( DetectedItem )
|
||||
end
|
||||
@@ -2281,7 +2426,7 @@ do -- DETECTION_TYPES
|
||||
function DETECTION_TYPES:DetectedItemReportSummary( DetectedItem, AttackGroup, Settings )
|
||||
self:F( { DetectedItem = DetectedItem } )
|
||||
|
||||
local DetectedSet = self:GetDetectedSet( DetectedItem )
|
||||
local DetectedSet = self:GetDetectedItemSet( DetectedItem )
|
||||
local DetectedItemID = self:GetDetectedItemID( DetectedItem )
|
||||
|
||||
self:T( DetectedItem )
|
||||
@@ -2397,28 +2542,67 @@ do -- DETECTION_AREAS
|
||||
-- @param Wrapper.Group#GROUP AttackGroup The group to get the settings for.
|
||||
-- @param Core.Settings#SETTINGS Settings (Optional) Message formatting settings to use.
|
||||
-- @return Core.Report#REPORT The report of the detection items.
|
||||
function DETECTION_AREAS:DetectedItemReportSummary( DetectedItem, AttackGroup, Settings )
|
||||
function DETECTION_AREAS:DetectedItemReportMenu( DetectedItem, AttackGroup, Settings )
|
||||
self:F( { DetectedItem = DetectedItem } )
|
||||
|
||||
local DetectedItemID = self:GetDetectedItemID( DetectedItem )
|
||||
|
||||
if DetectedItem then
|
||||
local DetectedSet = self:GetDetectedSet( DetectedItem )
|
||||
local DetectedSet = self:GetDetectedItemSet( DetectedItem )
|
||||
local ReportSummaryItem
|
||||
|
||||
local DetectedZone = self:GetDetectedItemZone( DetectedItem )
|
||||
local DetectedItemCoordinate = DetectedZone:GetCoordinate()
|
||||
local DetectedItemCoordText = DetectedItemCoordinate:ToString( AttackGroup, Settings )
|
||||
|
||||
local ThreatLevelA2G = self:GetDetectedItemThreatLevel( DetectedItem )
|
||||
|
||||
local Report = REPORT:New()
|
||||
Report:Add( DetectedItemID )
|
||||
Report:Add( string.format( "Threat: [%s%s]", string.rep( "■", ThreatLevelA2G ), string.rep( "□", 10-ThreatLevelA2G ) ) )
|
||||
|
||||
return Report
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Report summary of a detected item using a given numeric index.
|
||||
-- @param #DETECTION_AREAS self
|
||||
-- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem.
|
||||
-- @param Wrapper.Group#GROUP AttackGroup The group to get the settings for.
|
||||
-- @param Core.Settings#SETTINGS Settings (Optional) Message formatting settings to use.
|
||||
-- @return Core.Report#REPORT The report of the detection items.
|
||||
function DETECTION_AREAS:DetectedItemReportSummary( DetectedItem, AttackGroup, Settings )
|
||||
self:F( { DetectedItem = DetectedItem } )
|
||||
|
||||
local DetectedItemID = self:GetDetectedItemID( DetectedItem )
|
||||
|
||||
if DetectedItem then
|
||||
local DetectedSet = self:GetDetectedItemSet( DetectedItem )
|
||||
local ReportSummaryItem
|
||||
|
||||
--local DetectedZone = self:GetDetectedItemZone( DetectedItem )
|
||||
local DetectedItemCoordinate = self:GetDetectedItemCoordinate( DetectedItem )
|
||||
local DetectedAir = DetectedSet:HasAirUnits()
|
||||
local DetectedAltitude = self:GetDetectedItemCoordinate( DetectedItem )
|
||||
local DetectedItemCoordText = ""
|
||||
if DetectedAir > 0 then
|
||||
DetectedItemCoordText = DetectedItemCoordinate:ToStringA2A( AttackGroup, Settings )
|
||||
else
|
||||
DetectedItemCoordText = DetectedItemCoordinate:ToStringA2G( AttackGroup, Settings )
|
||||
end
|
||||
|
||||
|
||||
local ThreatLevelA2G = self:GetDetectedItemThreatLevel( DetectedItem )
|
||||
local DetectedItemsCount = DetectedSet:Count()
|
||||
local DetectedItemsTypes = DetectedSet:GetTypeNames()
|
||||
|
||||
local Report = REPORT:New()
|
||||
Report:Add(DetectedItemID .. ", " .. DetectedItemCoordText)
|
||||
Report:Add( string.format( "Threat: [%s]", string.rep( "■", ThreatLevelA2G ), string.rep( "□", 10-ThreatLevelA2G ) ) )
|
||||
Report:Add( string.format( "Threat: [%s%s]", string.rep( "■", ThreatLevelA2G ), string.rep( "□", 10-ThreatLevelA2G ) ) )
|
||||
Report:Add( string.format("Type: %2d of %s", DetectedItemsCount, DetectedItemsTypes ) )
|
||||
Report:Add( string.format("Detected: %s", DetectedItem.IsDetected and "yes" or "no" ) )
|
||||
--Report:Add( string.format("Detected: %s", DetectedItem.IsDetected and "yes" or "no" ) )
|
||||
|
||||
return Report
|
||||
end
|
||||
@@ -2736,7 +2920,7 @@ do -- DETECTION_AREAS
|
||||
if AddedToDetectionArea == false then
|
||||
|
||||
-- New detection area
|
||||
local DetectedItem = self:AddDetectedItemZone( nil,
|
||||
local DetectedItem = self:AddDetectedItemZone( "AREA", nil,
|
||||
SET_UNIT:New():FilterDeads():FilterCrashes(),
|
||||
ZONE_UNIT:New( DetectedUnitName, DetectedUnit, self.DetectionZoneRange )
|
||||
)
|
||||
@@ -2768,7 +2952,7 @@ do -- DETECTION_AREAS
|
||||
-- If there were friendlies nearby, and now there aren't any friendlies nearby, we flag the area as "changed".
|
||||
-- This is for the A2G dispatcher to detect if there is a change in the tactical situation.
|
||||
local OldFriendliesNearbyGround = self:IsFriendliesNearBy( DetectedItem, Unit.Category.GROUND_UNIT )
|
||||
self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table
|
||||
self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSet } ) -- Fill the Friendlies table
|
||||
local NewFriendliesNearbyGround = self:IsFriendliesNearBy( DetectedItem, Unit.Category.GROUND_UNIT )
|
||||
if OldFriendliesNearbyGround ~= NewFriendliesNearbyGround then
|
||||
DetectedItem.Changed = true
|
||||
@@ -2814,3 +2998,5 @@ do -- DETECTION_AREAS
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
416
Moose Development/Moose/Functional/DetectionZones.lua
Normal file
416
Moose Development/Moose/Functional/DetectionZones.lua
Normal file
@@ -0,0 +1,416 @@
|
||||
do -- DETECTION_ZONES
|
||||
|
||||
--- @type DETECTION_ZONES
|
||||
-- @field DCS#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target.
|
||||
-- @field #DETECTION_BASE.DetectedItems DetectedItems A list of areas containing the set of @{Wrapper.Unit}s, @{Zone}s, the center @{Wrapper.Unit} within the zone, and ID of each area that was detected within a DetectionZoneRange.
|
||||
-- @extends Functional.Detection#DETECTION_BASE
|
||||
|
||||
--- (old, to be revised ) Detect units within the battle zone for a list of @{Core.Zone}s detecting targets following (a) detection method(s),
|
||||
-- and will build a list (table) of @{Core.Set#SET_UNIT}s containing the @{Wrapper.Unit#UNIT}s detected.
|
||||
-- The class is group the detected units within zones given a DetectedZoneRange parameter.
|
||||
-- A set with multiple detected zones will be created as there are groups of units detected.
|
||||
--
|
||||
-- ## 4.1) Retrieve the Detected Unit Sets and Detected Zones
|
||||
--
|
||||
-- The methods to manage the DetectedItems[].Set(s) are implemented in @{Functional.Detection#DECTECTION_BASE} and
|
||||
-- the methods to manage the DetectedItems[].Zone(s) is implemented in @{Functional.Detection#DETECTION_ZONES}.
|
||||
--
|
||||
-- Retrieve the DetectedItems[].Set with the method @{Functional.Detection#DETECTION_BASE.GetDetectedSet}(). A @{Core.Set#SET_UNIT} object will be returned.
|
||||
--
|
||||
-- Retrieve the formed @{Zone@ZONE_UNIT}s as a result of the grouping the detected units within the DetectionZoneRange, use the method @{Functional.Detection#DETECTION_BASE.GetDetectionZones}().
|
||||
-- To understand the amount of zones created, use the method @{Functional.Detection#DETECTION_BASE.GetDetectionZoneCount}().
|
||||
-- If you want to obtain a specific zone from the DetectedZones, use the method @{Functional.Detection#DETECTION_BASE.GetDetectionZone}() with a given index.
|
||||
--
|
||||
-- ## 4.4) Flare or Smoke detected units
|
||||
--
|
||||
-- Use the methods @{Functional.Detection#DETECTION_ZONES.FlareDetectedUnits}() or @{Functional.Detection#DETECTION_ZONES.SmokeDetectedUnits}() to flare or smoke the detected units when a new detection has taken place.
|
||||
--
|
||||
-- ## 4.5) Flare or Smoke or Bound detected zones
|
||||
--
|
||||
-- Use the methods:
|
||||
--
|
||||
-- * @{Functional.Detection#DETECTION_ZONES.FlareDetectedZones}() to flare in a color
|
||||
-- * @{Functional.Detection#DETECTION_ZONES.SmokeDetectedZones}() to smoke in a color
|
||||
-- * @{Functional.Detection#DETECTION_ZONES.SmokeDetectedZones}() to bound with a tire with a white flag
|
||||
--
|
||||
-- the detected zones when a new detection has taken place.
|
||||
--
|
||||
-- @field #DETECTION_ZONES
|
||||
DETECTION_ZONES = {
|
||||
ClassName = "DETECTION_ZONES",
|
||||
DetectionZoneRange = nil,
|
||||
}
|
||||
|
||||
|
||||
--- DETECTION_ZONES constructor.
|
||||
-- @param #DETECTION_ZONES self
|
||||
-- @param Core.Set#SET_ZONE DetectionSetZone The @{Set} of ZONE_RADIUS.
|
||||
-- @param DCS#Coalition.side DetectionCoalition The coalition of the detection.
|
||||
-- @return #DETECTION_ZONES
|
||||
function DETECTION_ZONES:New( DetectionSetZone, DetectionCoalition )
|
||||
|
||||
-- Inherits from DETECTION_BASE
|
||||
local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetZone ) ) -- #DETECTION_ZONES
|
||||
|
||||
self.DetectionSetZone = DetectionSetZone -- Core.Set#SET_ZONE
|
||||
self.DetectionCoalition = DetectionCoalition
|
||||
|
||||
self._SmokeDetectedUnits = false
|
||||
self._FlareDetectedUnits = false
|
||||
self._SmokeDetectedZones = false
|
||||
self._FlareDetectedZones = false
|
||||
self._BoundDetectedZones = false
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- @param #DETECTION_ZONES self
|
||||
-- @param #number The amount of alive recce.
|
||||
function DETECTION_ZONES:CountAliveRecce()
|
||||
|
||||
return self.DetectionSetZone:Count()
|
||||
|
||||
end
|
||||
|
||||
--- @param #DETECTION_ZONES self
|
||||
function DETECTION_ZONES:ForEachAliveRecce( IteratorFunction, ... )
|
||||
self:F2( arg )
|
||||
|
||||
self.DetectionSetZone:ForEachZone( IteratorFunction, arg )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Report summary of a detected item using a given numeric index.
|
||||
-- @param #DETECTION_ZONES self
|
||||
-- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem.
|
||||
-- @param Wrapper.Group#GROUP AttackGroup The group to get the settings for.
|
||||
-- @param Core.Settings#SETTINGS Settings (Optional) Message formatting settings to use.
|
||||
-- @return Core.Report#REPORT The report of the detection items.
|
||||
function DETECTION_ZONES:DetectedItemReportSummary( DetectedItem, AttackGroup, Settings )
|
||||
self:F( { DetectedItem = DetectedItem } )
|
||||
|
||||
local DetectedItemID = self:GetDetectedItemID( DetectedItem )
|
||||
|
||||
if DetectedItem then
|
||||
local DetectedSet = self:GetDetectedItemSet( DetectedItem )
|
||||
local ReportSummaryItem
|
||||
|
||||
local DetectedZone = self:GetDetectedItemZone( DetectedItem )
|
||||
local DetectedItemCoordinate = DetectedZone:GetCoordinate()
|
||||
local DetectedItemCoordText = DetectedItemCoordinate:ToString( AttackGroup, Settings )
|
||||
|
||||
local ThreatLevelA2G = self:GetDetectedItemThreatLevel( DetectedItem )
|
||||
local DetectedItemsCount = DetectedSet:Count()
|
||||
local DetectedItemsTypes = DetectedSet:GetTypeNames()
|
||||
|
||||
local Report = REPORT:New()
|
||||
Report:Add(DetectedItemID .. ", " .. DetectedItemCoordText)
|
||||
Report:Add( string.format( "Threat: [%s]", string.rep( "■", ThreatLevelA2G ), string.rep( "□", 10-ThreatLevelA2G ) ) )
|
||||
Report:Add( string.format("Type: %2d of %s", DetectedItemsCount, DetectedItemsTypes ) )
|
||||
Report:Add( string.format("Detected: %s", DetectedItem.IsDetected and "yes" or "no" ) )
|
||||
|
||||
return Report
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Report detailed of a detection result.
|
||||
-- @param #DETECTION_ZONES self
|
||||
-- @param Wrapper.Group#GROUP AttackGroup The group to generate the report for.
|
||||
-- @return #string
|
||||
function DETECTION_ZONES:DetectedReportDetailed( AttackGroup ) --R2.1 Fixed missing report
|
||||
self:F()
|
||||
|
||||
local Report = REPORT:New()
|
||||
for DetectedItemIndex, DetectedItem in pairs( self.DetectedItems ) do
|
||||
local DetectedItem = DetectedItem -- #DETECTION_BASE.DetectedItem
|
||||
local ReportSummary = self:DetectedItemReportSummary( DetectedItem, AttackGroup )
|
||||
Report:SetTitle( "Detected areas:" )
|
||||
Report:Add( ReportSummary:Text() )
|
||||
end
|
||||
|
||||
local ReportText = Report:Text()
|
||||
|
||||
return ReportText
|
||||
end
|
||||
|
||||
|
||||
--- Calculate the optimal intercept point of the DetectedItem.
|
||||
-- @param #DETECTION_ZONES self
|
||||
-- @param #DETECTION_BASE.DetectedItem DetectedItem
|
||||
function DETECTION_ZONES:CalculateIntercept( DetectedItem )
|
||||
|
||||
local DetectedCoord = DetectedItem.Coordinate
|
||||
-- local DetectedSpeed = DetectedCoord:GetVelocity()
|
||||
-- local DetectedHeading = DetectedCoord:GetHeading()
|
||||
--
|
||||
-- if self.Intercept then
|
||||
-- local DetectedSet = DetectedItem.Set
|
||||
-- -- todo: speed
|
||||
--
|
||||
-- local TranslateDistance = DetectedSpeed * self.InterceptDelay
|
||||
--
|
||||
-- local InterceptCoord = DetectedCoord:Translate( TranslateDistance, DetectedHeading )
|
||||
--
|
||||
-- DetectedItem.InterceptCoord = InterceptCoord
|
||||
-- else
|
||||
-- DetectedItem.InterceptCoord = DetectedCoord
|
||||
-- end
|
||||
DetectedItem.InterceptCoord = DetectedCoord
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Smoke the detected units
|
||||
-- @param #DETECTION_ZONES self
|
||||
-- @return #DETECTION_ZONES self
|
||||
function DETECTION_ZONES:SmokeDetectedUnits()
|
||||
self:F2()
|
||||
|
||||
self._SmokeDetectedUnits = true
|
||||
return self
|
||||
end
|
||||
|
||||
--- Flare the detected units
|
||||
-- @param #DETECTION_ZONES self
|
||||
-- @return #DETECTION_ZONES self
|
||||
function DETECTION_ZONES:FlareDetectedUnits()
|
||||
self:F2()
|
||||
|
||||
self._FlareDetectedUnits = true
|
||||
return self
|
||||
end
|
||||
|
||||
--- Smoke the detected zones
|
||||
-- @param #DETECTION_ZONES self
|
||||
-- @return #DETECTION_ZONES self
|
||||
function DETECTION_ZONES:SmokeDetectedZones()
|
||||
self:F2()
|
||||
|
||||
self._SmokeDetectedZones = true
|
||||
return self
|
||||
end
|
||||
|
||||
--- Flare the detected zones
|
||||
-- @param #DETECTION_ZONES self
|
||||
-- @return #DETECTION_ZONES self
|
||||
function DETECTION_ZONES:FlareDetectedZones()
|
||||
self:F2()
|
||||
|
||||
self._FlareDetectedZones = true
|
||||
return self
|
||||
end
|
||||
|
||||
--- Bound the detected zones
|
||||
-- @param #DETECTION_ZONES self
|
||||
-- @return #DETECTION_ZONES self
|
||||
function DETECTION_ZONES:BoundDetectedZones()
|
||||
self:F2()
|
||||
|
||||
self._BoundDetectedZones = true
|
||||
return self
|
||||
end
|
||||
|
||||
--- Make text documenting the changes of the detected zone.
|
||||
-- @param #DETECTION_ZONES self
|
||||
-- @param #DETECTION_BASE.DetectedItem DetectedItem
|
||||
-- @return #string The Changes text
|
||||
function DETECTION_ZONES:GetChangeText( DetectedItem )
|
||||
self:F( DetectedItem )
|
||||
|
||||
local MT = {}
|
||||
|
||||
for ChangeCode, ChangeData in pairs( DetectedItem.Changes ) do
|
||||
|
||||
if ChangeCode == "AA" then
|
||||
MT[#MT+1] = "Detected new area " .. ChangeData.ID .. ". The center target is a " .. ChangeData.ItemUnitType .. "."
|
||||
end
|
||||
|
||||
if ChangeCode == "RAU" then
|
||||
MT[#MT+1] = "Changed area " .. ChangeData.ID .. ". Removed the center target."
|
||||
end
|
||||
|
||||
if ChangeCode == "AAU" then
|
||||
MT[#MT+1] = "Changed area " .. ChangeData.ID .. ". The new center target is a " .. ChangeData.ItemUnitType .. "."
|
||||
end
|
||||
|
||||
if ChangeCode == "RA" then
|
||||
MT[#MT+1] = "Removed old area " .. ChangeData.ID .. ". No more targets in this area."
|
||||
end
|
||||
|
||||
if ChangeCode == "AU" then
|
||||
local MTUT = {}
|
||||
for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do
|
||||
if ChangeUnitType ~= "ID" then
|
||||
MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType
|
||||
end
|
||||
end
|
||||
MT[#MT+1] = "Detected for area " .. ChangeData.ID .. " new target(s) " .. table.concat( MTUT, ", " ) .. "."
|
||||
end
|
||||
|
||||
if ChangeCode == "RU" then
|
||||
local MTUT = {}
|
||||
for ChangeUnitType, ChangeUnitCount in pairs( ChangeData ) do
|
||||
if ChangeUnitType ~= "ID" then
|
||||
MTUT[#MTUT+1] = ChangeUnitCount .. " of " .. ChangeUnitType
|
||||
end
|
||||
end
|
||||
MT[#MT+1] = "Removed for area " .. ChangeData.ID .. " invisible or destroyed target(s) " .. table.concat( MTUT, ", " ) .. "."
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return table.concat( MT, "\n" )
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Make a DetectionSet table. This function will be overridden in the derived clsses.
|
||||
-- @param #DETECTION_ZONES self
|
||||
-- @return #DETECTION_ZONES self
|
||||
function DETECTION_ZONES:CreateDetectionItems()
|
||||
|
||||
|
||||
self:F( "Checking Detected Items for new Detected Units ..." )
|
||||
|
||||
local DetectedUnits = SET_UNIT:New()
|
||||
|
||||
-- First go through all zones, and check if there are new Zones.
|
||||
-- New Zones become a new DetectedItem.
|
||||
for ZoneName, DetectionZone in pairs( self.DetectionSetZone:GetSet() ) do
|
||||
|
||||
local DetectedItem = self:GetDetectedItemByKey( ZoneName )
|
||||
|
||||
if DetectedItem == nil then
|
||||
DetectedItem = self:AddDetectedItemZone( "ZONE", ZoneName, nil, DetectionZone )
|
||||
end
|
||||
|
||||
local DetectedItemSetUnit = self:GetDetectedItemSet( DetectedItem )
|
||||
|
||||
-- Scan the zone
|
||||
DetectionZone:Scan( { Object.Category.UNIT }, { Unit.Category.GROUND_UNIT } )
|
||||
|
||||
-- For all the units in the zone,
|
||||
-- check if they are of the same coalition to be included.
|
||||
local ZoneUnits = DetectionZone:GetScannedUnits()
|
||||
for DCSUnitID, DCSUnit in pairs( ZoneUnits ) do
|
||||
local UnitName = DCSUnit:getName()
|
||||
local ZoneUnit = UNIT:FindByName( UnitName )
|
||||
local ZoneUnitCoalition = ZoneUnit:GetCoalition()
|
||||
if ZoneUnitCoalition == self.DetectionCoalition then
|
||||
if DetectedItemSetUnit:FindUnit( UnitName ) == nil and DetectedUnits:FindUnit( UnitName ) == nil then
|
||||
self:F( "Adding " .. UnitName )
|
||||
DetectedItemSetUnit:AddUnit( ZoneUnit )
|
||||
DetectedUnits:AddUnit( ZoneUnit )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Now all the tests should have been build, now make some smoke and flares...
|
||||
-- We also report here the friendlies within the detected areas.
|
||||
|
||||
for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do
|
||||
|
||||
local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem
|
||||
local DetectedSet = self:GetDetectedItemSet( DetectedItem )
|
||||
local DetectedFirstUnit = DetectedSet:GetFirst()
|
||||
local DetectedZone = self:GetDetectedItemZone( DetectedItem )
|
||||
|
||||
-- Set the last known coordinate to the detection item.
|
||||
local DetectedZoneCoord = DetectedZone:GetCoordinate()
|
||||
self:SetDetectedItemCoordinate( DetectedItem, DetectedZoneCoord, DetectedFirstUnit )
|
||||
|
||||
self:CalculateIntercept( DetectedItem )
|
||||
|
||||
-- We search for friendlies nearby.
|
||||
-- If there weren't any friendlies nearby, and now there are friendlies nearby, we flag the area as "changed".
|
||||
-- If there were friendlies nearby, and now there aren't any friendlies nearby, we flag the area as "changed".
|
||||
-- This is for the A2G dispatcher to detect if there is a change in the tactical situation.
|
||||
local OldFriendliesNearbyGround = self:IsFriendliesNearBy( DetectedItem, Unit.Category.GROUND_UNIT )
|
||||
self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table
|
||||
local NewFriendliesNearbyGround = self:IsFriendliesNearBy( DetectedItem, Unit.Category.GROUND_UNIT )
|
||||
if OldFriendliesNearbyGround ~= NewFriendliesNearbyGround then
|
||||
DetectedItem.Changed = true
|
||||
end
|
||||
|
||||
self:SetDetectedItemThreatLevel( DetectedItem ) -- Calculate A2G threat level
|
||||
--self:NearestRecce( DetectedItem )
|
||||
|
||||
|
||||
if DETECTION_ZONES._SmokeDetectedUnits or self._SmokeDetectedUnits then
|
||||
DetectedZone:SmokeZone( SMOKECOLOR.Red, 30 )
|
||||
end
|
||||
|
||||
--DetectedSet:Flush( self )
|
||||
|
||||
DetectedSet:ForEachUnit(
|
||||
--- @param Wrapper.Unit#UNIT DetectedUnit
|
||||
function( DetectedUnit )
|
||||
if DetectedUnit:IsAlive() then
|
||||
--self:T( "Detected Set #" .. DetectedItem.ID .. ":" .. DetectedUnit:GetName() )
|
||||
if DETECTION_ZONES._FlareDetectedUnits or self._FlareDetectedUnits then
|
||||
DetectedUnit:FlareGreen()
|
||||
end
|
||||
if DETECTION_ZONES._SmokeDetectedUnits or self._SmokeDetectedUnits then
|
||||
DetectedUnit:SmokeGreen()
|
||||
end
|
||||
end
|
||||
end
|
||||
)
|
||||
if DETECTION_ZONES._FlareDetectedZones or self._FlareDetectedZones then
|
||||
DetectedZone:FlareZone( SMOKECOLOR.White, 30, math.random( 0,90 ) )
|
||||
end
|
||||
if DETECTION_ZONES._SmokeDetectedZones or self._SmokeDetectedZones then
|
||||
DetectedZone:SmokeZone( SMOKECOLOR.White, 30 )
|
||||
end
|
||||
|
||||
if DETECTION_ZONES._BoundDetectedZones or self._BoundDetectedZones then
|
||||
self.CountryID = DetectedSet:GetFirst():GetCountry()
|
||||
DetectedZone:BoundZone( 12, self.CountryID )
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- @param #DETECTION_ZONES self
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @param Detection The element on which the detection is based.
|
||||
-- @param #number DetectionTimeStamp Time stamp of detection event.
|
||||
function DETECTION_ZONES:onafterDetection( From, Event, To, Detection, DetectionTimeStamp )
|
||||
|
||||
self.DetectionRun = self.DetectionRun + 1
|
||||
if self.DetectionCount > 0 and self.DetectionRun == self.DetectionCount then
|
||||
self:CreateDetectionItems() -- Polymorphic call to Create/Update the DetectionItems list for the DETECTION_ class grouping method.
|
||||
|
||||
for DetectedItemID, DetectedItem in pairs( self.DetectedItems ) do
|
||||
self:UpdateDetectedItemDetection( DetectedItem )
|
||||
self:CleanDetectionItem( DetectedItem, DetectedItemID ) -- Any DetectionItem that has a Set with zero elements in it, must be removed from the DetectionItems list.
|
||||
if DetectedItem then
|
||||
self:__DetectedItem( 0.1, DetectedItem )
|
||||
end
|
||||
end
|
||||
self:__Detect( -self.RefreshTimeInterval )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Set IsDetected flag for the DetectedItem, which can have more units.
|
||||
-- @param #DETECTION_ZONES self
|
||||
-- @return #DETECTION_ZONES.DetectedItem DetectedItem
|
||||
-- @return #boolean true if at least one UNIT is detected from the DetectedSet, false if no UNIT was detected from the DetectedSet.
|
||||
function DETECTION_ZONES:UpdateDetectedItemDetection( DetectedItem )
|
||||
|
||||
local IsDetected = true
|
||||
|
||||
DetectedItem.IsDetected = true
|
||||
|
||||
return IsDetected
|
||||
end
|
||||
|
||||
end
|
||||
@@ -2,6 +2,27 @@
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
-- * Escort navigation commands.
|
||||
-- * Escort hold at position commands.
|
||||
-- * Escorts reporting detected targets.
|
||||
-- * Escorts scanning targets in advance.
|
||||
-- * Escorts attacking specific targets.
|
||||
-- * Request assistance from other groups for attack.
|
||||
-- * Manage rule of engagement of escorts.
|
||||
-- * Manage the allowed evasion techniques of escorts.
|
||||
-- * Make escort to execute a defined mission or path.
|
||||
-- * Escort tactical situation reporting.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Missions:
|
||||
--
|
||||
-- [ESC - Escorting](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/ESC%20-%20Escorting)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- 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).
|
||||
@@ -77,47 +98,18 @@
|
||||
-- Escort groups can have their own mission. This menu item will allow the escort group to resume their Mission from a given waypoint.
|
||||
-- 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 @{Wrapper.Group#GROUP} for a @{Wrapper.Client#CLIENT}, with an optional briefing text.
|
||||
--
|
||||
-- # ESCORT initialization methods.
|
||||
-- ### Authors: **FlightControl**
|
||||
--
|
||||
-- The following menus are created within the RADIO MENU (F10) of an active unit hosted by a player:
|
||||
--
|
||||
-- * @{#ESCORT.MenuFollowAt}: Creates a menu to make the escort follow the client.
|
||||
-- * @{#ESCORT.MenuHoldAtEscortPosition}: Creates a menu to hold the escort at its current position.
|
||||
-- * @{#ESCORT.MenuHoldAtLeaderPosition}: Creates a menu to hold the escort at the client position.
|
||||
-- * @{#ESCORT.MenuScanForTargets}: Creates a menu so that the escort scans targets.
|
||||
-- * @{#ESCORT.MenuFlare}: Creates a menu to disperse flares.
|
||||
-- * @{#ESCORT.MenuSmoke}: Creates a menu to disparse smoke.
|
||||
-- * @{#ESCORT.MenuReportTargets}: Creates a menu so that the escort reports targets.
|
||||
-- * @{#ESCORT.MenuReportPosition}: Creates a menu so that the escort reports its current position from bullseye.
|
||||
-- * @{#ESCORT.MenuAssistedAttack: Creates a menu so that the escort supportes assisted attack from other escorts with the client.
|
||||
-- * @{#ESCORT.MenuROE: Creates a menu structure to set the rules of engagement of the escort.
|
||||
-- * @{#ESCORT.MenuEvasion: Creates a menu structure to set the evasion techniques when the escort is under threat.
|
||||
-- * @{#ESCORT.MenuResumeMission}: Creates a menu structure so that the escort can resume from a waypoint.
|
||||
--
|
||||
--
|
||||
-- @usage
|
||||
-- -- Declare a new EscortPlanes object as follows:
|
||||
--
|
||||
-- -- First find the GROUP object and the CLIENT object.
|
||||
-- local EscortClient = CLIENT:FindByName( "Unit Name" ) -- The Unit Name is the name of the unit flagged with the skill Client in the mission editor.
|
||||
-- local EscortGroup = GROUP:FindByName( "Group Name" ) -- The Group Name is the name of the group that will escort the Escort Client.
|
||||
--
|
||||
-- -- Now use these 2 objects to construct the new EscortPlanes object.
|
||||
-- EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the mission. You are escorted by a plane with code name 'Desert', which can be instructed through the F10 radio menu." )
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module Functional.Escort
|
||||
-- @image Escorting.JPG
|
||||
|
||||
--- ESCORT class
|
||||
-- @type ESCORT
|
||||
|
||||
|
||||
--- @type ESCORT
|
||||
-- @extends Core.Base#BASE
|
||||
-- @field Wrapper.Client#CLIENT EscortClient
|
||||
-- @field Wrapper.Group#GROUP EscortGroup
|
||||
@@ -129,6 +121,26 @@
|
||||
-- @Field DCS#AI.Option.Air.val.ROE OptionROE Which ROE is set to the EscortGroup.
|
||||
-- @field DCS#AI.Option.Air.val.REACTION_ON_THREAT OptionReactionOnThreat Which REACTION_ON_THREAT is set to the EscortGroup.
|
||||
-- @field FunctionalMENU_GROUPDETECTION_BASE Detection
|
||||
|
||||
--- ESCORT class
|
||||
--
|
||||
-- # ESCORT construction methods.
|
||||
--
|
||||
-- Create a new SPAWN object with the @{#ESCORT.New} method:
|
||||
--
|
||||
-- * @{#ESCORT.New}: Creates a new ESCORT object from a @{Wrapper.Group#GROUP} for a @{Wrapper.Client#CLIENT}, with an optional briefing text.
|
||||
--
|
||||
-- @usage
|
||||
-- -- Declare a new EscortPlanes object as follows:
|
||||
--
|
||||
-- -- First find the GROUP object and the CLIENT object.
|
||||
-- local EscortClient = CLIENT:FindByName( "Unit Name" ) -- The Unit Name is the name of the unit flagged with the skill Client in the mission editor.
|
||||
-- local EscortGroup = GROUP:FindByName( "Group Name" ) -- The Group Name is the name of the group that will escort the Escort Client.
|
||||
--
|
||||
-- -- Now use these 2 objects to construct the new EscortPlanes object.
|
||||
-- EscortPlanes = ESCORT:New( EscortClient, EscortGroup, "Desert", "Welcome to the mission. You are escorted by a plane with code name 'Desert', which can be instructed through the F10 radio menu." )
|
||||
--
|
||||
-- @field #ESCORT
|
||||
ESCORT = {
|
||||
ClassName = "ESCORT",
|
||||
EscortName = nil, -- The Escort Name
|
||||
@@ -860,7 +872,7 @@ function ESCORT:_AttackTarget( DetectedItem )
|
||||
EscortGroup:OptionROTPassiveDefense()
|
||||
EscortGroup:SetState( EscortGroup, "Escort", self )
|
||||
|
||||
local DetectedSet = self.Detection:GetDetectedSet( DetectedItem )
|
||||
local DetectedSet = self.Detection:GetDetectedItemSet( DetectedItem )
|
||||
|
||||
local Tasks = {}
|
||||
|
||||
@@ -883,7 +895,7 @@ function ESCORT:_AttackTarget( DetectedItem )
|
||||
|
||||
else
|
||||
|
||||
local DetectedSet = self.Detection:GetDetectedSet( DetectedItem )
|
||||
local DetectedSet = self.Detection:GetDetectedItemSet( DetectedItem )
|
||||
|
||||
local Tasks = {}
|
||||
|
||||
@@ -922,7 +934,7 @@ function ESCORT:_AssistTarget( EscortGroupAttack, DetectedItem )
|
||||
EscortGroupAttack:OptionROEOpenFire()
|
||||
EscortGroupAttack:OptionROTVertical()
|
||||
|
||||
local DetectedSet = self.Detection:GetDetectedSet( DetectedItem )
|
||||
local DetectedSet = self.Detection:GetDetectedItemSet( DetectedItem )
|
||||
|
||||
local Tasks = {}
|
||||
|
||||
@@ -944,7 +956,7 @@ function ESCORT:_AssistTarget( EscortGroupAttack, DetectedItem )
|
||||
)
|
||||
|
||||
else
|
||||
local DetectedSet = self.Detection:GetDetectedSet( DetectedItem )
|
||||
local DetectedSet = self.Detection:GetDetectedItemSet( DetectedItem )
|
||||
|
||||
local Tasks = {}
|
||||
|
||||
|
||||
1816
Moose Development/Moose/Functional/Fox.lua
Normal file
1816
Moose Development/Moose/Functional/Fox.lua
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,18 +1,27 @@
|
||||
--- **Functional** -- MISSILETRAINER helps you to train missile avoidance.
|
||||
--- **Functional** -- Train missile defence and deflection.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- The @{#MISSILETRAINER} class uses the DCS world messaging system to be alerted of any missiles fired, and when a missile would hit your aircraft,
|
||||
-- ## Features:
|
||||
--
|
||||
-- * Track the missiles fired at you and other players, providing bearing and range information of the missiles towards the airplanes.
|
||||
-- * Provide alerts of missile launches, including detailed information of the units launching, including bearing, range <20>
|
||||
-- * Provide alerts when a missile would have killed your aircraft.
|
||||
-- * Provide alerts when the missile self destructs.
|
||||
-- * Enable / Disable and Configure the Missile Trainer using the various menu options.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Missions:
|
||||
--
|
||||
-- [MIT - Missile Trainer](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/MIT%20-%20Missile%20Trainer)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- Uses the MOOSE 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:
|
||||
--
|
||||
-- * Track the missiles fired at you and other players, providing bearing and range information of the missiles towards the airplanes.
|
||||
-- * Provide alerts of missile launches, including detailed information of the units launching, including bearing, range <20>
|
||||
-- * Provide alerts when a missile would have killed your aircraft.
|
||||
-- * Provide alerts when the missile self destructs.
|
||||
-- * Enable / Disable and Configure the Missile Trainer using the various menu options.
|
||||
--
|
||||
-- When running a mission where MISSILETRAINER is used, the following radio menu structure ( 'Radio Menu' -> 'Other (F10)' -> 'MissileTrainer' ) options are available for the players:
|
||||
-- When running a mission where the missile trainer is used, the following radio menu structure ( 'Radio Menu' -> 'Other (F10)' -> 'MissileTrainer' ) options are available for the players:
|
||||
--
|
||||
-- * **Messages**: Menu to configure all messages.
|
||||
-- * **Messages On**: Show all messages.
|
||||
@@ -42,17 +51,40 @@
|
||||
-- * **150 meter**: Destroys the missile when the distance to the aircraft is below or equal to 150 meter.
|
||||
-- * **200 meter**: Destroys the missile when the distance to the aircraft is below or equal to 200 meter.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Authors: **FlightControl**
|
||||
--
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- * **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.
|
||||
-- Together with the **476 virtual team**, we tested the MISSILETRAINER class, and got much positive feedback!
|
||||
-- * **132nd Squadron**: Testing and optimizing the logic.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- 1.1) MISSILETRAINER construction methods:
|
||||
-- -----------------------------------------
|
||||
-- @module Functional.MissileTrainer
|
||||
-- @image Missile_Trainer.JPG
|
||||
|
||||
|
||||
--- @type MISSILETRAINER
|
||||
-- @field Core.Set#SET_CLIENT DBClients
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
|
||||
---
|
||||
--
|
||||
-- # Constructor:
|
||||
--
|
||||
-- Create a new MISSILETRAINER object with the @{#MISSILETRAINER.New} method:
|
||||
--
|
||||
-- * @{#MISSILETRAINER.New}: Creates a new MISSILETRAINER object taking the maximum distance to your aircraft to evaluate when a missile needs to be destroyed.
|
||||
--
|
||||
-- MISSILETRAINER will collect each unit declared in the mission with a skill level "Client" and "Player", and will monitor the missiles shot at those.
|
||||
--
|
||||
-- 1.2) MISSILETRAINER initialization methods:
|
||||
-- -------------------------------------------
|
||||
-- # Initialization:
|
||||
--
|
||||
-- A MISSILETRAINER object will behave differently based on the usage of initialization methods:
|
||||
--
|
||||
-- * @{#MISSILETRAINER.InitMessagesOnOff}: Sets by default the display of any message to be ON or OFF.
|
||||
@@ -65,24 +97,8 @@
|
||||
-- * @{#MISSILETRAINER.InitRangeOnOff}: Sets by default the display of range information of missiles ON of OFF.
|
||||
-- * @{#MISSILETRAINER.InitBearingOnOff}: Sets by default the display of bearing information of missiles ON of OFF.
|
||||
-- * @{#MISSILETRAINER.InitMenusOnOff}: Allows to configure the options through the radio menu.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- 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.
|
||||
-- Together with the **476 virtual team**, we tested the MISSILETRAINER class, and got much positive feedback!
|
||||
--
|
||||
-- @module Functional.MissileTrainer
|
||||
-- @image Missile_Trainer.JPG
|
||||
|
||||
|
||||
--- The MISSILETRAINER class
|
||||
-- @type MISSILETRAINER
|
||||
-- @field Core.Set#SET_CLIENT DBClients
|
||||
-- @extends Core.Base#BASE
|
||||
-- @field #MISSILETRAINER
|
||||
MISSILETRAINER = {
|
||||
ClassName = "MISSILETRAINER",
|
||||
TrackingMissiles = {},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
--- **Functional** -- Limit the MOVEMENT of simulaneous moving ground vehicles.
|
||||
--- **Functional** -- Limit the movement of simulaneous moving ground vehicles.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -10,9 +10,11 @@
|
||||
-- @module Functional.Movement
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
--- the MOVEMENT class
|
||||
-- @type MOVEMENT
|
||||
--- @type MOVEMENT
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
---
|
||||
--@field #MOVEMENT
|
||||
MOVEMENT = {
|
||||
ClassName = "MOVEMENT",
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
--- **Functional** - (R2.4) Rudimentary ATC.
|
||||
--- **Functional** - Rudimentary ATC.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
@@ -8,7 +8,7 @@
|
||||
--
|
||||
-- In particular, a menu entry "Pseudo ATC" is created in the "F10 Other..." radiomenu.
|
||||
--
|
||||
-- ## Features
|
||||
-- ## Features:
|
||||
--
|
||||
-- * Weather report at nearby airbases and mission waypoints.
|
||||
-- * Report absolute bearing and range to nearest airports and mission waypoints.
|
||||
@@ -20,12 +20,6 @@
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- # Demo Missions
|
||||
--
|
||||
-- ### [MOOSE - ALL Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS)
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- # YouTube Channel
|
||||
--
|
||||
-- ### [MOOSE YouTube Channel](https://www.youtube.com/channel/UCjrA9j5LQoWsG4SpS8i79Qg)
|
||||
@@ -87,7 +81,7 @@
|
||||
-- @field #PSEUDOATC
|
||||
PSEUDOATC={
|
||||
ClassName = "PSEUDOATC",
|
||||
player={},
|
||||
group={},
|
||||
Debug=false,
|
||||
mdur=30,
|
||||
mrefresh=120,
|
||||
@@ -104,7 +98,7 @@ PSEUDOATC.id="PseudoATC | "
|
||||
|
||||
--- PSEUDOATC version.
|
||||
-- @field #number version
|
||||
PSEUDOATC.version="0.9.0"
|
||||
PSEUDOATC.version="0.9.2"
|
||||
|
||||
-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -389,16 +383,23 @@ function PSEUDOATC:PlayerEntered(unit)
|
||||
local PlayerName=unit:GetPlayerName()
|
||||
local UnitName=unit:GetName()
|
||||
local CallSign=unit:GetCallsign()
|
||||
local UID=unit:GetDCSObject():getID()
|
||||
|
||||
if not self.group[GID] then
|
||||
self.group[GID]={}
|
||||
self.group[GID].player={}
|
||||
end
|
||||
|
||||
|
||||
-- Init player table.
|
||||
self.player[GID]={}
|
||||
self.player[GID].group=group
|
||||
self.player[GID].unit=unit
|
||||
self.player[GID].groupname=GroupName
|
||||
self.player[GID].unitname=UnitName
|
||||
self.player[GID].playername=PlayerName
|
||||
self.player[GID].callsign=CallSign
|
||||
self.player[GID].waypoints=group:GetTaskRoute()
|
||||
self.group[GID].player[UID]={}
|
||||
self.group[GID].player[UID].group=group
|
||||
self.group[GID].player[UID].unit=unit
|
||||
self.group[GID].player[UID].groupname=GroupName
|
||||
self.group[GID].player[UID].unitname=UnitName
|
||||
self.group[GID].player[UID].playername=PlayerName
|
||||
self.group[GID].player[UID].callsign=CallSign
|
||||
self.group[GID].player[UID].waypoints=group:GetTaskRoute()
|
||||
|
||||
-- Info message.
|
||||
local text=string.format("Player %s entered unit %s of group %s (id=%d).", PlayerName, UnitName, GroupName, GID)
|
||||
@@ -406,19 +407,26 @@ function PSEUDOATC:PlayerEntered(unit)
|
||||
MESSAGE:New(text, 30):ToAllIf(self.Debug)
|
||||
|
||||
-- Create main F10 menu, i.e. "F10/Pseudo ATC"
|
||||
self.player[GID].menu_main=missionCommands.addSubMenuForGroup(GID, "Pseudo ATC")
|
||||
local countPlayerInGroup = 0
|
||||
for _ in pairs(self.group[GID].player) do countPlayerInGroup = countPlayerInGroup + 1 end
|
||||
if countPlayerInGroup <= 1 then
|
||||
self.group[GID].menu_main=missionCommands.addSubMenuForGroup(GID, "Pseudo ATC")
|
||||
end
|
||||
|
||||
-- Create/update custom menu for player
|
||||
self:MenuCreatePlayer(GID,UID)
|
||||
|
||||
-- Create/update list of nearby airports.
|
||||
self:LocalAirports(GID)
|
||||
self:LocalAirports(GID,UID)
|
||||
|
||||
-- Create submenu of local airports.
|
||||
self:MenuAirports(GID)
|
||||
self:MenuAirports(GID,UID)
|
||||
|
||||
-- Create submenu Waypoints.
|
||||
self:MenuWaypoints(GID)
|
||||
self:MenuWaypoints(GID,UID)
|
||||
|
||||
-- Start scheduler to refresh the F10 menues.
|
||||
self.player[GID].scheduler, self.player[GID].schedulerid=SCHEDULER:New(nil, self.MenuRefresh, {self, GID}, self.mrefresh, self.mrefresh)
|
||||
self.group[GID].player[UID].scheduler, self.group[GID].player[UID].schedulerid=SCHEDULER:New(nil, self.MenuRefresh, {self, GID, UID}, self.mrefresh, self.mrefresh)
|
||||
|
||||
end
|
||||
|
||||
@@ -431,24 +439,23 @@ function PSEUDOATC:PlayerLanded(unit, place)
|
||||
|
||||
-- Gather some information.
|
||||
local group=unit:GetGroup()
|
||||
local id=group:GetID()
|
||||
local PlayerName=self.player[id].playername
|
||||
local Callsign=self.player[id].callsign
|
||||
local UnitName=self.player[id].unitname
|
||||
local GroupName=self.player[id].groupname
|
||||
local CallSign=self.player[id].callsign
|
||||
local GID=group:GetID()
|
||||
local UID=unit:GetDCSObject():getID()
|
||||
local PlayerName=self.group[GID].player[UID].playername
|
||||
local UnitName=self.group[GID].player[UID].unitname
|
||||
local GroupName=self.group[GID].player[UID].groupname
|
||||
|
||||
-- Debug message.
|
||||
local text=string.format("Player %s in unit %s of group %s (id=%d) landed at %s.", PlayerName, UnitName, GroupName, id, place)
|
||||
local text=string.format("Player %s in unit %s of group %s (id=%d) landed at %s.", PlayerName, UnitName, GroupName, GID, place)
|
||||
self:T(PSEUDOATC.id..text)
|
||||
MESSAGE:New(text, 30):ToAllIf(self.Debug)
|
||||
|
||||
-- Stop altitude reporting timer if its activated.
|
||||
self:AltitudeTimerStop(id)
|
||||
self:AltitudeTimerStop(GID,UID)
|
||||
|
||||
-- Welcome message.
|
||||
if place and self.chatty then
|
||||
local text=string.format("Touchdown! Welcome to %s. Have a nice day!", place)
|
||||
local text=string.format("Touchdown! Welcome to %s pilot %s. Have a nice day!", place,PlayerName)
|
||||
MESSAGE:New(text, self.mdur):ToGroup(group)
|
||||
end
|
||||
|
||||
@@ -463,15 +470,15 @@ function PSEUDOATC:PlayerTakeOff(unit, place)
|
||||
|
||||
-- Gather some information.
|
||||
local group=unit:GetGroup()
|
||||
local id=group:GetID()
|
||||
local PlayerName=self.player[id].playername
|
||||
local Callsign=self.player[id].callsign
|
||||
local UnitName=self.player[id].unitname
|
||||
local GroupName=self.player[id].groupname
|
||||
local CallSign=self.player[id].callsign
|
||||
local GID=group:GetID()
|
||||
local UID=unit:GetDCSObject():getID()
|
||||
local PlayerName=self.group[GID].player[UID].playername
|
||||
local CallSign=self.group[GID].player[UID].callsign
|
||||
local UnitName=self.group[GID].player[UID].unitname
|
||||
local GroupName=self.group[GID].player[UID].groupname
|
||||
|
||||
-- Debug message.
|
||||
local text=string.format("Player %s in unit %s of group %s (id=%d) took off at %s.", PlayerName, UnitName, GroupName, id, place)
|
||||
local text=string.format("Player %s in unit %s of group %s (id=%d) took off at %s.", PlayerName, UnitName, GroupName, GID, place)
|
||||
self:T(PSEUDOATC.id..text)
|
||||
MESSAGE:New(text, 30):ToAllIf(self.Debug)
|
||||
|
||||
@@ -491,30 +498,44 @@ function PSEUDOATC:PlayerLeft(unit)
|
||||
|
||||
-- Get id.
|
||||
local group=unit:GetGroup()
|
||||
local id=group:GetID()
|
||||
local GID=group:GetID()
|
||||
local UID=unit:GetDCSObject():getID()
|
||||
|
||||
if self.player[id] then
|
||||
if self.group[GID].player[UID] then
|
||||
local PlayerName=self.group[GID].player[UID].playername
|
||||
local CallSign=self.group[GID].player[UID].callsign
|
||||
local UnitName=self.group[GID].player[UID].unitname
|
||||
local GroupName=self.group[GID].player[UID].groupname
|
||||
|
||||
-- Debug message.
|
||||
local text=string.format("Player %s (callsign %s) of group %s just left unit %s.", self.player[id].playername, self.player[id].callsign, self.player[id].groupname, self.player[id].unitname)
|
||||
local text=string.format("Player %s (callsign %s) of group %s just left unit %s.", PlayerName, CallSign, GroupName, UnitName)
|
||||
self:T(PSEUDOATC.id..text)
|
||||
MESSAGE:New(text, 30):ToAllIf(self.Debug)
|
||||
|
||||
-- Stop scheduler for menu updates
|
||||
if self.player[id].schedulerid then
|
||||
self.player[id].scheduler:Stop(self.player[id].schedulerid)
|
||||
if self.group[GID].player[UID].schedulerid then
|
||||
self.group[GID].player[UID].scheduler:Stop(self.group[GID].player[UID].schedulerid)
|
||||
end
|
||||
|
||||
-- Stop scheduler for reporting alt if it runs.
|
||||
self:AltitudeTimerStop(id)
|
||||
self:AltitudeTimerStop(GID,UID)
|
||||
|
||||
-- Remove own menu.
|
||||
if self.group[GID].player[UID].menu_own then
|
||||
missionCommands.removeItemForGroup(GID,self.group[GID].player[UID].menu_own)
|
||||
end
|
||||
-- Remove main menu.
|
||||
if self.player[id].menu_main then
|
||||
missionCommands.removeItem(self.player[id].menu_main)
|
||||
-- WARNING: Remove only if last human element of group
|
||||
|
||||
local countPlayerInGroup = 0
|
||||
for _ in pairs(self.group[GID].player) do countPlayerInGroup = countPlayerInGroup + 1 end
|
||||
|
||||
if self.group[GID].menu_main and countPlayerInGroup==1 then
|
||||
missionCommands.removeItemForGroup(GID,self.group[GID].menu_main)
|
||||
end
|
||||
|
||||
-- Remove player array.
|
||||
self.player[id]=nil
|
||||
self.group[GID].player[UID]=nil
|
||||
|
||||
end
|
||||
end
|
||||
@@ -524,80 +545,94 @@ end
|
||||
|
||||
--- Refreshes all player menues.
|
||||
-- @param #PSEUDOATC self.
|
||||
-- @param #number id Group id of player unit.
|
||||
function PSEUDOATC:MenuRefresh(id)
|
||||
self:F({id=id})
|
||||
|
||||
-- @param #number GID Group id of player unit.
|
||||
-- @param #number UID Unit id of player.
|
||||
function PSEUDOATC:MenuRefresh(GID,UID)
|
||||
self:F({GID=GID,UID=UID})
|
||||
-- Debug message.
|
||||
local text=string.format("Refreshing menues for player %s in group %s.", self.player[id].playername, self.player[id].groupname)
|
||||
local text=string.format("Refreshing menues for player %s in group %s.", self.group[GID].player[UID].playername, self.group[GID].player[UID].groupname)
|
||||
self:T(PSEUDOATC.id..text)
|
||||
MESSAGE:New(text,30):ToAllIf(self.Debug)
|
||||
|
||||
-- Clear menu.
|
||||
self:MenuClear(id)
|
||||
self:MenuClear(GID,UID)
|
||||
|
||||
-- Create list of nearby airports.
|
||||
self:LocalAirports(id)
|
||||
self:LocalAirports(GID,UID)
|
||||
|
||||
-- Create submenu Local Airports.
|
||||
self:MenuAirports(id)
|
||||
self:MenuAirports(GID,UID)
|
||||
|
||||
-- Create submenu Waypoints etc.
|
||||
self:MenuWaypoints(id)
|
||||
self:MenuWaypoints(GID,UID)
|
||||
|
||||
end
|
||||
|
||||
--- Create player menus.
|
||||
-- @param #PSEUDOATC self.
|
||||
-- @param #number GID Group id of player unit.
|
||||
-- @param #number UID Unit id of player.
|
||||
function PSEUDOATC:MenuCreatePlayer(GID,UID)
|
||||
self:F({GID=GID,UID=UID})
|
||||
-- Table for menu entries.
|
||||
local PlayerName=self.group[GID].player[UID].playername
|
||||
self.group[GID].player[UID].menu_own=missionCommands.addSubMenuForGroup(GID, PlayerName, self.group[GID].menu_main)
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Clear player menus.
|
||||
-- @param #PSEUDOATC self.
|
||||
-- @param #number id Group id of player unit.
|
||||
function PSEUDOATC:MenuClear(id)
|
||||
self:F(id)
|
||||
-- @param #number GID Group id of player unit.
|
||||
-- @param #number UID Unit id of player.
|
||||
function PSEUDOATC:MenuClear(GID,UID)
|
||||
self:F({GID=GID,UID=UID})
|
||||
|
||||
-- Debug message.
|
||||
local text=string.format("Clearing menus for player %s in group %s.", self.player[id].playername, self.player[id].groupname)
|
||||
local text=string.format("Clearing menus for player %s in group %s.", self.group[GID].player[UID].playername, self.group[GID].player[UID].groupname)
|
||||
self:T(PSEUDOATC.id..text)
|
||||
MESSAGE:New(text,30):ToAllIf(self.Debug)
|
||||
|
||||
-- Delete Airports menu.
|
||||
if self.player[id].menu_airports then
|
||||
missionCommands.removeItemForGroup(id, self.player[id].menu_airports)
|
||||
self.player[id].menu_airports=nil
|
||||
if self.group[GID].player[UID].menu_airports then
|
||||
missionCommands.removeItemForGroup(GID, self.group[GID].player[UID].menu_airports)
|
||||
self.group[GID].player[UID].menu_airports=nil
|
||||
else
|
||||
self:T2(PSEUDOATC.id.."No airports to clear menus.")
|
||||
end
|
||||
|
||||
-- Delete waypoints menu.
|
||||
if self.player[id].menu_waypoints then
|
||||
missionCommands.removeItemForGroup(id, self.player[id].menu_waypoints)
|
||||
self.player[id].menu_waypoints=nil
|
||||
if self.group[GID].player[UID].menu_waypoints then
|
||||
missionCommands.removeItemForGroup(GID, self.group[GID].player[UID].menu_waypoints)
|
||||
self.group[GID].player[UID].menu_waypoints=nil
|
||||
end
|
||||
|
||||
-- Delete report alt until touchdown menu command.
|
||||
if self.player[id].menu_reportalt then
|
||||
missionCommands.removeItemForGroup(id, self.player[id].menu_reportalt)
|
||||
self.player[id].menu_reportalt=nil
|
||||
if self.group[GID].player[UID].menu_reportalt then
|
||||
missionCommands.removeItemForGroup(GID, self.group[GID].player[UID].menu_reportalt)
|
||||
self.group[GID].player[UID].menu_reportalt=nil
|
||||
end
|
||||
|
||||
-- Delete request current alt menu command.
|
||||
if self.player[id].menu_requestalt then
|
||||
missionCommands.removeItemForGroup(id, self.player[id].menu_requestalt)
|
||||
self.player[id].menu_requestalt=nil
|
||||
if self.group[GID].player[UID].menu_requestalt then
|
||||
missionCommands.removeItemForGroup(GID, self.group[GID].player[UID].menu_requestalt)
|
||||
self.group[GID].player[UID].menu_requestalt=nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Create "F10/Pseudo ATC/Local Airports/Airport Name/" menu items each containing weather report and BR request.
|
||||
-- @param #PSEUDOATC self
|
||||
-- @param #number id Group id of player unit for which menues are created.
|
||||
function PSEUDOATC:MenuAirports(id)
|
||||
self:F(id)
|
||||
-- @param #number GID Group id of player unit.
|
||||
-- @param #number UID Unit id of player.
|
||||
function PSEUDOATC:MenuAirports(GID,UID)
|
||||
self:F({GID=GID,UID=UID})
|
||||
|
||||
-- Table for menu entries.
|
||||
self.player[id].menu_airports=missionCommands.addSubMenuForGroup(id, "Local Airports", self.player[id].menu_main)
|
||||
self.group[GID].player[UID].menu_airports=missionCommands.addSubMenuForGroup(GID, "Local Airports", self.group[GID].player[UID].menu_own)
|
||||
|
||||
local i=0
|
||||
for _,airport in pairs(self.player[id].airports) do
|
||||
for _,airport in pairs(self.group[GID].player[UID].airports) do
|
||||
|
||||
i=i+1
|
||||
if i > 10 then
|
||||
@@ -609,37 +644,38 @@ function PSEUDOATC:MenuAirports(id)
|
||||
local pos=AIRBASE:FindByName(name):GetCoordinate()
|
||||
|
||||
--F10menu_ATC_airports[ID][name] = missionCommands.addSubMenuForGroup(ID, name, F10menu_ATC)
|
||||
local submenu=missionCommands.addSubMenuForGroup(id, name, self.player[id].menu_airports)
|
||||
local submenu=missionCommands.addSubMenuForGroup(GID, name, self.group[GID].player[UID].menu_airports)
|
||||
|
||||
-- Create menu reporting commands
|
||||
missionCommands.addCommandForGroup(id, "Weather Report", submenu, self.ReportWeather, self, id, pos, name)
|
||||
missionCommands.addCommandForGroup(id, "Request BR", submenu, self.ReportBR, self, id, pos, name)
|
||||
missionCommands.addCommandForGroup(GID, "Weather Report", submenu, self.ReportWeather, self, GID, UID, pos, name)
|
||||
missionCommands.addCommandForGroup(GID, "Request BR", submenu, self.ReportBR, self, GID, UID, pos, name)
|
||||
|
||||
-- Debug message.
|
||||
self:T(string.format(PSEUDOATC.id.."Creating airport menu item %s for ID %d", name, id))
|
||||
self:T(string.format(PSEUDOATC.id.."Creating airport menu item %s for ID %d", name, GID))
|
||||
end
|
||||
end
|
||||
|
||||
--- Create "F10/Pseudo ATC/Waypoints/<Waypoint i> menu items.
|
||||
-- @param #PSEUDOATC self
|
||||
-- @param #number id Group id of player unit for which menues are created.
|
||||
function PSEUDOATC:MenuWaypoints(id)
|
||||
self:F(id)
|
||||
-- @param #number GID Group id of player unit.
|
||||
-- @param #number UID Unit id of player.
|
||||
function PSEUDOATC:MenuWaypoints(GID, UID)
|
||||
self:F({GID=GID, UID=UID})
|
||||
|
||||
-- Player unit and callsign.
|
||||
local unit=self.player[id].unit --Wrapper.Unit#UNIT
|
||||
local callsign=self.player[id].callsign
|
||||
-- local unit=self.group[GID].player[UID].unit --Wrapper.Unit#UNIT
|
||||
local callsign=self.group[GID].player[UID].callsign
|
||||
|
||||
-- Debug info.
|
||||
self:T(PSEUDOATC.id..string.format("Creating waypoint menu for %s (ID %d).", callsign, id))
|
||||
self:T(PSEUDOATC.id..string.format("Creating waypoint menu for %s (ID %d).", callsign, GID))
|
||||
|
||||
if #self.player[id].waypoints>0 then
|
||||
if #self.group[GID].player[UID].waypoints>0 then
|
||||
|
||||
-- F10/PseudoATC/Waypoints
|
||||
self.player[id].menu_waypoints=missionCommands.addSubMenuForGroup(id, "Waypoints", self.player[id].menu_main)
|
||||
self.group[GID].player[UID].menu_waypoints=missionCommands.addSubMenuForGroup(GID, "Waypoints", self.group[GID].player[UID].menu_own)
|
||||
|
||||
local j=0
|
||||
for i, wp in pairs(self.player[id].waypoints) do
|
||||
for i, wp in pairs(self.group[GID].player[UID].waypoints) do
|
||||
|
||||
-- Increase counter
|
||||
j=j+1
|
||||
@@ -653,16 +689,16 @@ function PSEUDOATC:MenuWaypoints(id)
|
||||
local name=string.format("Waypoint %d", i-1)
|
||||
|
||||
-- "F10/PseudoATC/Waypoints/Waypoint X"
|
||||
local submenu=missionCommands.addSubMenuForGroup(id, name, self.player[id].menu_waypoints)
|
||||
local submenu=missionCommands.addSubMenuForGroup(GID, name, self.group[GID].player[UID].menu_waypoints)
|
||||
|
||||
-- Menu commands for each waypoint "F10/PseudoATC/My Aircraft (callsign)/Waypoints/Waypoint X/<Commands>"
|
||||
missionCommands.addCommandForGroup(id, "Weather Report", submenu, self.ReportWeather, self, id, pos, name)
|
||||
missionCommands.addCommandForGroup(id, "Request BR", submenu, self.ReportBR, self, id, pos, name)
|
||||
missionCommands.addCommandForGroup(GID, "Weather Report", submenu, self.ReportWeather, self, GID, UID, pos, name)
|
||||
missionCommands.addCommandForGroup(GID, "Request BR", submenu, self.ReportBR, self, GID, UID, pos, name)
|
||||
end
|
||||
end
|
||||
|
||||
self.player[id].menu_reportalt = missionCommands.addCommandForGroup(id, "Talk me down", self.player[id].menu_main, self.AltidudeTimerToggle, self, id)
|
||||
self.player[id].menu_requestalt = missionCommands.addCommandForGroup(id, "Request altitude", self.player[id].menu_main, self.ReportHeight, self, id)
|
||||
self.group[GID].player[UID].menu_reportalt = missionCommands.addCommandForGroup(GID, "Talk me down", self.group[GID].player[UID].menu_own, self.AltidudeTimerToggle, self, GID, UID)
|
||||
self.group[GID].player[UID].menu_requestalt = missionCommands.addCommandForGroup(GID, "Request altitude", self.group[GID].player[UID].menu_own, self.ReportHeight, self, GID, UID)
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -670,14 +706,15 @@ end
|
||||
|
||||
--- Weather Report. Report pressure QFE/QNH, temperature, wind at certain location.
|
||||
-- @param #PSEUDOATC self
|
||||
-- @param #number id Group id to which the report is delivered.
|
||||
-- @param #number GID Group id of player unit.
|
||||
-- @param #number UID Unit id of player.
|
||||
-- @param Core.Point#COORDINATE position Coordinates at which the pressure is measured.
|
||||
-- @param #string location Name of the location at which the pressure is measured.
|
||||
function PSEUDOATC:ReportWeather(id, position, location)
|
||||
self:F({id=id, position=position, location=location})
|
||||
function PSEUDOATC:ReportWeather(GID, UID, position, location)
|
||||
self:F({GID=GID, UID=UID, position=position, location=location})
|
||||
|
||||
-- Player unit system settings.
|
||||
local settings=_DATABASE:GetPlayerSettings(self.player[id].playername) or _SETTINGS --Core.Settings#SETTINGS
|
||||
local settings=_DATABASE:GetPlayerSettings(self.group[GID].player[UID].playername) or _SETTINGS --Core.Settings#SETTINGS
|
||||
|
||||
local text=string.format("Local weather at %s:\n", location)
|
||||
|
||||
@@ -729,36 +766,37 @@ function PSEUDOATC:ReportWeather(id, position, location)
|
||||
end
|
||||
|
||||
-- Message text.
|
||||
local text=text..string.format("Wind from %s at %s (%s).", Ds, Vs, Bd)
|
||||
local text=text..string.format("%s, Wind from %s at %s (%s).", self.group[GID].player[UID].playername, Ds, Vs, Bd)
|
||||
|
||||
-- Send message
|
||||
self:_DisplayMessageToGroup(self.player[id].unit, text, self.mdur, true)
|
||||
self:_DisplayMessageToGroup(self.group[GID].player[UID].unit, text, self.mdur, true)
|
||||
|
||||
end
|
||||
|
||||
--- Report absolute bearing and range form player unit to airport.
|
||||
-- @param #PSEUDOATC self
|
||||
-- @param #number id Group id to the report is delivered.
|
||||
-- @param #number GID Group id of player unit.
|
||||
-- @param #number UID Unit id of player.
|
||||
-- @param Core.Point#COORDINATE position Coordinates at which the pressure is measured.
|
||||
-- @param #string location Name of the location at which the pressure is measured.
|
||||
function PSEUDOATC:ReportBR(id, position, location)
|
||||
self:F({id=id, position=position, location=location})
|
||||
function PSEUDOATC:ReportBR(GID, UID, position, location)
|
||||
self:F({GID=GID, UID=UID, position=position, location=location})
|
||||
|
||||
-- Current coordinates.
|
||||
local unit=self.player[id].unit --Wrapper.Unit#UNIT
|
||||
local unit=self.group[GID].player[UID].unit --Wrapper.Unit#UNIT
|
||||
local coord=unit:GetCoordinate()
|
||||
|
||||
-- Direction vector from current position (coord) to target (position).
|
||||
local pos=coord:Translate(30,90)
|
||||
local vec3=coord:GetDirectionVec3(pos)
|
||||
local angle=coord:GetAngleDegrees(vec3)
|
||||
local angle=coord:HeadingTo(position)
|
||||
|
||||
-- Range from current to
|
||||
local range=coord:Get2DDistance(position)
|
||||
|
||||
-- Bearing string.
|
||||
local Bs=string.format('%03d°', angle)
|
||||
|
||||
-- Settings.
|
||||
local settings=_DATABASE:GetPlayerSettings(self.player[id].playername) or _SETTINGS --Core.Settings#SETTINGS
|
||||
local settings=_DATABASE:GetPlayerSettings(self.group[GID].player[UID].playername) or _SETTINGS --Core.Settings#SETTINGS
|
||||
|
||||
|
||||
local Rs=string.format("%.1f NM", UTILS.MetersToNM(range))
|
||||
@@ -769,18 +807,20 @@ function PSEUDOATC:ReportBR(id, position, location)
|
||||
-- Message text.
|
||||
local text=string.format("%s: Bearing %s, Range %s.", location, Bs, Rs)
|
||||
|
||||
-- Send message to player group.
|
||||
MESSAGE:New(text, self.mdur):ToGroup(self.player[id].group)
|
||||
-- Send message
|
||||
self:_DisplayMessageToGroup(self.group[GID].player[UID].unit, text, self.mdur, true)
|
||||
|
||||
end
|
||||
|
||||
--- Report altitude above ground level of player unit.
|
||||
-- @param #PSEUDOATC self
|
||||
-- @param #number id Group id to the report is delivered.
|
||||
-- @param #number GID Group id of player unit.
|
||||
-- @param #number UID Unit id of player.
|
||||
-- @param #number dt (Optional) Duration the message is displayed.
|
||||
-- @param #boolean _clear (Optional) Clear previouse messages.
|
||||
-- @return #number Altitude above ground.
|
||||
function PSEUDOATC:ReportHeight(id, dt, _clear)
|
||||
self:F({id=id, dt=dt})
|
||||
function PSEUDOATC:ReportHeight(GID, UID, dt, _clear)
|
||||
self:F({GID=GID, UID=UID, dt=dt})
|
||||
|
||||
local dt = dt or self.mdur
|
||||
if _clear==nil then
|
||||
@@ -797,7 +837,7 @@ function PSEUDOATC:ReportHeight(id, dt, _clear)
|
||||
end
|
||||
|
||||
-- Get height AGL.
|
||||
local unit=self.player[id].unit --Wrapper.Unit#UNIT
|
||||
local unit=self.group[GID].player[UID].unit --Wrapper.Unit#UNIT
|
||||
|
||||
if unit and unit:IsAlive() then
|
||||
|
||||
@@ -806,7 +846,7 @@ function PSEUDOATC:ReportHeight(id, dt, _clear)
|
||||
local callsign=unit:GetCallsign()
|
||||
|
||||
-- Settings.
|
||||
local settings=_DATABASE:GetPlayerSettings(self.player[id].playername) or _SETTINGS --Core.Settings#SETTINGS
|
||||
local settings=_DATABASE:GetPlayerSettings(self.group[GID].player[UID].playername) or _SETTINGS --Core.Settings#SETTINGS
|
||||
|
||||
-- Height string.
|
||||
local Hs=string.format("%d ft", UTILS.MetersToFeet(height))
|
||||
@@ -823,7 +863,7 @@ function PSEUDOATC:ReportHeight(id, dt, _clear)
|
||||
end
|
||||
|
||||
-- Send message to player group.
|
||||
self:_DisplayMessageToGroup(self.player[id].unit,_text, dt,_clear)
|
||||
self:_DisplayMessageToGroup(self.group[GID].player[UID].unit,_text, dt,_clear)
|
||||
|
||||
-- Return height
|
||||
return height
|
||||
@@ -836,47 +876,50 @@ end
|
||||
|
||||
--- Toggle report altitude reporting on/off.
|
||||
-- @param #PSEUDOATC self.
|
||||
-- @param #number id Group id of player unit.
|
||||
function PSEUDOATC:AltidudeTimerToggle(id)
|
||||
self:F(id)
|
||||
-- @param #number GID Group id of player unit.
|
||||
-- @param #number UID Unit id of player.
|
||||
function PSEUDOATC:AltidudeTimerToggle(GID,UID)
|
||||
self:F({GID=GID, UID=UID})
|
||||
|
||||
if self.player[id].altimerid then
|
||||
if self.group[GID].player[UID].altimerid then
|
||||
-- If the timer is on, we turn it off.
|
||||
self:AltitudeTimerStop(id)
|
||||
self:AltitudeTimerStop(GID, UID)
|
||||
else
|
||||
-- If the timer is off, we turn it on.
|
||||
self:AltitudeTimeStart(id)
|
||||
self:AltitudeTimeStart(GID, UID)
|
||||
end
|
||||
end
|
||||
|
||||
--- Start altitude reporting scheduler.
|
||||
-- @param #PSEUDOATC self.
|
||||
-- @param #number id Group id of player unit.
|
||||
function PSEUDOATC:AltitudeTimeStart(id)
|
||||
self:F(id)
|
||||
-- @param #number GID Group id of player unit.
|
||||
-- @param #number UID Unit id of player.
|
||||
function PSEUDOATC:AltitudeTimeStart(GID, UID)
|
||||
self:F({GID=GID, UID=UID})
|
||||
|
||||
-- Debug info.
|
||||
self:T(PSEUDOATC.id..string.format("Starting altitude report timer for player ID %d.", id))
|
||||
self:T(PSEUDOATC.id..string.format("Starting altitude report timer for player ID %d.", UID))
|
||||
|
||||
-- Start timer. Altitude is reported every ~3 seconds.
|
||||
self.player[id].altimer, self.player[id].altimerid=SCHEDULER:New(nil, self.ReportHeight, {self, id, 0.1, true}, 1, 3)
|
||||
self.group[GID].player[UID].altimer, self.group[GID].player[UID].altimerid=SCHEDULER:New(nil, self.ReportHeight, {self, GID, UID, 0.1, true}, 1, 3)
|
||||
end
|
||||
|
||||
--- Stop/destroy DCS scheduler function for reporting altitude.
|
||||
-- @param #PSEUDOATC self.
|
||||
-- @param #number id Group id of player unit.
|
||||
function PSEUDOATC:AltitudeTimerStop(id)
|
||||
|
||||
-- @param #number GID Group id of player unit.
|
||||
-- @param #number UID Unit id of player.
|
||||
function PSEUDOATC:AltitudeTimerStop(GID, UID)
|
||||
self:F({GID=GID,UID=UID})
|
||||
-- Debug info.
|
||||
self:T(PSEUDOATC.id..string.format("Stopping altitude report timer for player ID %d.", id))
|
||||
self:T(PSEUDOATC.id..string.format("Stopping altitude report timer for player ID %d.", UID))
|
||||
|
||||
-- Stop timer.
|
||||
if self.player[id].altimerid then
|
||||
self.player[id].altimer:Stop(self.player[id].altimerid)
|
||||
if self.group[GID].player[UID].altimerid then
|
||||
self.group[GID].player[UID].altimer:Stop(self.group[GID].player[UID].altimerid)
|
||||
end
|
||||
|
||||
self.player[id].altimer=nil
|
||||
self.player[id].altimerid=nil
|
||||
self.group[GID].player[UID].altimer=nil
|
||||
self.group[GID].player[UID].altimerid=nil
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -884,16 +927,17 @@ end
|
||||
|
||||
--- Create list of nearby airports sorted by distance to player unit.
|
||||
-- @param #PSEUDOATC self
|
||||
-- @param #number id Group id of player unit.
|
||||
function PSEUDOATC:LocalAirports(id)
|
||||
self:F(id)
|
||||
-- @param #number GID Group id of player unit.
|
||||
-- @param #number UID Unit id of player.
|
||||
function PSEUDOATC:LocalAirports(GID, UID)
|
||||
self:F({GID=GID, UID=UID})
|
||||
|
||||
-- Airports table.
|
||||
self.player[id].airports=nil
|
||||
self.player[id].airports={}
|
||||
self.group[GID].player[UID].airports=nil
|
||||
self.group[GID].player[UID].airports={}
|
||||
|
||||
-- Current player position.
|
||||
local pos=self.player[id].unit:GetCoordinate()
|
||||
local pos=self.group[GID].player[UID].unit:GetCoordinate()
|
||||
|
||||
-- Loop over coalitions.
|
||||
for i=0,2 do
|
||||
@@ -909,7 +953,7 @@ function PSEUDOATC:LocalAirports(id)
|
||||
local d=q:Get2DDistance(pos)
|
||||
|
||||
-- Add to table.
|
||||
table.insert(self.player[id].airports, {distance=d, name=name})
|
||||
table.insert(self.group[GID].player[UID].airports, {distance=d, name=name})
|
||||
|
||||
end
|
||||
end
|
||||
@@ -920,7 +964,7 @@ function PSEUDOATC:LocalAirports(id)
|
||||
end
|
||||
|
||||
-- Sort airports table w.r.t. distance to player.
|
||||
table.sort(self.player[id].airports, compare)
|
||||
table.sort(self.group[GID].player[UID].airports, compare)
|
||||
|
||||
end
|
||||
|
||||
@@ -998,3 +1042,5 @@ function PSEUDOATC:_myname(unitname)
|
||||
return string.format("%s (%s)", csign, pname)
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,33 +1,31 @@
|
||||
--- **Functional** - (R2.2) - Create random airtraffic in your missions.
|
||||
--- **Functional** - Create random airtraffic in your missions.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- The aim of the RAT class is to fill the empty DCS world with randomized air traffic and bring more life to your airports.
|
||||
--
|
||||
-- In particular, it is designed to spawn AI air units at random airports. These units will be assigned a random flight path to another random airport on the map.
|
||||
--
|
||||
-- The aim of the RAT class is to fill the empty DCS world with randomized air traffic and bring more life to your airports.
|
||||
-- In particular, it is designed to spawn AI air units at random airports. These units will be assigned a random flight path to another random airport on the map.
|
||||
-- Even the mission designer will not know where aircraft will be spawned and which route they follow.
|
||||
--
|
||||
-- ## Features
|
||||
-- ## Features:
|
||||
--
|
||||
-- * Very simple interface. Just one unit and two lines of Lua code needed to fill your map.
|
||||
-- * High degree of randomization. Aircraft will spawn at random airports, have random routes and random destinations.
|
||||
-- * Specific departure and/or destination airports can be chosen.
|
||||
-- * Departure and destination airports can be restricted by coalition.
|
||||
-- * Planes and helicopters supported. Helicopters can also be send to FARPs and ships.
|
||||
-- * Units can also be spawned in air within pre-defined zones of the map.
|
||||
-- * Aircraft will be removed when they arrive at their destination (or get stuck on the ground).
|
||||
-- * When a unit is removed a new unit with a different flight plan is respawned.
|
||||
-- * Aircraft can report their status during the route.
|
||||
-- * All of the above can be customized by the user if necessary.
|
||||
-- * All current (Caucasus, Nevada, Normandy, Persian Gulf) and future maps are supported.
|
||||
-- * Very simple interface. Just one unit and two lines of Lua code needed to fill your map.
|
||||
-- * High degree of randomization. Aircraft will spawn at random airports, have random routes and random destinations.
|
||||
-- * Specific departure and/or destination airports can be chosen.
|
||||
-- * Departure and destination airports can be restricted by coalition.
|
||||
-- * Planes and helicopters supported. Helicopters can also be send to FARPs and ships.
|
||||
-- * Units can also be spawned in air within pre-defined zones of the map.
|
||||
-- * Aircraft will be removed when they arrive at their destination (or get stuck on the ground).
|
||||
-- * When a unit is removed a new unit with a different flight plan is respawned.
|
||||
-- * Aircraft can report their status during the route.
|
||||
-- * All of the above can be customized by the user if necessary.
|
||||
-- * All current (Caucasus, Nevada, Normandy, Persian Gulf) and future maps are supported.
|
||||
--
|
||||
-- The RAT class creates an entry in the F10 radio menu which allows to
|
||||
-- The RAT class creates an entry in the F10 radio menu which allows to:
|
||||
--
|
||||
-- * Create new groups on-the-fly, i.e. at run time within the mission,
|
||||
-- * Destroy specific groups (e.g. if they get stuck or damaged and block a runway),
|
||||
-- * Request the status of all RAT aircraft or individual groups,
|
||||
-- * Place markers at waypoints on the F10 map for each group.
|
||||
-- * Create new groups on-the-fly, i.e. at run time within the mission,
|
||||
-- * Destroy specific groups (e.g. if they get stuck or damaged and block a runway),
|
||||
-- * Request the status of all RAT aircraft or individual groups,
|
||||
-- * Place markers at waypoints on the F10 map for each group.
|
||||
--
|
||||
-- Note that by its very nature, this class is suited best for civil or transport aircraft. However, it also works perfectly fine for military aircraft of any kind.
|
||||
--
|
||||
@@ -35,10 +33,9 @@
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # Demo Missions
|
||||
-- ## Missions:
|
||||
--
|
||||
-- ### [MOOSE - ALL Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS)
|
||||
-- ### [MOOSE - RAT Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/RAT%20-%20Random%20Air%20Traffic)
|
||||
-- ### [RAT - Random Air Traffic](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/RAT%20-%20Random%20Air%20Traffic)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -153,6 +150,8 @@
|
||||
-- @field #number parkingscanradius Radius in meters until which parking spots are scanned for obstacles like other units, statics or scenery.
|
||||
-- @field #boolean parkingscanscenery If true, area around parking spots is scanned for scenery objects. Default is false.
|
||||
-- @field #boolean parkingverysafe If true, parking spots are considered as non-free until a possible aircraft has left and taken off. Default false.
|
||||
-- @field #boolean despawnair If true, aircraft are despawned when they reach their destination zone. Default.
|
||||
-- @field #boolean eplrs If true, turn on EPLSR datalink for the RAT group.
|
||||
-- @extends Core.Spawn#SPAWN
|
||||
|
||||
--- Implements an easy to use way to randomly fill your map with AI aircraft.
|
||||
@@ -243,7 +242,7 @@
|
||||
-- * AIRBASE.TerminalType.OpenMed: Open/Shelter air airplane only.
|
||||
-- * AIRBASE.TerminalType.OpenBig: Open air spawn points. Generally larger but does not guarantee large aircraft are capable of spawning there.
|
||||
-- * AIRBASE.TerminalType.OpenMedOrBig: Combines OpenMed and OpenBig spots.
|
||||
-- * AIRBASE.TerminalType.HelicopterUnsable: Combines HelicopterOnly, OpenMed and OpenBig.
|
||||
-- * AIRBASE.TerminalType.HelicopterUsable: Combines HelicopterOnly, OpenMed and OpenBig.
|
||||
-- * AIRBASE.TerminalType.FighterAircraft: Combines Shelter, OpenMed and OpenBig spots. So effectively all spots usable by fixed wing aircraft.
|
||||
--
|
||||
-- So for example
|
||||
@@ -431,6 +430,8 @@ RAT={
|
||||
parkingscanradius=40, -- Scan radius.
|
||||
parkingscanscenery=false, -- Scan parking spots for scenery obstacles.
|
||||
parkingverysafe=false, -- Very safe option.
|
||||
despawnair=true,
|
||||
eplrs=false,
|
||||
}
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -549,7 +550,7 @@ RAT.id="RAT | "
|
||||
--- RAT version.
|
||||
-- @list version
|
||||
RAT.version={
|
||||
version = "2.3.4",
|
||||
version = "2.3.9",
|
||||
print = true,
|
||||
}
|
||||
|
||||
@@ -720,6 +721,11 @@ function RAT:Spawn(naircraft)
|
||||
self.FLcruise=005*RAT.unit.FL2m
|
||||
end
|
||||
end
|
||||
|
||||
-- Enable helos to go to destinations 100 meters away.
|
||||
if self.category==RAT.cat.heli then
|
||||
self.mindist=50
|
||||
end
|
||||
|
||||
-- Run consistency checks.
|
||||
self:_CheckConsistency()
|
||||
@@ -1096,6 +1102,14 @@ function RAT:SetParkingSpotSafeOFF()
|
||||
return self
|
||||
end
|
||||
|
||||
--- Aircraft that reach their destination zone are not despawned. They will probably go the the nearest airbase and try to land.
|
||||
-- @param #RAT self
|
||||
-- @return #RAT RAT self object.
|
||||
function RAT:SetDespawnAirOFF()
|
||||
self.despawnair=false
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set takeoff type. Starting cold at airport, starting hot at airport, starting at runway, starting in the air.
|
||||
-- Default is "takeoff-coldorhot". So there is a 50% chance that the aircraft starts with cold engines and 50% that it starts with hot engines.
|
||||
-- @param #RAT self
|
||||
@@ -1630,6 +1644,19 @@ function RAT:Invisible()
|
||||
return self
|
||||
end
|
||||
|
||||
--- Turn EPLRS datalink on/off.
|
||||
-- @param #RAT self
|
||||
-- @param #boolean switch If true (or nil), turn EPLRS on.
|
||||
-- @return #RAT RAT self object.
|
||||
function RAT:SetEPLRS(switch)
|
||||
if switch==nil or switch==true then
|
||||
self.eplrs=true
|
||||
else
|
||||
self.eplrs=false
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Aircraft are immortal.
|
||||
-- @param #RAT self
|
||||
-- @return #RAT RAT self object.
|
||||
@@ -1815,14 +1842,14 @@ function RAT:ATC_Delay(time)
|
||||
end
|
||||
|
||||
--- Set minimum distance between departure and destination. Default is 5 km.
|
||||
-- Minimum distance should not be smaller than maybe ~500 meters to ensure that departure and destination are different.
|
||||
-- Minimum distance should not be smaller than maybe ~100 meters to ensure that departure and destination are different.
|
||||
-- @param #RAT self
|
||||
-- @param #number dist Distance in km.
|
||||
-- @return #RAT RAT self object.
|
||||
function RAT:SetMinDistance(dist)
|
||||
self:F2(dist)
|
||||
-- Distance in meters. Absolute minimum is 500 m.
|
||||
self.mindist=math.max(500, dist*1000)
|
||||
self.mindist=math.max(100, dist*1000)
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -2152,6 +2179,11 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live
|
||||
self:_CommandImmortal(group, true)
|
||||
end
|
||||
|
||||
-- Set group to be immortal.
|
||||
if self.eplrs then
|
||||
group:CommandEPLRS(true, 1)
|
||||
end
|
||||
|
||||
-- Set ROE, default is "weapon hold".
|
||||
self:_SetROE(group, self.roe)
|
||||
|
||||
@@ -2449,7 +2481,7 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination, _waypoint)
|
||||
local VxCruiseMax
|
||||
if self.Vcruisemax then
|
||||
-- User input.
|
||||
VxCruiseMax = min(self.Vcruisemax, self.aircraft.Vmax)
|
||||
VxCruiseMax = math.min(self.Vcruisemax, self.aircraft.Vmax)
|
||||
else
|
||||
-- Max cruise speed 90% of Vmax or 900 km/h whichever is lower.
|
||||
VxCruiseMax = math.min(self.aircraft.Vmax*0.90, 250)
|
||||
@@ -3360,11 +3392,19 @@ function RAT:_GetAirportsOfMap()
|
||||
local _name=airbase:getName()
|
||||
local _myab=AIRBASE:FindByName(_name)
|
||||
|
||||
-- Add airport to table.
|
||||
table.insert(self.airports_map, _myab)
|
||||
if _myab then
|
||||
|
||||
local text="MOOSE: Airport ID = ".._myab:GetID().." and Name = ".._myab:GetName()..", Category = ".._myab:GetCategory()..", TypeName = ".._myab:GetTypeName()
|
||||
self:T(RAT.id..text)
|
||||
-- Add airport to table.
|
||||
table.insert(self.airports_map, _myab)
|
||||
|
||||
local text="MOOSE: Airport ID = ".._myab:GetID().." and Name = ".._myab:GetName()..", Category = ".._myab:GetCategory()..", TypeName = ".._myab:GetTypeName()
|
||||
self:T(RAT.id..text)
|
||||
|
||||
else
|
||||
|
||||
self:E(RAT.id..string.format("WARNING: Airbase %s does not exsist as MOOSE object!", tostring(_name)))
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -3376,7 +3416,7 @@ function RAT:_GetAirportsOfCoalition()
|
||||
for _,coalition in pairs(self.ctable) do
|
||||
for _,_airport in pairs(self.airports_map) do
|
||||
local airport=_airport --Wrapper.Airbase#AIRBASE
|
||||
local category=airport:GetDesc().category
|
||||
local category=airport:GetAirbaseCategory()
|
||||
if airport:GetCoalition()==coalition then
|
||||
-- Planes cannot land on FARPs.
|
||||
--local condition1=self.category==RAT.cat.plane and airport:GetTypeName()=="FARP"
|
||||
@@ -3602,13 +3642,18 @@ function RAT:Status(message, forID)
|
||||
|
||||
local text=string.format("Flight %s will be despawned NOW!", self.alias)
|
||||
self:T(RAT.id..text)
|
||||
-- Despawn old group.
|
||||
|
||||
-- Respawn group
|
||||
if (not self.norespawn) and (not self.respawn_after_takeoff) then
|
||||
local idx=self:GetSpawnIndexFromGroup(group)
|
||||
local coord=group:GetCoordinate()
|
||||
self:_Respawn(idx, coord, 0)
|
||||
end
|
||||
self:_Despawn(group, 0)
|
||||
|
||||
-- Despawn old group.
|
||||
if self.despawnair then
|
||||
self:_Despawn(group, 0)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -3802,7 +3847,7 @@ function RAT:_OnBirth(EventData)
|
||||
|
||||
-- Check if any unit of the group was spawned on top of another unit in the MOOSE data base.
|
||||
local ontop=false
|
||||
if self.checkontop and (_airbase and _airbase:GetDesc().category==Airbase.Category.AIRDROME) then
|
||||
if self.checkontop and (_airbase and _airbase:GetAirbaseCategory()==Airbase.Category.AIRDROME) then
|
||||
ontop=self:_CheckOnTop(SpawnGroup, self.ontopradius)
|
||||
end
|
||||
|
||||
@@ -4412,7 +4457,7 @@ function RAT:_Waypoint(index, description, Type, Coord, Speed, Altitude, Airport
|
||||
|
||||
if (Airport~=nil) and (Type~=RAT.wp.air) then
|
||||
local AirbaseID = Airport:GetID()
|
||||
local AirbaseCategory = Airport:GetDesc().category
|
||||
local AirbaseCategory = Airport:GetAirbaseCategory()
|
||||
if AirbaseCategory == Airbase.Category.SHIP then
|
||||
RoutePoint.linkUnit = AirbaseID
|
||||
RoutePoint.helipadId = AirbaseID
|
||||
@@ -5096,7 +5141,7 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take
|
||||
local spawnonrunway=false
|
||||
local spawnonairport=false
|
||||
if spawnonground then
|
||||
local AirbaseCategory = departure:GetDesc().category
|
||||
local AirbaseCategory = departure:GetAirbaseCategory()
|
||||
if AirbaseCategory == Airbase.Category.SHIP then
|
||||
spawnonship=true
|
||||
elseif AirbaseCategory == Airbase.Category.HELIPAD then
|
||||
@@ -5128,6 +5173,7 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take
|
||||
if self.uncontrolled then
|
||||
-- This is used in the SPAWN:SpawnWithIndex() function. Some values are overwritten there!
|
||||
self.SpawnUnControlled=true
|
||||
SpawnTemplate.uncontrolled=true
|
||||
end
|
||||
|
||||
-- Number of units in the group. With grouping this can actually differ from the template group size!
|
||||
@@ -5438,7 +5484,7 @@ function RAT:_ATCInit(airports_map)
|
||||
if not RAT.ATC.init then
|
||||
local text
|
||||
text="Starting RAT ATC.\nSimultanious = "..RAT.ATC.Nclearance.."\n".."Delay = "..RAT.ATC.delay
|
||||
self:T(RAT.id..text)
|
||||
BASE:T(RAT.id..text)
|
||||
RAT.ATC.init=true
|
||||
for _,ap in pairs(airports_map) do
|
||||
local name=ap:GetName()
|
||||
@@ -5461,7 +5507,7 @@ end
|
||||
-- @param #string name Group name of the flight.
|
||||
-- @param #string dest Name of the destination airport.
|
||||
function RAT:_ATCAddFlight(name, dest)
|
||||
self:T(string.format("%sATC %s: Adding flight %s with destination %s.", RAT.id, dest, name, dest))
|
||||
BASE:T(string.format("%sATC %s: Adding flight %s with destination %s.", RAT.id, dest, name, dest))
|
||||
RAT.ATC.flight[name]={}
|
||||
RAT.ATC.flight[name].destination=dest
|
||||
RAT.ATC.flight[name].Tarrive=-1
|
||||
@@ -5486,7 +5532,7 @@ end
|
||||
-- @param #string name Group name of the flight.
|
||||
-- @param #number time Time the fight first registered.
|
||||
function RAT:_ATCRegisterFlight(name, time)
|
||||
self:T(RAT.id.."Flight ".. name.." registered at ATC for landing clearance.")
|
||||
BASE:T(RAT.id.."Flight ".. name.." registered at ATC for landing clearance.")
|
||||
RAT.ATC.flight[name].Tarrive=time
|
||||
RAT.ATC.flight[name].holding=0
|
||||
end
|
||||
@@ -5517,7 +5563,7 @@ function RAT:_ATCStatus()
|
||||
|
||||
-- Aircraft is holding.
|
||||
local text=string.format("ATC %s: Flight %s is holding for %i:%02d. %s.", dest, name, hold/60, hold%60, busy)
|
||||
self:T(RAT.id..text)
|
||||
BASE:T(RAT.id..text)
|
||||
|
||||
elseif hold==RAT.ATC.onfinal then
|
||||
|
||||
@@ -5525,7 +5571,7 @@ function RAT:_ATCStatus()
|
||||
local Tfinal=Tnow-RAT.ATC.flight[name].Tonfinal
|
||||
|
||||
local text=string.format("ATC %s: Flight %s is on final. Waiting %i:%02d for landing event.", dest, name, Tfinal/60, Tfinal%60)
|
||||
self:T(RAT.id..text)
|
||||
BASE:T(RAT.id..text)
|
||||
|
||||
elseif hold==RAT.ATC.unregistered then
|
||||
|
||||
@@ -5533,7 +5579,7 @@ function RAT:_ATCStatus()
|
||||
--self:T(string.format("ATC %s: Flight %s is not registered yet (hold %d).", dest, name, hold))
|
||||
|
||||
else
|
||||
self:E(RAT.id.."ERROR: Unknown holding time in RAT:_ATCStatus().")
|
||||
BASE:E(RAT.id.."ERROR: Unknown holding time in RAT:_ATCStatus().")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -5575,12 +5621,12 @@ function RAT:_ATCCheck()
|
||||
|
||||
-- Debug message.
|
||||
local text=string.format("ATC %s: Flight %s runway is busy. You are #%d of %d in landing queue. Your holding time is %i:%02d.", name, flight,qID, nqueue, RAT.ATC.flight[flight].holding/60, RAT.ATC.flight[flight].holding%60)
|
||||
self:T(RAT.id..text)
|
||||
BASE:T(RAT.id..text)
|
||||
|
||||
else
|
||||
|
||||
local text=string.format("ATC %s: Flight %s was cleared for landing. Your holding time was %i:%02d.", name, flight, RAT.ATC.flight[flight].holding/60, RAT.ATC.flight[flight].holding%60)
|
||||
self:T(RAT.id..text)
|
||||
BASE:T(RAT.id..text)
|
||||
|
||||
-- Clear flight for landing.
|
||||
RAT:_ATCClearForLanding(name, flight)
|
||||
@@ -5708,12 +5754,7 @@ function RAT:_ATCQueue()
|
||||
for k,v in ipairs(_queue) do
|
||||
table.insert(RAT.ATC.airport[airport].queue, v[1])
|
||||
end
|
||||
|
||||
--fvh
|
||||
--for k,v in ipairs(RAT.ATC.airport[airport].queue) do
|
||||
--print(string.format("queue #%02i flight \"%s\" holding %d seconds",k, v, RAT.ATC.flight[v].holding))
|
||||
--end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,25 @@
|
||||
--- **Functional** -- (R2.0) - Administer the scoring of player achievements, and create a CSV file logging the scoring events for use at team or squadron websites.
|
||||
--- **Functional** - Administer the scoring of player achievements, and create a CSV file logging the scoring events for use at team or squadron websites.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
-- * Set the scoring scales based on threat level.
|
||||
-- * Positive scores and negative scores.
|
||||
-- * A contribution model to score achievements.
|
||||
-- * Score goals.
|
||||
-- * Score specific achievements.
|
||||
-- * Score the hits and destroys of units.
|
||||
-- * Score the hits and destroys of statics.
|
||||
-- * Score the hits and destroys of scenery.
|
||||
-- * Log scores into a CSV file.
|
||||
-- * Connect to a remote server using JSON and IP.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Missions:
|
||||
--
|
||||
-- [SCO - Scoring](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SCO%20-%20Scoring)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -52,9 +73,34 @@
|
||||
-- Use the radio menu F10 to consult the scores while running the mission.
|
||||
-- Scores can be reported for your user, or an overall score can be reported of all players currently active in the mission.
|
||||
--
|
||||
-- # 1) @{Functional.Scoring#SCORING} class, extends @{Core.Base#BASE}
|
||||
-- ===
|
||||
--
|
||||
-- ## 1.1) Set the destroy score or penalty scale
|
||||
-- ### Authors: **FlightControl**
|
||||
--
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- * **Wingthor (TAW)**: Testing & Advice.
|
||||
-- * **Dutch-Baron (TAW)**: Testing & Advice.
|
||||
-- * **[Whisper](http://forums.eagle.ru/member.php?u=3829): Testing and Advice.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module Functional.Scoring
|
||||
-- @image Scoring.JPG
|
||||
|
||||
|
||||
--- @type SCORING
|
||||
-- @field Players A collection of the current players that have joined the game.
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
--- SCORING class
|
||||
--
|
||||
-- # Constructor:
|
||||
--
|
||||
-- local Scoring = SCORING:New( "Scoring File" )
|
||||
--
|
||||
--
|
||||
-- # Set the destroy score or penalty scale:
|
||||
--
|
||||
-- Score scales can be set for scores granted when enemies or friendlies are destroyed.
|
||||
-- Use the method @{#SCORING.SetScaleDestroyScore}() to set the scale of enemy destroys (positive destroys).
|
||||
@@ -67,7 +113,7 @@
|
||||
-- The above sets the scale for valid scores to 10. So scores will be given in a scale from 0 to 10.
|
||||
-- The penalties will be given in a scale from 0 to 40.
|
||||
--
|
||||
-- ## 1.2) Define special targets that will give extra scores.
|
||||
-- # Define special targets that will give extra scores:
|
||||
--
|
||||
-- Special targets can be set that will give extra scores to the players when these are destroyed.
|
||||
-- Use the methods @{#SCORING.AddUnitScore}() and @{#SCORING.RemoveUnitScore}() to specify a special additional score for a specific @{Wrapper.Unit}s.
|
||||
@@ -84,7 +130,7 @@
|
||||
--
|
||||
-- Scoring:RemoveUnitScore( UNIT:FindByName( "Unit #001" ) )
|
||||
--
|
||||
-- ## 1.3) Define destruction zones that will give extra scores.
|
||||
-- # Define destruction zones that will give extra scores:
|
||||
--
|
||||
-- Define zones of destruction. Any object destroyed within the zone of the given category will give extra points.
|
||||
-- Use the method @{#SCORING.AddZoneScore}() to add a @{Zone} for additional scoring.
|
||||
@@ -94,12 +140,12 @@
|
||||
-- The other implementation could be to designate a scenery target (a building) in the mission editor surrounded by a @{Zone},
|
||||
-- just large enough around that building.
|
||||
--
|
||||
-- ## 1.4) Add extra Goal scores upon an event or a condition.
|
||||
-- # Add extra Goal scores upon an event or a condition:
|
||||
--
|
||||
-- A mission has goals and achievements. The scoring system provides an API to set additional scores when a goal or achievement event happens.
|
||||
-- Use the method @{#SCORING.AddGoalScore}() to add a score for a Player at any time in your mission.
|
||||
--
|
||||
-- ## 1.5) (Decommissioned) Configure fratricide level.
|
||||
-- # (Decommissioned) Configure fratricide level.
|
||||
--
|
||||
-- **This functionality is decomissioned until the DCS bug concerning Unit:destroy() not being functional in multi player for player units has been fixed by ED**.
|
||||
--
|
||||
@@ -107,13 +153,13 @@
|
||||
-- Use the method @{#SCORING.SetFratricide}() to define the level when a player gets kicked.
|
||||
-- By default, the fratricide level is the default penalty mutiplier * 2 for the penalty score.
|
||||
--
|
||||
-- ## 1.6) Penalty score when a player changes the coalition.
|
||||
-- # Penalty score when a player changes the coalition.
|
||||
--
|
||||
-- When a player changes the coalition, he can receive a penalty score.
|
||||
-- Use the method @{#SCORING.SetCoalitionChangePenalty}() to define the penalty when a player changes coalition.
|
||||
-- By default, the penalty for changing coalition is the default penalty scale.
|
||||
--
|
||||
-- ## 1.8) Define output CSV files.
|
||||
-- # Define output CSV files.
|
||||
--
|
||||
-- The CSV file is given the name of the string given in the @{#SCORING.New}{} constructor, followed by the .csv extension.
|
||||
-- The file is incrementally saved in the **<User>\\Saved Games\\DCS\\Logs** folder, and has a time stamp indicating each mission run.
|
||||
@@ -150,7 +196,7 @@
|
||||
-- The MOOSE designer cannot take any responsibility of any damage inflicted as a result of the de-sanitization.
|
||||
-- That being said, I hope that the SCORING class provides you with a great add-on to score your squad mates achievements.
|
||||
--
|
||||
-- ## 1.9) Configure messages.
|
||||
-- # Configure messages.
|
||||
--
|
||||
-- When players hit or destroy targets, messages are sent.
|
||||
-- Various methods exist to configure:
|
||||
@@ -158,7 +204,7 @@
|
||||
-- * Which messages are sent upon the event.
|
||||
-- * Which audience receives the message.
|
||||
--
|
||||
-- ### 1.9.1) Configure the messages sent upon the event.
|
||||
-- ## Configure the messages sent upon the event.
|
||||
--
|
||||
-- Use the following methods to configure when to send messages. By default, all messages are sent.
|
||||
--
|
||||
@@ -167,49 +213,16 @@
|
||||
-- * @{#SCORING.SetMessagesAddon}(): Configure to send messages for additional score, after a target has been destroyed.
|
||||
-- * @{#SCORING.SetMessagesZone}(): Configure to send messages for additional score, after a target has been destroyed within a given zone.
|
||||
--
|
||||
-- ### 1.9.2) Configure the audience of the messages.
|
||||
-- ## Configure the audience of the messages.
|
||||
--
|
||||
-- Use the following methods to configure the audience of the messages. By default, the messages are sent to all players in the mission.
|
||||
--
|
||||
-- * @{#SCORING.SetMessagesToAll}(): Configure to send messages to all players.
|
||||
-- * @{#SCORING.SetMessagesToCoalition}(): Configure to send messages to only those players within the same coalition as the player.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # **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-26: Initial class and API.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # **AUTHORS and CONTRIBUTIONS**
|
||||
--
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- * **Wingthor (TAW)**: Testing & Advice.
|
||||
-- * **Dutch-Baron (TAW)**: Testing & Advice.
|
||||
-- * **[Whisper](http://forums.eagle.ru/member.php?u=3829): Testing and Advice.
|
||||
--
|
||||
-- ### Authors:
|
||||
--
|
||||
-- * **FlightControl**: Concept, Design & Programming.
|
||||
--
|
||||
-- @module Functional.Scoring
|
||||
-- @image Scoring.JPG
|
||||
|
||||
|
||||
--- The Scoring class
|
||||
-- @type SCORING
|
||||
-- @field Players A collection of the current players that have joined the game.
|
||||
-- @extends Core.Base#BASE
|
||||
-- @field #SCORING
|
||||
SCORING = {
|
||||
ClassName = "SCORING",
|
||||
ClassID = 0,
|
||||
@@ -236,8 +249,10 @@ local _SCORINGCategory =
|
||||
-- @param #string GameName The name of the game. This name is also logged in the CSV score file.
|
||||
-- @return #SCORING self
|
||||
-- @usage
|
||||
--
|
||||
-- -- Define a new scoring object for the mission Gori Valley.
|
||||
-- ScoringObject = SCORING:New( "Gori Valley" )
|
||||
--
|
||||
function SCORING:New( GameName )
|
||||
|
||||
-- Inherits from BASE
|
||||
@@ -1620,7 +1635,7 @@ function SCORING:ReportScoreGroupSummary( PlayerGroup )
|
||||
self:F( { ReportMissions, ScoreMissions, PenaltyMissions } )
|
||||
|
||||
local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions
|
||||
local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + ScoreGoals + PenaltyMissions
|
||||
local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + PenaltyGoals + PenaltyMissions
|
||||
|
||||
PlayerMessage =
|
||||
string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )",
|
||||
|
||||
@@ -1,13 +1,41 @@
|
||||
--- **Functional** -- Provides defensive behaviour to a set of SAM sites within a running Mission.
|
||||
--- **Functional** -- Make SAM sites execute evasive and defensive behaviour when being fired upon.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
-- * When SAM sites are being fired upon, the SAMs will take evasive action will reposition themselves when possible.
|
||||
-- * When SAM sites are being fired upon, the SAMs will take defensive action by shutting down their radars.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Missions:
|
||||
--
|
||||
-- [SEV - SEAD Evasion](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SEV%20-%20SEAD%20Evasion)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Authors: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module Functional.Sead
|
||||
-- @image SEAD.JPG
|
||||
|
||||
--- The SEAD class
|
||||
-- @type SEAD
|
||||
--- @type SEAD
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
--- Make SAM sites execute evasive and defensive behaviour when being fired upon.
|
||||
--
|
||||
-- This class is very easy to use. Just setup a SEAD object by using @{#SEAD.New}() and SAMs will evade and take defensive action when being fired upon.
|
||||
--
|
||||
-- # Constructor:
|
||||
--
|
||||
-- Use the @{#SEAD.New}() constructor to create a new SEAD object.
|
||||
--
|
||||
-- SEAD_RU_SAM_Defenses = SEAD:New( { 'RU SA-6 Kub', 'RU SA-6 Defenses', 'RU MI-26 Troops', 'RU Attack Gori' } )
|
||||
--
|
||||
-- @field #SEAD
|
||||
SEAD = {
|
||||
ClassName = "SEAD",
|
||||
TargetSkill = {
|
||||
@@ -29,8 +57,10 @@ SEAD = {
|
||||
-- -- Defends the Russian SA installations from SEAD attacks.
|
||||
-- SEAD_RU_SAM_Defenses = SEAD:New( { 'RU SA-6 Kub', 'RU SA-6 Defenses', 'RU MI-26 Troops', 'RU Attack Gori' } )
|
||||
function SEAD:New( SEADGroupPrefixes )
|
||||
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
self:F( SEADGroupPrefixes )
|
||||
self:F( SEADGroupPrefixes )
|
||||
|
||||
if type( SEADGroupPrefixes ) == 'table' then
|
||||
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
|
||||
self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix
|
||||
@@ -57,7 +87,29 @@ function SEAD:OnEventShot( EventData )
|
||||
local SEADWeaponName = EventData.WeaponName -- return weapon type
|
||||
-- Start of the 2nd loop
|
||||
self:T( "Missile Launched = " .. SEADWeaponName )
|
||||
if SEADWeaponName == "KH-58" or SEADWeaponName == "KH-25MPU" or SEADWeaponName == "AGM-88" or SEADWeaponName == "KH-31A" or SEADWeaponName == "KH-31P" then -- Check if the missile is a SEAD
|
||||
|
||||
--if SEADWeaponName == "KH-58" or SEADWeaponName == "KH-25MPU" or SEADWeaponName == "AGM-88" or SEADWeaponName == "KH-31A" or SEADWeaponName == "KH-31P" then -- Check if the missile is a SEAD
|
||||
if SEADWeaponName == "weapons.missiles.X_58" --Kh-58U anti-radiation missiles fired
|
||||
or
|
||||
SEADWeaponName == "weapons.missiles.Kh25MP_PRGS1VP" --Kh-25MP anti-radiation missiles fired
|
||||
or
|
||||
SEADWeaponName == "weapons.missiles.X_25MP" --Kh-25MPU anti-radiation missiles fired
|
||||
or
|
||||
SEADWeaponName == "weapons.missiles.X_28" --Kh-28 anti-radiation missiles fired
|
||||
or
|
||||
SEADWeaponName == "weapons.missiles.X_31P" --Kh-31P anti-radiation missiles fired
|
||||
or
|
||||
SEADWeaponName == "weapons.missiles.AGM_45A" --AGM-45A anti-radiation missiles fired
|
||||
or
|
||||
SEADWeaponName == "weapons.missiles.AGM_45" --AGM-45B anti-radiation missiles fired
|
||||
or
|
||||
SEADWeaponName == "weapons.missiles.AGM_88" --AGM-88C anti-radiation missiles fired
|
||||
or
|
||||
SEADWeaponName == "weapons.missiles.AGM_122" --AGM-122 Sidearm anti-radiation missiles fired
|
||||
or
|
||||
SEADWeaponName == "weapons.missiles.ALARM" --ALARM anti-radiation missiles fired
|
||||
then
|
||||
|
||||
local _evade = math.random (1,100) -- random number for chance of evading action
|
||||
local _targetMim = EventData.Weapon:getTarget() -- Identify target
|
||||
local _targetMimname = Unit.getName(_targetMim)
|
||||
@@ -83,47 +135,62 @@ function SEAD:OnEventShot( EventData )
|
||||
self:T( _targetskill )
|
||||
if self.TargetSkill[_targetskill] then
|
||||
if (_evade > self.TargetSkill[_targetskill].Evade) then
|
||||
|
||||
self:T( string.format("Evading, target skill " ..string.format(_targetskill)) )
|
||||
|
||||
local _targetMim = Weapon.getTarget(SEADWeapon)
|
||||
local _targetMimname = Unit.getName(_targetMim)
|
||||
local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon))
|
||||
local _targetMimcont= _targetMimgroup:getController()
|
||||
|
||||
routines.groupRandomDistSelf(_targetMimgroup,300,'Diamond',250,20) -- move randomly
|
||||
|
||||
local SuppressedGroups1 = {} -- unit suppressed radar off for a random time
|
||||
|
||||
local function SuppressionEnd1(id)
|
||||
id.ctrl:setOption(AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.GREEN)
|
||||
SuppressedGroups1[id.groupName] = nil
|
||||
end
|
||||
|
||||
local id = {
|
||||
groupName = _targetMimgroup,
|
||||
ctrl = _targetMimcont
|
||||
}
|
||||
|
||||
local delay1 = math.random(self.TargetSkill[_targetskill].DelayOff[1], self.TargetSkill[_targetskill].DelayOff[2])
|
||||
|
||||
if SuppressedGroups1[id.groupName] == nil then
|
||||
|
||||
SuppressedGroups1[id.groupName] = {
|
||||
SuppressionEndTime1 = timer.getTime() + delay1,
|
||||
SuppressionEndN1 = SuppressionEndCounter1 --Store instance of SuppressionEnd() scheduled function
|
||||
}
|
||||
}
|
||||
|
||||
Controller.setOption(_targetMimcont, AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.GREEN)
|
||||
timer.scheduleFunction(SuppressionEnd1, id, SuppressedGroups1[id.groupName].SuppressionEndTime1) --Schedule the SuppressionEnd() function
|
||||
--trigger.action.outText( string.format("Radar Off " ..string.format(delay1)), 20)
|
||||
end
|
||||
|
||||
local SuppressedGroups = {}
|
||||
|
||||
local function SuppressionEnd(id)
|
||||
id.ctrl:setOption(AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.RED)
|
||||
SuppressedGroups[id.groupName] = nil
|
||||
end
|
||||
|
||||
local id = {
|
||||
groupName = _targetMimgroup,
|
||||
ctrl = _targetMimcont
|
||||
}
|
||||
|
||||
local delay = math.random(self.TargetSkill[_targetskill].DelayOn[1], self.TargetSkill[_targetskill].DelayOn[2])
|
||||
|
||||
if SuppressedGroups[id.groupName] == nil then
|
||||
SuppressedGroups[id.groupName] = {
|
||||
SuppressionEndTime = timer.getTime() + delay,
|
||||
SuppressionEndN = SuppressionEndCounter --Store instance of SuppressionEnd() scheduled function
|
||||
}
|
||||
|
||||
timer.scheduleFunction(SuppressionEnd, id, SuppressedGroups[id.groupName].SuppressionEndTime) --Schedule the SuppressionEnd() function
|
||||
--trigger.action.outText( string.format("Radar On " ..string.format(delay)), 20)
|
||||
end
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,19 +1,45 @@
|
||||
--- **Functional** -- (R2.3) Models the process to zone guarding and capturing.
|
||||
--- **Functional** -- Models the process to zone guarding and capturing.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/CAZ - Capture Zones)
|
||||
-- ## Features:
|
||||
--
|
||||
-- - CAZ-000 - Capture Zone: Demonstrates the basic concept of capturing a zone.
|
||||
-- * Models the possible state transitions between the Guarded, Attacked, Empty and Captured states.
|
||||
-- * A zone has an owning coalition, that means that at a specific point in time, a zone can be owned by the red or blue coalition.
|
||||
-- * Provide event handlers to tailor the actions when a zone changes coalition or state.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### [YouTube Playlist](https://www.youtube.com/watch?v=0m6K6Yxa-os&list=PL7ZUrU4zZUl0qqJsfa8DPvZWDY-OyDumE)
|
||||
-- ## Missions:
|
||||
--
|
||||
-- [CAZ - Capture Zones](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/CAZ%20-%20Capture%20Zones)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # Player Experience
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- The above models the possible state transitions between the **Guarded**, **Attacked**, **Empty** and **Captured** states.
|
||||
-- A zone has an __owning coalition__, that means that at a specific point in time, a zone can be owned by the red or blue coalition.
|
||||
--
|
||||
-- The Zone can be in the state **Guarded** by the __owning coalition__, which is the coalition that initially occupies the zone with units of its coalition.
|
||||
-- Once units of an other coalition are entering the Zone, the state will change to **Attacked**. As long as these units remain in the zone, the state keeps set to Attacked.
|
||||
-- When all units are destroyed in the Zone, the state will change to **Empty**, which expresses that the Zone is empty, and can be captured.
|
||||
-- When units of the other coalition are in the Zone, and no other units of the owning coalition is in the Zone, the Zone is captured, and its state will change to **Captured**.
|
||||
--
|
||||
-- The zone needs to be monitored regularly for the presence of units to interprete the correct state transition required.
|
||||
-- This monitoring process MUST be started using the @{#ZONE_CAPTURE_COALITION.Start}() method.
|
||||
-- Otherwise no monitoring will be active and the zone will stay in the current state forever.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## [YouTube Playlist](https://www.youtube.com/watch?v=0m6K6Yxa-os&list=PL7ZUrU4zZUl0qqJsfa8DPvZWDY-OyDumE)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions: **Millertime** - Concept
|
||||
-- ### Contributions: **Millertime** - Concept, **funkyfranky**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -23,71 +49,91 @@
|
||||
do -- ZONE_CAPTURE_COALITION
|
||||
|
||||
--- @type ZONE_CAPTURE_COALITION
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @field #number MarkBlue ID of blue F10 mark.
|
||||
-- @field #number MarkRed ID of red F10 mark.
|
||||
-- @field #number StartInterval Time in seconds after the status monitor is started.
|
||||
-- @field #number RepeatInterval Time in seconds after which the zone status is updated.
|
||||
-- @field #boolean HitsOn If true, hit events are monitored and trigger the "Attack" event when a defending unit is hit.
|
||||
-- @field #number HitTimeLast Time stamp in seconds when the last unit inside the zone was hit.
|
||||
-- @field #number HitTimeAttackOver Time interval in seconds before the zone goes from "Attacked" to "Guarded" state after the last hit.
|
||||
-- @field #boolean MarkOn If true, create marks of zone status on F10 map.
|
||||
-- @extends Functional.ZoneGoalCoalition#ZONE_GOAL_COALITION
|
||||
|
||||
|
||||
--- Models the process to capture a Zone for a Coalition, which is guarded by another Coalition.
|
||||
-- This is a powerful concept that allows to create very dynamic missions based on the different state transitions of various zones.
|
||||
--
|
||||
-- ---
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ---
|
||||
-- ===
|
||||
--
|
||||
-- # 0. Player Experience
|
||||
-- In order to use ZONE_CAPTURE_COALITION, you need to:
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- The above models the possible state transitions between the **Guarded**, **Attacked**, **Empty** and **Captured** states.
|
||||
-- A zone has an __owning coalition__, that means that at a specific point in time, a zone can be owned by the red or blue coalition.
|
||||
--
|
||||
-- The Zone can be in the state **Guarded** by the __owning coalition__, which is the coalition that initially occupies the zone with units of its coalition.
|
||||
-- Once units of an other coalition are entering the Zone, the state will change to **Attacked**. As long as these units remain in the zone, the state keeps set to Attacked.
|
||||
-- When all units are destroyed in the Zone, the state will change to **Empty**, which expresses that the Zone is empty, and can be captured.
|
||||
-- When units of the other coalition are in the Zone, and no other units of the owning coalition is in the Zone, the Zone is captured, and its state will change to **Captured**.
|
||||
--
|
||||
-- The zone needs to be monitored regularly for the presence of units to interprete the correct state transition required.
|
||||
-- This monitoring process MUST be started using the @{#ZONE_CAPTURE_COALITION.Start}() method.
|
||||
-- Otherwise no monitoring will be active and the zone will stay in the current state forever.
|
||||
-- See further in chapter 3.3 for more information about this.
|
||||
--
|
||||
-- ## 1. ZONE\_CAPTURE\_COALITION constructor
|
||||
--
|
||||
-- * @{#ZONE_CAPTURE_COALITION.New}(): Creates a new ZONE\_CAPTURE\_COALITION object.
|
||||
--
|
||||
-- In order to use ZONE\_CAPTURE\_COALITION, you need to:
|
||||
--
|
||||
-- - Create a @{Zone} object from one of the ZONE\_ classes. Note that ZONE\_POLYGON\_ classes are not yet functional. The only functional ZONE\_ classses are those derived from a ZONE\_RADIUS.
|
||||
-- * Create a @{Zone} object from one of the ZONE_ classes.
|
||||
-- Note that ZONE_POLYGON_ classes are not yet functional.
|
||||
-- The only functional ZONE_ classses are those derived from a ZONE_RADIUS.
|
||||
-- * Set the state of the zone. Most of the time, Guarded would be the initial state.
|
||||
-- * Start the zone capturing **monitoring process**.
|
||||
-- This will check the presence of friendly and/or enemy units within the zone and will transition the state of the zone when the tactical situation changed.
|
||||
-- The frequency of the monitoring must not be real-time, a 30 second interval to execute the checks is sufficient.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Ensure that during the life cycle of the ZONE\_CAPTURE\_COALITION object, the object keeps alive.
|
||||
-- It is best to declare the object globally within your script.
|
||||
-- ### Important:
|
||||
--
|
||||
-- ## 2. ZONE\_CAPTURE\_COALITION is a finite state machine (FSM).
|
||||
-- You must start the monitoring process within your code, or there won't be any state transition checks executed.
|
||||
-- See further the start/stop monitoring process.
|
||||
--
|
||||
-- ### Important:
|
||||
--
|
||||
-- Ensure that the object containing the ZONE_CAPTURE_COALITION object is persistent.
|
||||
-- Otherwise the garbage collector of lua will remove the object and the monitoring process will stop.
|
||||
-- This will result in your object to be destroyed (removed) from internal memory and there won't be any zone state transitions anymore detected!
|
||||
-- So use the `local` keyword in lua with thought! Most of the time, you can declare your object gobally.
|
||||
--
|
||||
--
|
||||
--
|
||||
-- # Example:
|
||||
--
|
||||
-- -- Define a new ZONE object, which is based on the trigger zone `CaptureZone`, which is defined within the mission editor.
|
||||
-- CaptureZone = ZONE:New( "CaptureZone" )
|
||||
--
|
||||
-- -- Here we create a new ZONE_CAPTURE_COALITION object, using the :New constructor.
|
||||
-- ZoneCaptureCoalition = ZONE_CAPTURE_COALITION:New( CaptureZone, coalition.side.RED )
|
||||
--
|
||||
-- -- Set the zone to Guarding state.
|
||||
-- ZoneCaptureCoalition:__Guard( 1 )
|
||||
--
|
||||
-- -- Start the zone monitoring process in 30 seconds and check every 30 seconds.
|
||||
-- ZoneCaptureCoalition:Start( 30, 30 )
|
||||
--
|
||||
--
|
||||
-- # Constructor:
|
||||
--
|
||||
-- Use the @{#ZONE_CAPTURE_COALITION.New}() constructor to create a new ZONE_CAPTURE_COALITION object.
|
||||
--
|
||||
-- # ZONE_CAPTURE_COALITION is a finite state machine (FSM).
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ### 2.1 ZONE\_CAPTURE\_COALITION States
|
||||
-- ## ZONE_CAPTURE_COALITION States
|
||||
--
|
||||
-- * **Captured**: The Zone has been captured by an other coalition.
|
||||
-- * **Attacked**: The Zone is currently intruded by an other coalition. There are units of the owning coalition and an other coalition in the Zone.
|
||||
-- * **Guarded**: The Zone is guarded by the owning coalition. There is no other unit of an other coalition in the Zone.
|
||||
-- * **Empty**: The Zone is empty. There is not valid unit in the Zone.
|
||||
--
|
||||
-- ### 2.2 ZONE\_CAPTURE\_COALITION Events
|
||||
-- ## 2.2 ZONE_CAPTURE_COALITION Events
|
||||
--
|
||||
-- * **Capture**: The Zone has been captured by an other coalition.
|
||||
-- * **Attack**: The Zone is currently intruded by an other coalition. There are units of the owning coalition and an other coalition in the Zone.
|
||||
-- * **Guard**: The Zone is guarded by the owning coalition. There is no other unit of an other coalition in the Zone.
|
||||
-- * **Empty**: The Zone is empty. There is not valid unit in the Zone.
|
||||
--
|
||||
-- ## 3. "Script It"
|
||||
-- # "Script It"
|
||||
--
|
||||
-- ZONE\_CAPTURE\_COALITION allows to take action on the various state transitions and add your custom code and logic.
|
||||
-- ZONE_CAPTURE_COALITION allows to take action on the various state transitions and add your custom code and logic.
|
||||
--
|
||||
-- ### 3.1. Take action using state- and event handlers.
|
||||
-- ## Take action using state- and event handlers.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
@@ -104,8 +150,6 @@ do -- ZONE_CAPTURE_COALITION
|
||||
-- - On Before the event is triggered. Return false to cancel the transition.
|
||||
-- - On After the event is triggered.
|
||||
--
|
||||
--
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- Each handler can receive optionally 3 parameters:
|
||||
@@ -126,7 +170,7 @@ do -- ZONE_CAPTURE_COALITION
|
||||
--
|
||||
-- This code checks that when the __Guarded__ state has been reached, that if the **From** state was __Empty__, then display a message.
|
||||
--
|
||||
-- ### 3.2. Example Event Handler.
|
||||
-- ## Example Event Handler.
|
||||
--
|
||||
-- --- @param Functional.ZoneCaptureCoalition#ZONE_CAPTURE_COALITION self
|
||||
-- function ZoneCaptureCoalition:OnEnterGuarded( From, Event, To )
|
||||
@@ -145,7 +189,7 @@ do -- ZONE_CAPTURE_COALITION
|
||||
-- end
|
||||
-- end
|
||||
--
|
||||
-- ### 3.3. Stop and Start the zone monitoring process.
|
||||
-- ## Stop and Start the zone monitoring process.
|
||||
--
|
||||
-- At regular intervals, the state of the zone needs to be monitored.
|
||||
-- The zone needs to be scanned for the presence of units within the zone boundaries.
|
||||
@@ -157,18 +201,17 @@ do -- ZONE_CAPTURE_COALITION
|
||||
--
|
||||
-- Therefore, the mission designer is given 2 methods that allow to take control of the CPU utilization efficiency:
|
||||
--
|
||||
-- - @{#ZONE_CAPTURE_COALITION.Start()}(): This starts the monitoring process.
|
||||
-- - @{#ZONE_CAPTURE_COALITION.Stop()}(): This stops the monitoring process.
|
||||
-- * @{#ZONE_CAPTURE_COALITION.Start}(): This starts the monitoring process.
|
||||
-- * @{#ZONE_CAPTURE_COALITION.Stop}(): This stops the monitoring process.
|
||||
--
|
||||
-- ### IMPORTANT
|
||||
--
|
||||
-- **Each capture zone object must have the monitoring process started specifically.
|
||||
-- The monitoring process is NOT started by default!!!**
|
||||
-- **Each capture zone object must have the monitoring process started specifically. The monitoring process is NOT started by default!**
|
||||
--
|
||||
--
|
||||
-- ## 4. Full Example
|
||||
-- # Full Example
|
||||
--
|
||||
-- The following annotated code shows a real example of how ZONE\_CAPTURE\_COALITION can be applied.
|
||||
-- The following annotated code shows a real example of how ZONE_CAPTURE_COALITION can be applied.
|
||||
--
|
||||
-- The concept is simple.
|
||||
--
|
||||
@@ -303,33 +346,52 @@ do -- ZONE_CAPTURE_COALITION
|
||||
--
|
||||
-- @field #ZONE_CAPTURE_COALITION
|
||||
ZONE_CAPTURE_COALITION = {
|
||||
ClassName = "ZONE_CAPTURE_COALITION",
|
||||
ClassName = "ZONE_CAPTURE_COALITION",
|
||||
MarkBlue = nil,
|
||||
MarkRed = nil,
|
||||
StartInterval = nil,
|
||||
RepeatInterval = nil,
|
||||
HitsOn = nil,
|
||||
HitTimeLast = nil,
|
||||
HitTimeAttackOver = nil,
|
||||
MarkOn = nil,
|
||||
}
|
||||
|
||||
--- @field #table ZONE_CAPTURE_COALITION.States
|
||||
ZONE_CAPTURE_COALITION.States = {}
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Constructor and Start/Stop Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- ZONE_CAPTURE_COALITION Constructor.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param Core.Zone#ZONE Zone A @{Zone} object with the goal to be achieved.
|
||||
-- @param DCSCoalition.DCSCoalition#coalition Coalition The initial coalition owning the zone.
|
||||
-- @param #table UnitCategories Table of unit categories. See [DCS Class Unit](https://wiki.hoggitworld.com/view/DCS_Class_Unit). Default {Unit.Category.GROUND_UNIT}.
|
||||
-- @param #table ObjectCategories Table of unit categories. See [DCS Class Object](https://wiki.hoggitworld.com/view/DCS_Class_Object). Default {Object.Category.UNIT, Object.Category.STATIC}, i.e. all UNITS and STATICS.
|
||||
-- @return #ZONE_CAPTURE_COALITION
|
||||
-- @usage
|
||||
--
|
||||
-- AttackZone = ZONE:New( "AttackZone" )
|
||||
--
|
||||
-- ZoneCaptureCoalition = ZONE_CAPTURE_COALITION:New( AttackZone, coalition.side.RED ) -- Create a new ZONE_CAPTURE_COALITION object of zone AttackZone with ownership RED coalition.
|
||||
-- ZoneCaptureCoalition = ZONE_CAPTURE_COALITION:New( AttackZone, coalition.side.RED, {UNITS ) -- Create a new ZONE_CAPTURE_COALITION object of zone AttackZone with ownership RED coalition.
|
||||
-- ZoneCaptureCoalition:__Guard( 1 ) -- Start the Guarding of the AttackZone.
|
||||
--
|
||||
function ZONE_CAPTURE_COALITION:New( Zone, Coalition )
|
||||
function ZONE_CAPTURE_COALITION:New( Zone, Coalition, UnitCategories, ObjectCategories )
|
||||
|
||||
local self = BASE:Inherit( self, ZONE_GOAL_COALITION:New( Zone, Coalition ) ) -- #ZONE_CAPTURE_COALITION
|
||||
|
||||
self:F( { Zone = Zone, Coalition = Coalition } )
|
||||
local self = BASE:Inherit( self, ZONE_GOAL_COALITION:New( Zone, Coalition, UnitCategories ) ) -- #ZONE_CAPTURE_COALITION
|
||||
self:F( { Zone = Zone, Coalition = Coalition, UnitCategories = UnitCategories, ObjectCategories = ObjectCategories } )
|
||||
|
||||
self:SetObjectCategories(ObjectCategories)
|
||||
|
||||
-- Default is no smoke.
|
||||
self:SetSmokeZone(false)
|
||||
-- Default is F10 marks ON.
|
||||
self:SetMarkZone(true)
|
||||
-- Start in state "Empty".
|
||||
self:SetStartState("Empty")
|
||||
|
||||
do
|
||||
|
||||
--- Captured State Handler OnLeave for ZONE\_CAPTURE\_COALITION
|
||||
--- Captured State Handler OnLeave for ZONE_CAPTURE_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] OnLeaveCaptured
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #string From
|
||||
@@ -337,7 +399,7 @@ do -- ZONE_CAPTURE_COALITION
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Captured State Handler OnEnter for ZONE\_CAPTURE\_COALITION
|
||||
--- Captured State Handler OnEnter for ZONE_CAPTURE_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] OnEnterCaptured
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #string From
|
||||
@@ -349,7 +411,7 @@ do -- ZONE_CAPTURE_COALITION
|
||||
|
||||
do
|
||||
|
||||
--- Attacked State Handler OnLeave for ZONE\_CAPTURE\_COALITION
|
||||
--- Attacked State Handler OnLeave for ZONE_CAPTURE_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] OnLeaveAttacked
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #string From
|
||||
@@ -357,7 +419,7 @@ do -- ZONE_CAPTURE_COALITION
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Attacked State Handler OnEnter for ZONE\_CAPTURE\_COALITION
|
||||
--- Attacked State Handler OnEnter for ZONE_CAPTURE_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] OnEnterAttacked
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #string From
|
||||
@@ -368,7 +430,7 @@ do -- ZONE_CAPTURE_COALITION
|
||||
|
||||
do
|
||||
|
||||
--- Guarded State Handler OnLeave for ZONE\_CAPTURE\_COALITION
|
||||
--- Guarded State Handler OnLeave for ZONE_CAPTURE_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] OnLeaveGuarded
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #string From
|
||||
@@ -376,7 +438,7 @@ do -- ZONE_CAPTURE_COALITION
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Guarded State Handler OnEnter for ZONE\_CAPTURE\_COALITION
|
||||
--- Guarded State Handler OnEnter for ZONE_CAPTURE_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] OnEnterGuarded
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #string From
|
||||
@@ -388,7 +450,7 @@ do -- ZONE_CAPTURE_COALITION
|
||||
|
||||
do
|
||||
|
||||
--- Empty State Handler OnLeave for ZONE\_CAPTURE\_COALITION
|
||||
--- Empty State Handler OnLeave for ZONE_CAPTURE_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] OnLeaveEmpty
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #string From
|
||||
@@ -396,7 +458,7 @@ do -- ZONE_CAPTURE_COALITION
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Empty State Handler OnEnter for ZONE\_CAPTURE\_COALITION
|
||||
--- Empty State Handler OnEnter for ZONE_CAPTURE_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] OnEnterEmpty
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #string From
|
||||
@@ -407,7 +469,7 @@ do -- ZONE_CAPTURE_COALITION
|
||||
|
||||
self:AddTransition( "*", "Guard", "Guarded" )
|
||||
|
||||
--- Guard Handler OnBefore for ZONE\_CAPTURE\_COALITION
|
||||
--- Guard Handler OnBefore for ZONE_CAPTURE_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] OnBeforeGuard
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #string From
|
||||
@@ -415,25 +477,25 @@ do -- ZONE_CAPTURE_COALITION
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Guard Handler OnAfter for ZONE\_CAPTURE\_COALITION
|
||||
--- Guard Handler OnAfter for ZONE_CAPTURE_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] OnAfterGuard
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
--- Guard Trigger for ZONE\_CAPTURE\_COALITION
|
||||
--- Guard Trigger for ZONE_CAPTURE_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] Guard
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
|
||||
--- Guard Asynchronous Trigger for ZONE\_CAPTURE\_COALITION
|
||||
--- Guard Asynchronous Trigger for ZONE_CAPTURE_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] __Guard
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #number Delay
|
||||
|
||||
self:AddTransition( "*", "Empty", "Empty" )
|
||||
|
||||
--- Empty Handler OnBefore for ZONE\_CAPTURE\_COALITION
|
||||
--- Empty Handler OnBefore for ZONE_CAPTURE_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] OnBeforeEmpty
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #string From
|
||||
@@ -441,18 +503,18 @@ do -- ZONE_CAPTURE_COALITION
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Empty Handler OnAfter for ZONE\_CAPTURE\_COALITION
|
||||
--- Empty Handler OnAfter for ZONE_CAPTURE_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] OnAfterEmpty
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
--- Empty Trigger for ZONE\_CAPTURE\_COALITION
|
||||
--- Empty Trigger for ZONE_CAPTURE_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] Empty
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
|
||||
--- Empty Asynchronous Trigger for ZONE\_CAPTURE\_COALITION
|
||||
--- Empty Asynchronous Trigger for ZONE_CAPTURE_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] __Empty
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #number Delay
|
||||
@@ -460,7 +522,7 @@ do -- ZONE_CAPTURE_COALITION
|
||||
|
||||
self:AddTransition( { "Guarded", "Empty" }, "Attack", "Attacked" )
|
||||
|
||||
--- Attack Handler OnBefore for ZONE\_CAPTURE\_COALITION
|
||||
--- Attack Handler OnBefore for ZONE_CAPTURE_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] OnBeforeAttack
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #string From
|
||||
@@ -468,25 +530,25 @@ do -- ZONE_CAPTURE_COALITION
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Attack Handler OnAfter for ZONE\_CAPTURE\_COALITION
|
||||
--- Attack Handler OnAfter for ZONE_CAPTURE_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] OnAfterAttack
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
--- Attack Trigger for ZONE\_CAPTURE\_COALITION
|
||||
--- Attack Trigger for ZONE_CAPTURE_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] Attack
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
|
||||
--- Attack Asynchronous Trigger for ZONE\_CAPTURE\_COALITION
|
||||
--- Attack Asynchronous Trigger for ZONE_CAPTURE_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] __Attack
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #number Delay
|
||||
|
||||
self:AddTransition( { "Guarded", "Attacked", "Empty" }, "Capture", "Captured" )
|
||||
|
||||
--- Capture Handler OnBefore for ZONE\_CAPTURE\_COALITION
|
||||
--- Capture Handler OnBefore for ZONE_CAPTURE_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] OnBeforeCapture
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #string From
|
||||
@@ -494,190 +556,28 @@ do -- ZONE_CAPTURE_COALITION
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Capture Handler OnAfter for ZONE\_CAPTURE\_COALITION
|
||||
--- Capture Handler OnAfter for ZONE_CAPTURE_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] OnAfterCapture
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
--- Capture Trigger for ZONE\_CAPTURE\_COALITION
|
||||
--- Capture Trigger for ZONE_CAPTURE_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] Capture
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
|
||||
--- Capture Asynchronous Trigger for ZONE\_CAPTURE\_COALITION
|
||||
--- Capture Asynchronous Trigger for ZONE_CAPTURE_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] __Capture
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #number Delay
|
||||
|
||||
-- ZoneGoal objects are added to the _DATABASE.ZONES_GOAL and SET_ZONE_GOAL sets.
|
||||
_EVENTDISPATCHER:CreateEventNewZoneGoal(self)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- @param #ZONE_CAPTURE_COALITION self
|
||||
function ZONE_CAPTURE_COALITION:onenterCaptured()
|
||||
|
||||
self:GetParent( self, ZONE_CAPTURE_COALITION ).onenterCaptured( self )
|
||||
|
||||
self.Goal:Achieved()
|
||||
end
|
||||
|
||||
|
||||
function ZONE_CAPTURE_COALITION:IsGuarded()
|
||||
|
||||
local IsGuarded = self.Zone:IsAllInZoneOfCoalition( self.Coalition )
|
||||
self:F( { IsGuarded = IsGuarded } )
|
||||
return IsGuarded
|
||||
end
|
||||
|
||||
|
||||
function ZONE_CAPTURE_COALITION:IsEmpty()
|
||||
|
||||
local IsEmpty = self.Zone:IsNoneInZone()
|
||||
self:F( { IsEmpty = IsEmpty } )
|
||||
return IsEmpty
|
||||
end
|
||||
|
||||
|
||||
function ZONE_CAPTURE_COALITION:IsCaptured()
|
||||
|
||||
local IsCaptured = self.Zone:IsAllInZoneOfOtherCoalition( self.Coalition )
|
||||
self:F( { IsCaptured = IsCaptured } )
|
||||
return IsCaptured
|
||||
end
|
||||
|
||||
|
||||
function ZONE_CAPTURE_COALITION:IsAttacked()
|
||||
|
||||
local IsAttacked = self.Zone:IsSomeInZoneOfCoalition( self.Coalition )
|
||||
self:F( { IsAttacked = IsAttacked } )
|
||||
return IsAttacked
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Mark.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
function ZONE_CAPTURE_COALITION:Mark()
|
||||
|
||||
local Coord = self.Zone:GetCoordinate()
|
||||
local ZoneName = self:GetZoneName()
|
||||
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( "Coalition: Blue\nGuard Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||
self.MarkRed = Coord:MarkToCoalitionRed( "Coalition: Blue\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||
else
|
||||
self.MarkRed = Coord:MarkToCoalitionRed( "Coalition: Red\nGuard Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||
self.MarkBlue = Coord:MarkToCoalitionBlue( "Coalition: Red\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||
end
|
||||
end
|
||||
|
||||
--- Bound.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
function ZONE_CAPTURE_COALITION:onenterGuarded()
|
||||
|
||||
--self:GetParent( self ):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 ZONE_CAPTURE_COALITION:onenterCaptured()
|
||||
|
||||
--self:GetParent( self ):onenterCaptured()
|
||||
|
||||
local NewCoalition = self.Zone:GetScannedCoalition()
|
||||
self:F( { NewCoalition = NewCoalition } )
|
||||
self:SetCoalition( NewCoalition )
|
||||
|
||||
self:Mark()
|
||||
end
|
||||
|
||||
|
||||
function ZONE_CAPTURE_COALITION:onenterEmpty()
|
||||
|
||||
--self:GetParent( self ):onenterEmpty()
|
||||
|
||||
self:Mark()
|
||||
end
|
||||
|
||||
|
||||
function ZONE_CAPTURE_COALITION:onenterAttacked()
|
||||
|
||||
--self:GetParent( self ):onenterAttacked()
|
||||
|
||||
self:Mark()
|
||||
end
|
||||
|
||||
|
||||
--- When started, check the Coalition status.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
function ZONE_CAPTURE_COALITION:onafterGuard()
|
||||
|
||||
--self:F({BASE:GetParent( self )})
|
||||
--BASE:GetParent( self ).onafterGuard( self )
|
||||
|
||||
if not self.SmokeScheduler then
|
||||
self.SmokeScheduler = self:ScheduleRepeat( 1, 1, 0.1, nil, self.StatusSmoke, self )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function ZONE_CAPTURE_COALITION:IsCaptured()
|
||||
|
||||
local IsCaptured = self.Zone:IsAllInZoneOfOtherCoalition( self.Coalition )
|
||||
self:F( { IsCaptured = IsCaptured } )
|
||||
return IsCaptured
|
||||
end
|
||||
|
||||
|
||||
function ZONE_CAPTURE_COALITION:IsAttacked()
|
||||
|
||||
local IsAttacked = self.Zone:IsSomeInZoneOfCoalition( self.Coalition )
|
||||
self:F( { IsAttacked = IsAttacked } )
|
||||
return IsAttacked
|
||||
end
|
||||
|
||||
|
||||
--- Check status Coalition ownership.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
function ZONE_CAPTURE_COALITION:StatusZone()
|
||||
|
||||
local State = self:GetState()
|
||||
self:F( { State = self:GetState() } )
|
||||
|
||||
self:GetParent( self, ZONE_CAPTURE_COALITION ).StatusZone( self )
|
||||
|
||||
if State ~= "Guarded" and self:IsGuarded() then
|
||||
self:Guard()
|
||||
end
|
||||
|
||||
if State ~= "Empty" and self:IsEmpty() then
|
||||
self:Empty()
|
||||
end
|
||||
|
||||
if State ~= "Attacked" and self:IsAttacked() then
|
||||
self:Attack()
|
||||
end
|
||||
|
||||
if State ~= "Captured" and self:IsCaptured() then
|
||||
self:Capture()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Starts the zone capturing monitoring process.
|
||||
-- This process can be CPU intensive, ensure that you specify reasonable time intervals for the monitoring process.
|
||||
@@ -688,6 +588,7 @@ do -- ZONE_CAPTURE_COALITION
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #number StartInterval (optional) Specifies the start time interval in seconds when the zone state will be checked for the first time.
|
||||
-- @param #number RepeatInterval (optional) Specifies the repeat time interval in seconds when the zone state will be checked repeatedly.
|
||||
-- @return #ZONE_CAPTURE_COALITION self
|
||||
-- @usage
|
||||
--
|
||||
-- -- Setup the zone.
|
||||
@@ -702,13 +603,23 @@ do -- ZONE_CAPTURE_COALITION
|
||||
--
|
||||
function ZONE_CAPTURE_COALITION:Start( StartInterval, RepeatInterval )
|
||||
|
||||
StartInterval = StartInterval or 15
|
||||
RepeatInterval = RepeatInterval or 15
|
||||
self.StartInterval = StartInterval or 1
|
||||
self.RepeatInterval = RepeatInterval or 15
|
||||
|
||||
if self.ScheduleStatusZone then
|
||||
self:ScheduleStop( self.ScheduleStatusZone )
|
||||
end
|
||||
self.ScheduleStatusZone = self:ScheduleRepeat( StartInterval, RepeatInterval, 0.1, nil, self.StatusZone, self )
|
||||
|
||||
-- Start Status scheduler.
|
||||
self.ScheduleStatusZone = self:ScheduleRepeat( self.StartInterval, self.RepeatInterval, 0.1, nil, self.StatusZone, self )
|
||||
|
||||
-- We check if a unit within the zone is hit. If it is, then we must move the zone to attack state.
|
||||
self:HandleEvent(EVENTS.Hit, self.OnEventHit)
|
||||
|
||||
-- Create mark on F10 map.
|
||||
self:Mark()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
@@ -750,9 +661,281 @@ do -- ZONE_CAPTURE_COALITION
|
||||
function ZONE_CAPTURE_COALITION:Stop()
|
||||
|
||||
if self.ScheduleStatusZone then
|
||||
self:ScheduleStop( self.ScheduleStatusZone )
|
||||
self:ScheduleStop(self.ScheduleStatusZone)
|
||||
end
|
||||
|
||||
if self.SmokeScheduler then
|
||||
self:ScheduleStop(self.SmokeScheduler)
|
||||
end
|
||||
|
||||
self:UnHandleEvent(EVENTS.Hit)
|
||||
|
||||
end
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- User API Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Set whether hit events of defending units are monitored and trigger "Attack" events.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #boolean Switch If *true*, hit events are monitored. If *false* or *nil*, hit events are not monitored.
|
||||
-- @param #number TimeAttackOver (Optional) Time in seconds after an attack is over after the last hit and the zone state goes to "Guarded". Default is 300 sec = 5 min.
|
||||
-- @return #ZONE_CAPTURE_COALITION self
|
||||
function ZONE_CAPTURE_COALITION:SetMonitorHits(Switch, TimeAttackOver)
|
||||
self.HitsOn=Switch
|
||||
self.HitTimeAttackOver=TimeAttackOver or 5*60
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set whether marks on the F10 map are shown, which display the current zone status.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #boolean Switch If *true* or *nil*, marks are shown. If *false*, marks are not displayed.
|
||||
-- @return #ZONE_CAPTURE_COALITION self
|
||||
function ZONE_CAPTURE_COALITION:SetMarkZone(Switch)
|
||||
if Switch==nil or Switch==true then
|
||||
self.MarkOn=true
|
||||
else
|
||||
self.MarkOn=false
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- DCS Event Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
--- Monitor hit events.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param Core.Event#EVENTDATA EventData The event data.
|
||||
function ZONE_CAPTURE_COALITION:OnEventHit( EventData )
|
||||
|
||||
if self.HitsOn then
|
||||
|
||||
local UnitHit = EventData.TgtUnit
|
||||
|
||||
-- Check if unit is inside the capture zone and that it is of the defending coalition.
|
||||
if UnitHit and UnitHit:IsInZone(self) and UnitHit:GetCoalition()==self.Coalition then
|
||||
|
||||
-- Update last hit time.
|
||||
self.HitTimeLast=timer.getTime()
|
||||
|
||||
-- Only trigger attacked event if not already in state "Attacked".
|
||||
if self:GetState()~="Attacked" then
|
||||
self:F2("Hit ==> Attack")
|
||||
self:Attack()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- FSM Event Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- On after "Guard" event.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
function ZONE_CAPTURE_COALITION:onafterGuard()
|
||||
self:F2("After Guard")
|
||||
|
||||
if self.SmokeZone and not self.SmokeScheduler then
|
||||
self.SmokeScheduler = self:ScheduleRepeat( self.StartInterval, self.RepeatInterval, 0.1, nil, self.StatusSmoke, self )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
--- On enter "Guarded" state.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
function ZONE_CAPTURE_COALITION:onenterGuarded()
|
||||
self:F2("Enter Guarded")
|
||||
self:Mark()
|
||||
end
|
||||
|
||||
--- On enter "Captured" state.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
function ZONE_CAPTURE_COALITION:onenterCaptured()
|
||||
self:F2("Enter Captured")
|
||||
|
||||
-- Get new coalition.
|
||||
local NewCoalition = self:GetScannedCoalition()
|
||||
self:F( { NewCoalition = NewCoalition } )
|
||||
|
||||
-- Set new owner of zone.
|
||||
self:SetCoalition(NewCoalition)
|
||||
|
||||
-- Update mark.
|
||||
self:Mark()
|
||||
|
||||
-- Goal achieved.
|
||||
self.Goal:Achieved()
|
||||
end
|
||||
|
||||
--- On enter "Empty" state.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
function ZONE_CAPTURE_COALITION:onenterEmpty()
|
||||
self:F2("Enter Empty")
|
||||
self:Mark()
|
||||
end
|
||||
|
||||
--- On enter "Attacked" state.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
function ZONE_CAPTURE_COALITION:onenterAttacked()
|
||||
self:F2("Enter Attacked")
|
||||
self:Mark()
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Status Check Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Check if zone is "Empty".
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @return #boolean self:IsNoneInZone()
|
||||
function ZONE_CAPTURE_COALITION:IsEmpty()
|
||||
|
||||
local IsEmpty = self:IsNoneInZone()
|
||||
self:F( { IsEmpty = IsEmpty } )
|
||||
|
||||
return IsEmpty
|
||||
end
|
||||
|
||||
--- Check if zone is "Guarded", i.e. only one (the defending) coaliton is present inside the zone.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @return #boolean self:IsAllInZoneOfCoalition( self.Coalition )
|
||||
function ZONE_CAPTURE_COALITION:IsGuarded()
|
||||
|
||||
local IsGuarded = self:IsAllInZoneOfCoalition( self.Coalition )
|
||||
self:F( { IsGuarded = IsGuarded } )
|
||||
|
||||
return IsGuarded
|
||||
end
|
||||
|
||||
--- Check if zone is "Captured", i.e. another coalition took control over the zone and is the only one present.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @return #boolean self:IsAllInZoneOfOtherCoalition( self.Coalition )
|
||||
function ZONE_CAPTURE_COALITION:IsCaptured()
|
||||
|
||||
local IsCaptured = self:IsAllInZoneOfOtherCoalition( self.Coalition )
|
||||
self:F( { IsCaptured = IsCaptured } )
|
||||
|
||||
return IsCaptured
|
||||
end
|
||||
|
||||
--- Check if zone is "Attacked", i.e. another coaliton entered the zone.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @return #boolean self:IsSomeInZoneOfCoalition( self.Coalition )
|
||||
function ZONE_CAPTURE_COALITION:IsAttacked()
|
||||
|
||||
local IsAttacked = self:IsSomeInZoneOfCoalition( self.Coalition )
|
||||
self:F( { IsAttacked = IsAttacked } )
|
||||
|
||||
return IsAttacked
|
||||
end
|
||||
|
||||
|
||||
--- Check status Coalition ownership.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
function ZONE_CAPTURE_COALITION:StatusZone()
|
||||
|
||||
-- Get FSM state.
|
||||
local State = self:GetState()
|
||||
|
||||
-- Scan zone in parent class ZONE_GOAL_COALITION
|
||||
self:GetParent( self, ZONE_CAPTURE_COALITION ).StatusZone( self )
|
||||
|
||||
local Tnow=timer.getTime()
|
||||
|
||||
-- Check if zone is guarded.
|
||||
if State ~= "Guarded" and self:IsGuarded() then
|
||||
|
||||
-- Check that there was a sufficient amount of time after the last hit before going back to "Guarded".
|
||||
if self.HitTimeLast==nil or Tnow>=self.HitTimeLast+self.HitTimeAttackOver then
|
||||
self:Guard()
|
||||
self.HitTimeLast=nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Check if zone is empty.
|
||||
if State ~= "Empty" and self:IsEmpty() then
|
||||
self:Empty()
|
||||
end
|
||||
|
||||
-- Check if zone is attacked.
|
||||
if State ~= "Attacked" and self:IsAttacked() then
|
||||
self:Attack()
|
||||
end
|
||||
|
||||
-- Check if zone is captured.
|
||||
if State ~= "Captured" and self:IsCaptured() then
|
||||
self:Capture()
|
||||
end
|
||||
|
||||
-- Count stuff in zone.
|
||||
local unitset=self:GetScannedSetUnit()
|
||||
local nRed=0
|
||||
local nBlue=0
|
||||
for _,object in pairs(unitset:GetSet()) do
|
||||
local coal=object:GetCoalition()
|
||||
if object:IsAlive() then
|
||||
if coal==coalition.side.RED then
|
||||
nRed=nRed+1
|
||||
elseif coal==coalition.side.BLUE then
|
||||
nBlue=nBlue+1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Status text.
|
||||
local text=string.format("CAPTURE ZONE %s: Owner=%s (Previous=%s): #blue=%d, #red=%d, Status %s", self:GetZoneName(), self:GetCoalitionName(), UTILS.GetCoalitionName(self:GetPreviousCoalition()), nBlue, nRed, State)
|
||||
local NewState = self:GetState()
|
||||
if NewState~=State then
|
||||
text=text..string.format(" --> %s", NewState)
|
||||
end
|
||||
self:I(text)
|
||||
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Misc Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
--- Update Mark on F10 map.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
function ZONE_CAPTURE_COALITION:Mark()
|
||||
|
||||
if self.MarkOn then
|
||||
|
||||
local Coord = self:GetCoordinate()
|
||||
local ZoneName = self:GetZoneName()
|
||||
local State = self:GetState()
|
||||
|
||||
-- Remove marks.
|
||||
if self.MarkRed then
|
||||
Coord:RemoveMark(self.MarkRed)
|
||||
end
|
||||
if self.MarkBlue then
|
||||
Coord:RemoveMark(self.MarkBlue)
|
||||
end
|
||||
|
||||
-- Create new marks for each coaliton.
|
||||
if self.Coalition == coalition.side.BLUE then
|
||||
self.MarkBlue = Coord:MarkToCoalitionBlue( "Coalition: Blue\nGuard Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||
self.MarkRed = Coord:MarkToCoalitionRed( "Coalition: Blue\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||
elseif self.Coalition == coalition.side.RED then
|
||||
self.MarkRed = Coord:MarkToCoalitionRed( "Coalition: Red\nGuard Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||
self.MarkBlue = Coord:MarkToCoalitionBlue( "Coalition: Red\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||
else
|
||||
self.MarkRed = Coord:MarkToCoalitionRed( "Coalition: Neutral\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||
self.MarkBlue = Coord:MarkToCoalitionBlue( "Coalition: Neutral\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions: **funkyfranky**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -17,10 +18,16 @@
|
||||
do -- Zone
|
||||
|
||||
--- @type ZONE_GOAL
|
||||
-- @extends Core.Fsm#FSM
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @field Core.Goal#GOAL Goal The goal object.
|
||||
-- @field #number SmokeTime Time stamp in seconds when the last smoke of the zone was triggered.
|
||||
-- @field Core.Scheduler#SCHEDULER SmokeScheduler Scheduler responsible for smoking the zone.
|
||||
-- @field #number SmokeColor Color of the smoke.
|
||||
-- @field #boolean SmokeZone If true, smoke zone.
|
||||
-- @extends Core.Zone#ZONE_RADIUS
|
||||
|
||||
|
||||
-- Models processes that have a Goal with a defined achievement involving a Zone.
|
||||
--- Models processes that have a Goal with a defined achievement involving a Zone.
|
||||
-- Derived classes implement the ways how the achievements can be realized.
|
||||
--
|
||||
-- ## 1. ZONE_GOAL constructor
|
||||
@@ -39,24 +46,41 @@ do -- Zone
|
||||
--
|
||||
-- @field #ZONE_GOAL
|
||||
ZONE_GOAL = {
|
||||
ClassName = "ZONE_GOAL",
|
||||
ClassName = "ZONE_GOAL",
|
||||
Goal = nil,
|
||||
SmokeTime = nil,
|
||||
SmokeScheduler = nil,
|
||||
SmokeColor = nil,
|
||||
SmokeZone = nil,
|
||||
}
|
||||
|
||||
--- ZONE_GOAL Constructor.
|
||||
-- @param #ZONE_GOAL self
|
||||
-- @param Core.Zone#ZONE_BASE Zone A @{Zone} object with the goal to be achieved.
|
||||
-- @param Core.Zone#ZONE_RADIUS Zone A @{Zone} object with the goal to be achieved.
|
||||
-- @return #ZONE_GOAL
|
||||
function ZONE_GOAL:New( Zone )
|
||||
|
||||
local self = BASE:Inherit( self, FSM:New() ) -- #ZONE_GOAL
|
||||
local self = BASE:Inherit( self, ZONE_RADIUS:New( Zone:GetName(), Zone:GetVec2(), Zone:GetRadius() ) ) -- #ZONE_GOAL
|
||||
self:F( { Zone = Zone } )
|
||||
|
||||
self.Zone = Zone -- Core.Zone#ZONE_BASE
|
||||
-- Goal object.
|
||||
self.Goal = GOAL:New()
|
||||
|
||||
self.SmokeTime = nil
|
||||
|
||||
-- Set smoke ON.
|
||||
self:SetSmokeZone(true)
|
||||
|
||||
self:AddTransition( "*", "DestroyedUnit", "*" )
|
||||
|
||||
--- DestroyedUnit event.
|
||||
-- @function [parent=#ZONE_GOAL] DestroyedUnit
|
||||
-- @param #ZONE_GOAL self
|
||||
|
||||
--- DestroyedUnit delayed event
|
||||
-- @function [parent=#ZONE_GOAL] __DestroyedUnit
|
||||
-- @param #ZONE_GOAL self
|
||||
-- @param #number delay Delay in seconds.
|
||||
|
||||
--- DestroyedUnit Handler OnAfter for ZONE_GOAL
|
||||
-- @function [parent=#ZONE_GOAL] OnAfterDestroyedUnit
|
||||
@@ -70,52 +94,64 @@ do -- Zone
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the Zone
|
||||
--- Get the Zone.
|
||||
-- @param #ZONE_GOAL self
|
||||
-- @return Core.Zone#ZONE_BASE
|
||||
-- @return #ZONE_GOAL
|
||||
function ZONE_GOAL:GetZone()
|
||||
return self.Zone
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Get the name of the ProtectZone
|
||||
--- Get the name of the Zone.
|
||||
-- @param #ZONE_GOAL self
|
||||
-- @return #string
|
||||
function ZONE_GOAL:GetZoneName()
|
||||
return self.Zone:GetName()
|
||||
return self:GetName()
|
||||
end
|
||||
|
||||
|
||||
--- Smoke the center of theh zone.
|
||||
--- Activate smoking of zone with the color or the current owner.
|
||||
-- @param #ZONE_GOAL self
|
||||
-- @param #SMOKECOLOR.Color SmokeColor
|
||||
function ZONE_GOAL:Smoke( SmokeColor )
|
||||
|
||||
-- @param #boolean switch If *true* or *nil* activate smoke. If *false* or *nil*, no smoke.
|
||||
-- @return #ZONE_GOAL
|
||||
function ZONE_GOAL:SetSmokeZone(switch)
|
||||
self.SmokeZone=switch
|
||||
--[[
|
||||
if switch==nil or switch==true then
|
||||
self.SmokeZone=true
|
||||
else
|
||||
self.SmokeZone=false
|
||||
end
|
||||
]]
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set the smoke color.
|
||||
-- @param #ZONE_GOAL self
|
||||
-- @param DCS#SMOKECOLOR.Color SmokeColor
|
||||
function ZONE_GOAL:Smoke( SmokeColor )
|
||||
self:F( { SmokeColor = SmokeColor} )
|
||||
|
||||
self.SmokeColor = SmokeColor
|
||||
end
|
||||
|
||||
|
||||
--- Flare the center of the zone.
|
||||
--- Flare the zone boundary.
|
||||
-- @param #ZONE_GOAL self
|
||||
-- @param #SMOKECOLOR.Color FlareColor
|
||||
-- @param DCS#SMOKECOLOR.Color FlareColor
|
||||
function ZONE_GOAL:Flare( FlareColor )
|
||||
self.Zone:FlareZone( FlareColor, math.random( 1, 360 ) )
|
||||
self:FlareZone( FlareColor, 30)
|
||||
end
|
||||
|
||||
|
||||
--- When started, check the Smoke and the Zone status.
|
||||
-- @param #ZONE_GOAL self
|
||||
function ZONE_GOAL:onafterGuard()
|
||||
|
||||
--self:GetParent( self ):onafterStart()
|
||||
|
||||
self:F("Guard")
|
||||
|
||||
--self:ScheduleRepeat( 15, 15, 0.1, nil, self.StatusZone, self )
|
||||
if not self.SmokeScheduler then
|
||||
self.SmokeScheduler = self:ScheduleRepeat( 1, 1, 0.1, nil, self.StatusSmoke, self )
|
||||
|
||||
-- Start smoke
|
||||
if self.SmokeZone and not self.SmokeScheduler then
|
||||
self.SmokeScheduler = self:ScheduleRepeat(1, 1, 0.1, nil, self.StatusSmoke, self)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -123,44 +159,54 @@ do -- Zone
|
||||
--- Check status Smoke.
|
||||
-- @param #ZONE_GOAL self
|
||||
function ZONE_GOAL:StatusSmoke()
|
||||
|
||||
self:F({self.SmokeTime, self.SmokeColor})
|
||||
|
||||
local CurrentTime = timer.getTime()
|
||||
|
||||
if self.SmokeTime == nil or self.SmokeTime + 300 <= CurrentTime then
|
||||
if self.SmokeColor then
|
||||
self.Zone:GetCoordinate():Smoke( self.SmokeColor )
|
||||
--self.SmokeColor = nil
|
||||
self.SmokeTime = CurrentTime
|
||||
if self.SmokeZone then
|
||||
|
||||
-- Current time.
|
||||
local CurrentTime = timer.getTime()
|
||||
|
||||
-- Restart smoke every 5 min.
|
||||
if self.SmokeTime == nil or self.SmokeTime + 300 <= CurrentTime then
|
||||
if self.SmokeColor then
|
||||
self:GetCoordinate():Smoke( self.SmokeColor )
|
||||
self.SmokeTime = CurrentTime
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- @param #ZONE_GOAL self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
-- @param Core.Event#EVENTDATA EventData Event data table.
|
||||
function ZONE_GOAL:__Destroyed( EventData )
|
||||
self:F( { "EventDead", EventData } )
|
||||
|
||||
self:F( { EventData.IniUnit } )
|
||||
|
||||
local Vec3 = EventData.IniDCSUnit:getPosition().p
|
||||
self:F( { Vec3 = Vec3 } )
|
||||
local ZoneGoal = self:GetZone()
|
||||
self:F({ZoneGoal})
|
||||
|
||||
if EventData.IniDCSUnit then
|
||||
if ZoneGoal:IsVec3InZone(Vec3) then
|
||||
|
||||
local Vec3 = EventData.IniDCSUnit:getPosition().p
|
||||
self:F( { Vec3 = Vec3 } )
|
||||
|
||||
if Vec3 and self:IsVec3InZone(Vec3) then
|
||||
|
||||
local PlayerHits = _DATABASE.HITS[EventData.IniUnitName]
|
||||
|
||||
if PlayerHits then
|
||||
|
||||
for PlayerName, PlayerHit in pairs( PlayerHits.Players or {} ) do
|
||||
self.Goal:AddPlayerContribution( PlayerName )
|
||||
self:DestroyedUnit( EventData.IniUnitName, PlayerName )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -17,6 +17,11 @@
|
||||
do -- ZoneGoal
|
||||
|
||||
--- @type ZONE_GOAL_COALITION
|
||||
-- @field #string ClassName Name of the Class.
|
||||
-- @field #number Coalition The current coalition ID of the zone owner.
|
||||
-- @field #number PreviousCoalition The previous owner of the zone.
|
||||
-- @field #table UnitCategories Table of unit categories that are able to capture and hold the zone. Default is only GROUND units.
|
||||
-- @field #table ObjectCategories Table of object categories that are able to hold a zone. Default is UNITS and STATICS.
|
||||
-- @extends Functional.ZoneGoal#ZONE_GOAL
|
||||
|
||||
|
||||
@@ -37,7 +42,11 @@ do -- ZoneGoal
|
||||
--
|
||||
-- @field #ZONE_GOAL_COALITION
|
||||
ZONE_GOAL_COALITION = {
|
||||
ClassName = "ZONE_GOAL_COALITION",
|
||||
ClassName = "ZONE_GOAL_COALITION",
|
||||
Coalition = nil,
|
||||
PreviousCoaliton = nil,
|
||||
UnitCategories = nil,
|
||||
ObjectCategories = nil,
|
||||
}
|
||||
|
||||
--- @field #table ZONE_GOAL_COALITION.States
|
||||
@@ -46,27 +55,70 @@ do -- ZoneGoal
|
||||
--- ZONE_GOAL_COALITION Constructor.
|
||||
-- @param #ZONE_GOAL_COALITION self
|
||||
-- @param Core.Zone#ZONE Zone A @{Zone} object with the goal to be achieved.
|
||||
-- @param DCSCoalition.DCSCoalition#coalition Coalition The initial coalition owning the zone.
|
||||
-- @param DCSCoalition.DCSCoalition#coalition Coalition The initial coalition owning the zone. Default coalition.side.NEUTRAL.
|
||||
-- @param #table UnitCategories Table of unit categories. See [DCS Class Unit](https://wiki.hoggitworld.com/view/DCS_Class_Unit). Default {Unit.Category.GROUND_UNIT}.
|
||||
-- @return #ZONE_GOAL_COALITION
|
||||
function ZONE_GOAL_COALITION:New( Zone, Coalition )
|
||||
function ZONE_GOAL_COALITION:New( Zone, Coalition, UnitCategories )
|
||||
|
||||
if not Zone then
|
||||
BASE:E("ERROR: No Zone specified in ZONE_GOAL_COALITON!")
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Inherit ZONE_GOAL.
|
||||
local self = BASE:Inherit( self, ZONE_GOAL:New( Zone ) ) -- #ZONE_GOAL_COALITION
|
||||
self:F( { Zone = Zone, Coalition = Coalition } )
|
||||
|
||||
self:SetCoalition( Coalition )
|
||||
|
||||
|
||||
-- Set initial owner.
|
||||
self:SetCoalition( Coalition or coalition.side.NEUTRAL)
|
||||
|
||||
-- Set default unit and object categories for the zone scan.
|
||||
self:SetUnitCategories(UnitCategories)
|
||||
self:SetObjectCategories()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set the owning coalition of the zone.
|
||||
-- @param #ZONE_GOAL_COALITION self
|
||||
-- @param DCSCoalition.DCSCoalition#coalition Coalition
|
||||
-- @param DCSCoalition.DCSCoalition#coalition Coalition The coalition ID, e.g. *coalition.side.RED*.
|
||||
-- @return #ZONE_GOAL_COALITION
|
||||
function ZONE_GOAL_COALITION:SetCoalition( Coalition )
|
||||
self.PreviousCoalition=self.Coalition or Coalition
|
||||
self.Coalition = Coalition
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set the owning coalition of the zone.
|
||||
-- @param #ZONE_GOAL_COALITION self
|
||||
-- @param #table UnitCategories Table of unit categories. See [DCS Class Unit](https://wiki.hoggitworld.com/view/DCS_Class_Unit). Default {Unit.Category.GROUND_UNIT}.
|
||||
-- @return #ZONE_GOAL_COALITION
|
||||
function ZONE_GOAL_COALITION:SetUnitCategories( UnitCategories )
|
||||
|
||||
if UnitCategories and type(UnitCategories)~="table" then
|
||||
UnitCategories={UnitCategories}
|
||||
end
|
||||
|
||||
self.UnitCategories=UnitCategories or {Unit.Category.GROUND_UNIT}
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set the owning coalition of the zone.
|
||||
-- @param #ZONE_GOAL_COALITION self
|
||||
-- @param #table ObjectCategories Table of unit categories. See [DCS Class Object](https://wiki.hoggitworld.com/view/DCS_Class_Object). Default {Object.Category.UNIT, Object.Category.STATIC}, i.e. all UNITS and STATICS.
|
||||
-- @return #ZONE_GOAL_COALITION
|
||||
function ZONE_GOAL_COALITION:SetObjectCategories( ObjectCategories )
|
||||
|
||||
if ObjectCategories and type(ObjectCategories)~="table" then
|
||||
ObjectCategories={ObjectCategories}
|
||||
end
|
||||
|
||||
self.ObjectCategories=ObjectCategories or {Object.Category.UNIT, Object.Category.STATIC}
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the owning coalition of the zone.
|
||||
-- @param #ZONE_GOAL_COALITION self
|
||||
@@ -75,37 +127,38 @@ do -- ZoneGoal
|
||||
return self.Coalition
|
||||
end
|
||||
|
||||
--- Get the previous coaliton, i.e. the one owning the zone before the current one.
|
||||
-- @param #ZONE_GOAL_COALITION self
|
||||
-- @return DCSCoalition.DCSCoalition#coalition Coalition.
|
||||
function ZONE_GOAL_COALITION:GetPreviousCoalition()
|
||||
return self.PreviousCoalition
|
||||
end
|
||||
|
||||
|
||||
--- Get the owning coalition name of the zone.
|
||||
-- @param #ZONE_GOAL_COALITION self
|
||||
-- @return #string Coalition name.
|
||||
function ZONE_GOAL_COALITION: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 ""
|
||||
return UTILS.GetCoalitionName(self.Coalition)
|
||||
end
|
||||
|
||||
|
||||
--- Check status Coalition ownership.
|
||||
-- @param #ZONE_GOAL_COALITION self
|
||||
-- @return #ZONE_GOAL_COALITION
|
||||
function ZONE_GOAL_COALITION:StatusZone()
|
||||
|
||||
-- Get current state.
|
||||
local State = self:GetState()
|
||||
self:F( { State = self:GetState() } )
|
||||
|
||||
self.Zone:Scan( { Object.Category.UNIT, Object.Category.STATIC } )
|
||||
-- Debug text.
|
||||
local text=string.format("Zone state=%s, Owner=%s, Scanning...", State, self:GetCoalitionName())
|
||||
self:F(text)
|
||||
|
||||
-- Scan zone.
|
||||
self:Scan( self.ObjectCategories, self.UnitCategories )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
_EVENTDISPATCHER = EVENT:New() -- Core.Event#EVENT
|
||||
|
||||
--- Declare the timer dispatcher based on the SCHEDULEDISPATCHER class
|
||||
_SCHEDULEDISPATCHER = SCHEDULEDISPATCHER:New() -- Core.Timer#SCHEDULEDISPATCHER
|
||||
_SCHEDULEDISPATCHER = SCHEDULEDISPATCHER:New() -- Core.ScheduleDispatcher#SCHEDULEDISPATCHER
|
||||
|
||||
--- Declare the main database object, which is used internally by the MOOSE classes.
|
||||
_DATABASE = DATABASE:New() -- Core.Database#DATABASE
|
||||
125
Moose Development/Moose/Modules.lua
Normal file
125
Moose Development/Moose/Modules.lua
Normal file
@@ -0,0 +1,125 @@
|
||||
__Moose.Include( 'Scripts/Moose/Utilities/Enums.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Utilities/Routines.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Utilities/Utils.lua' )
|
||||
|
||||
__Moose.Include( 'Scripts/Moose/Core/Base.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/UserFlag.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/UserSound.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/Report.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/Scheduler.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/ScheduleDispatcher.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/Event.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/Settings.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/Menu.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/Zone.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/Zone_Detection.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/Database.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/Set.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/Point.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/Velocity.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/Message.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/Fsm.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/Radio.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/RadioQueue.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/RadioSpeech.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/Spawn.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/SpawnStatic.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/Goal.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/Spot.lua' )
|
||||
|
||||
__Moose.Include( 'Scripts/Moose/Wrapper/Object.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Wrapper/Identifiable.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Wrapper/Positionable.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Wrapper/Controllable.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Wrapper/Group.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Wrapper/Unit.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Wrapper/Client.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Wrapper/Static.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Wrapper/Airbase.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Wrapper/Scenery.lua' )
|
||||
|
||||
__Moose.Include( 'Scripts/Moose/Cargo/Cargo.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Cargo/CargoUnit.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Cargo/CargoSlingload.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Cargo/CargoCrate.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Cargo/CargoGroup.lua' )
|
||||
|
||||
__Moose.Include( 'Scripts/Moose/Functional/Scoring.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Functional/CleanUp.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Functional/Movement.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Functional/Sead.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Functional/Escort.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Functional/MissileTrainer.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Functional/ATC_Ground.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Functional/Detection.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Functional/DetectionZones.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Functional/Designate.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Functional/RAT.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Functional/Range.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Functional/ZoneGoal.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Functional/ZoneGoalCoalition.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Functional/ZoneCaptureCoalition.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Functional/Artillery.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Functional/Suppression.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Functional/PseudoATC.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Functional/Warehouse.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Functional/Fox.lua' )
|
||||
|
||||
__Moose.Include( 'Scripts/Moose/Ops/Airboss.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Ops/RecoveryTanker.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Ops/RescueHelo.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Ops/ATIS.lua' )
|
||||
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Balancer.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Air.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Air_Patrol.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Air_Engage.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_A2A_Patrol.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_A2A_Cap.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_A2A_Gci.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_A2A_Dispatcher.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_A2G_BAI.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_A2G_CAS.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_A2G_SEAD.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_A2G_Dispatcher.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Patrol.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Cap.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Cas.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Bai.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Formation.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Escort.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Escort_Request.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Escort_Dispatcher.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Escort_Dispatcher_Request.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_APC.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Helicopter.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Airplane.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Dispatcher.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Dispatcher_APC.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua' )
|
||||
|
||||
__Moose.Include( 'Scripts/Moose/Actions/Act_Assign.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Actions/Act_Route.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Actions/Act_Account.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Actions/Act_Assist.lua' )
|
||||
|
||||
__Moose.Include( 'Scripts/Moose/Tasking/CommandCenter.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Tasking/Mission.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Tasking/Task.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Tasking/TaskInfo.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Tasking/Task_Manager.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Tasking/DetectionManager.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Tasking/Task_A2G_Dispatcher.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Tasking/Task_A2G.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Tasking/Task_A2A_Dispatcher.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Tasking/Task_A2A.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Tasking/Task_Cargo.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Tasking/Task_Cargo_Transport.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Tasking/Task_Cargo_CSAR.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Tasking/Task_Cargo_Dispatcher.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Tasking/Task_Capture_Zone.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Tasking/Task_Capture_Dispatcher.lua' )
|
||||
|
||||
__Moose.Include( 'Scripts/Moose/Globals.lua' )
|
||||
2177
Moose Development/Moose/Ops/ATIS.lua
Normal file
2177
Moose Development/Moose/Ops/ATIS.lua
Normal file
File diff suppressed because it is too large
Load Diff
17929
Moose Development/Moose/Ops/Airboss.lua
Normal file
17929
Moose Development/Moose/Ops/Airboss.lua
Normal file
File diff suppressed because it is too large
Load Diff
1698
Moose Development/Moose/Ops/RecoveryTanker.lua
Normal file
1698
Moose Development/Moose/Ops/RecoveryTanker.lua
Normal file
File diff suppressed because it is too large
Load Diff
1296
Moose Development/Moose/Ops/RescueHelo.lua
Normal file
1296
Moose Development/Moose/Ops/RescueHelo.lua
Normal file
File diff suppressed because it is too large
Load Diff
@@ -144,6 +144,25 @@
|
||||
-- This is done by using the method @{#COMMANDCENTER.SetReferenceZones}().
|
||||
-- For the moment, only one Reference Zone class can be specified, but in the future, more classes will become possible.
|
||||
--
|
||||
-- ## 7. Tasks.
|
||||
--
|
||||
-- ### 7.1. Automatically assign tasks.
|
||||
--
|
||||
-- One of the most important roles of the command center is the management of tasks.
|
||||
-- The command center can assign automatically tasks to the players using the @{Tasking.CommandCenter#COMMANDCENTER.SetAutoAssignTasks}() method.
|
||||
-- When this method is used with a parameter true; the command center will scan at regular intervals which players in a slot are not having a task assigned.
|
||||
-- For those players; the tasking is enabled to assign automatically a task.
|
||||
-- An Assign Menu will be accessible for the player under the command center menu, to configure the automatic tasking to switched on or off.
|
||||
--
|
||||
-- ### 7.2. Automatically accept assigned tasks.
|
||||
--
|
||||
-- When a task is assigned; the mission designer can decide if players are immediately assigned to the task; or they can accept/reject the assigned task.
|
||||
-- Use the method @{Tasking.CommandCenter#COMMANDCENTER.SetAutoAcceptTasks}() to configure this behaviour.
|
||||
-- If the tasks are not automatically accepted; the player will receive a message that he needs to access the command center menu and
|
||||
-- choose from 2 added menu options either to accept or reject the assigned task within 30 seconds.
|
||||
-- If the task is not accepted within 30 seconds; the task will be cancelled and a new task will be assigned.
|
||||
--
|
||||
--
|
||||
-- @field #COMMANDCENTER
|
||||
COMMANDCENTER = {
|
||||
ClassName = "COMMANDCENTER",
|
||||
@@ -156,6 +175,14 @@ COMMANDCENTER = {
|
||||
CommunicationMode = "80",
|
||||
}
|
||||
|
||||
|
||||
--- @type COMMANDCENTER.AutoAssignMethods
|
||||
COMMANDCENTER.AutoAssignMethods = {
|
||||
["Random"] = 1,
|
||||
["Distance"] = 2,
|
||||
["Priority"] = 3,
|
||||
}
|
||||
|
||||
--- The constructor takes an IDENTIFIABLE as the HQ command center.
|
||||
-- @param #COMMANDCENTER self
|
||||
-- @param Wrapper.Positionable#POSITIONABLE CommandCenterPositionable
|
||||
@@ -169,21 +196,24 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName )
|
||||
self.CommandCenterName = CommandCenterName or CommandCenterPositionable:GetName()
|
||||
self.CommandCenterCoalition = CommandCenterPositionable:GetCoalition()
|
||||
|
||||
self.AutoAssignTasks = false
|
||||
|
||||
self.Missions = {}
|
||||
|
||||
self:SetAutoAssignTasks( false )
|
||||
self:SetAutoAcceptTasks( true )
|
||||
self:SetAutoAssignMethod( COMMANDCENTER.AutoAssignMethods.Distance )
|
||||
self:SetFlashStatus( false )
|
||||
|
||||
self:HandleEvent( EVENTS.Birth,
|
||||
--- @param #COMMANDCENTER self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function( self, EventData )
|
||||
if EventData.IniObjectCategory == 1 then
|
||||
local EventGroup = GROUP:Find( EventData.IniDCSGroup )
|
||||
self:E( { CommandCenter = self:GetName(), EventGroup = EventGroup:GetName(), HasGroup = self:HasGroup( EventGroup ), EventData = EventData } )
|
||||
if EventGroup and self:HasGroup( EventGroup ) then
|
||||
--self:E( { CommandCenter = self:GetName(), EventGroup = EventGroup:GetName(), HasGroup = self:HasGroup( EventGroup ), EventData = EventData } )
|
||||
if EventGroup and EventGroup:IsAlive() and self:HasGroup( EventGroup ) then
|
||||
local CommandCenterMenu = MENU_GROUP:New( EventGroup, self:GetText() )
|
||||
local MenuReporting = MENU_GROUP:New( EventGroup, "Missions Reports", CommandCenterMenu )
|
||||
local MenuMissionsSummary = MENU_GROUP_COMMAND:New( EventGroup, "Missions Status Report", MenuReporting, self.ReportMissionsStatus, self, EventGroup )
|
||||
local MenuMissionsSummary = MENU_GROUP_COMMAND:New( EventGroup, "Missions Status Report", MenuReporting, self.ReportSummary, self, EventGroup )
|
||||
local MenuMissionsDetails = MENU_GROUP_COMMAND:New( EventGroup, "Missions Players Report", MenuReporting, self.ReportMissionsPlayers, self, EventGroup )
|
||||
self:ReportSummary( EventGroup )
|
||||
local PlayerUnit = EventData.IniUnit
|
||||
@@ -442,7 +472,7 @@ function COMMANDCENTER:GetMenu( TaskGroup )
|
||||
self.CommandCenterMenus[TaskGroup] = CommandCenterMenu
|
||||
|
||||
if self.AutoAssignTasks == false then
|
||||
local AssignTaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Assign Task", CommandCenterMenu, self.AssignRandomTask, self, TaskGroup ):SetTime(MenuTime):SetTag("AutoTask")
|
||||
local AssignTaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Assign Task", CommandCenterMenu, self.AssignTask, self, TaskGroup ):SetTime(MenuTime):SetTag("AutoTask")
|
||||
end
|
||||
CommandCenterMenu:Remove( MenuTime, "AutoTask" )
|
||||
|
||||
@@ -453,23 +483,43 @@ end
|
||||
--- Assigns a random task to a TaskGroup.
|
||||
-- @param #COMMANDCENTER self
|
||||
-- @return #COMMANDCENTER
|
||||
function COMMANDCENTER:AssignRandomTask( TaskGroup )
|
||||
function COMMANDCENTER:AssignTask( TaskGroup )
|
||||
|
||||
local Tasks = {}
|
||||
local AssignPriority = 99999999
|
||||
local AutoAssignMethod = self.AutoAssignMethod
|
||||
|
||||
for MissionID, Mission in pairs( self:GetMissions() ) do
|
||||
local Mission = Mission -- Tasking.Mission#MISSION
|
||||
local MissionTasks = Mission:GetGroupTasks( TaskGroup )
|
||||
for MissionTaskName, MissionTask in pairs( MissionTasks or {} ) do
|
||||
Tasks[#Tasks+1] = MissionTask
|
||||
local MissionTask = MissionTask -- Tasking.Task#TASK
|
||||
if MissionTask:IsStatePlanned() or MissionTask:IsStateReplanned() or MissionTask:IsStateAssigned() then
|
||||
local TaskPriority = MissionTask:GetAutoAssignPriority( self.AutoAssignMethod, self, TaskGroup )
|
||||
if TaskPriority < AssignPriority then
|
||||
AssignPriority = TaskPriority
|
||||
Tasks = {}
|
||||
end
|
||||
if TaskPriority == AssignPriority then
|
||||
Tasks[#Tasks+1] = MissionTask
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local Task = Tasks[ math.random( 1, #Tasks ) ] -- Tasking.Task#TASK
|
||||
|
||||
Task:SetAssignMethod( ACT_ASSIGN_MENU_ACCEPT:New( Task.TaskBriefing ) )
|
||||
|
||||
Task:AssignToGroup( TaskGroup )
|
||||
if Task then
|
||||
|
||||
self:I( "Assigning task " .. Task:GetName() .. " using auto assign method " .. self.AutoAssignMethod .. " to " .. TaskGroup:GetName() .. " with task priority " .. AssignPriority )
|
||||
|
||||
if not self.AutoAcceptTasks == true then
|
||||
Task:SetAutoAssignMethod( ACT_ASSIGN_MENU_ACCEPT:New( Task.TaskBriefing ) )
|
||||
end
|
||||
|
||||
Task:AssignToGroup( TaskGroup )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -498,29 +548,54 @@ end
|
||||
|
||||
|
||||
--- Automatically assigns tasks to all TaskGroups.
|
||||
-- One of the most important roles of the command center is the management of tasks.
|
||||
-- When this method is used with a parameter true; the command center will scan at regular intervals which players in a slot are not having a task assigned.
|
||||
-- For those players; the tasking is enabled to assign automatically a task.
|
||||
-- An Assign Menu will be accessible for the player under the command center menu, to configure the automatic tasking to switched on or off.
|
||||
-- @param #COMMANDCENTER self
|
||||
-- @param #boolean AutoAssign true for ON and false or nil for OFF.
|
||||
function COMMANDCENTER:SetAutoAssignTasks( AutoAssign )
|
||||
|
||||
self.AutoAssignTasks = AutoAssign or false
|
||||
|
||||
local GroupSet = self:AddGroups()
|
||||
|
||||
for GroupID, TaskGroup in pairs( GroupSet:GetSet() ) do
|
||||
local TaskGroup = TaskGroup -- Wrapper.Group#GROUP
|
||||
self:GetMenu( TaskGroup )
|
||||
end
|
||||
|
||||
if self.AutoAssignTasks == true then
|
||||
self:ScheduleRepeat( 10, 30, 0, nil, self.AssignTasks, self )
|
||||
else
|
||||
self:ScheduleStop( self.AssignTasks )
|
||||
end
|
||||
|
||||
self:SetCommandCenterMenu()
|
||||
|
||||
end
|
||||
|
||||
--- Automatically accept tasks for all TaskGroups.
|
||||
-- When a task is assigned; the mission designer can decide if players are immediately assigned to the task; or they can accept/reject the assigned task.
|
||||
-- If the tasks are not automatically accepted; the player will receive a message that he needs to access the command center menu and
|
||||
-- choose from 2 added menu options either to accept or reject the assigned task within 30 seconds.
|
||||
-- If the task is not accepted within 30 seconds; the task will be cancelled and a new task will be assigned.
|
||||
-- @param #COMMANDCENTER self
|
||||
-- @param #boolean AutoAccept true for ON and false or nil for OFF.
|
||||
function COMMANDCENTER:SetAutoAcceptTasks( AutoAccept )
|
||||
|
||||
self.AutoAcceptTasks = AutoAccept or false
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Define the method to be used to assign automatically a task from the available tasks in the mission.
|
||||
-- There are 3 types of methods that can be applied for the moment:
|
||||
--
|
||||
-- 1. Random - assigns a random task in the mission to the player.
|
||||
-- 2. Distance - assigns a task based on a distance evaluation from the player. The closest are to be assigned first.
|
||||
-- 3. Priority - assigns a task based on the priority as defined by the mission designer, using the SetTaskPriority parameter.
|
||||
--
|
||||
-- The different task classes implement the logic to determine the priority of automatic task assignment to a player, depending on one of the above methods.
|
||||
-- The method @{Tasking.Task#TASK.GetAutoAssignPriority} calculate the priority of the tasks to be assigned.
|
||||
-- @param #COMMANDCENTER self
|
||||
-- @param #COMMANDCENTER.AutoAssignMethods AutoAssignMethod A selection of an assign method from the COMMANDCENTER.AutoAssignMethods enumeration.
|
||||
function COMMANDCENTER:SetAutoAssignMethod( AutoAssignMethod )
|
||||
|
||||
self.AutoAssignMethod = AutoAssignMethod or COMMANDCENTER.AutoAssignMethods.Random
|
||||
|
||||
end
|
||||
|
||||
--- Automatically assigns tasks to all TaskGroups.
|
||||
-- @param #COMMANDCENTER self
|
||||
@@ -531,12 +606,16 @@ function COMMANDCENTER:AssignTasks()
|
||||
for GroupID, TaskGroup in pairs( GroupSet:GetSet() ) do
|
||||
local TaskGroup = TaskGroup -- Wrapper.Group#GROUP
|
||||
|
||||
if self:IsGroupAssigned( TaskGroup ) then
|
||||
else
|
||||
-- Only groups with planes or helicopters will receive automatic tasks.
|
||||
-- TODO Workaround DCS-BUG-3 - https://github.com/FlightControl-Master/MOOSE/issues/696
|
||||
if TaskGroup:IsAir() then
|
||||
self:AssignRandomTask( TaskGroup )
|
||||
if TaskGroup:IsAlive() then
|
||||
self:GetMenu( TaskGroup )
|
||||
|
||||
if self:IsGroupAssigned( TaskGroup ) then
|
||||
else
|
||||
-- Only groups with planes or helicopters will receive automatic tasks.
|
||||
-- TODO Workaround DCS-BUG-3 - https://github.com/FlightControl-Master/MOOSE/issues/696
|
||||
if TaskGroup:IsAir() then
|
||||
self:AssignTask( TaskGroup )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -687,9 +766,9 @@ function COMMANDCENTER:ReportMissionsPlayers( ReportGroup )
|
||||
|
||||
Report:Add( "Players active in all missions." )
|
||||
|
||||
for MissionID, Mission in pairs( self.Missions ) do
|
||||
local Mission = Mission -- Tasking.Mission#MISSION
|
||||
Report:Add( " - " .. Mission:ReportPlayers() )
|
||||
for MissionID, MissionData in pairs( self.Missions ) do
|
||||
local Mission = MissionData -- Tasking.Mission#MISSION
|
||||
Report:Add( " - " .. Mission:ReportPlayersPerTask(ReportGroup) )
|
||||
end
|
||||
|
||||
self:MessageToGroup( Report:Text(), ReportGroup )
|
||||
@@ -713,3 +792,12 @@ function COMMANDCENTER:ReportDetails( ReportGroup, Task )
|
||||
self:MessageToGroup( Report:Text(), ReportGroup )
|
||||
end
|
||||
|
||||
|
||||
--- Let the command center flash a report of the status of the subscribed task to a group.
|
||||
-- @param #COMMANDCENTER self
|
||||
function COMMANDCENTER:SetFlashStatus( Flash )
|
||||
self:F()
|
||||
|
||||
self.FlashStatus = Flash or true
|
||||
|
||||
end
|
||||
|
||||
@@ -46,6 +46,7 @@ do -- DETECTION MANAGER
|
||||
--- @type DETECTION_MANAGER
|
||||
-- @field Core.Set#SET_GROUP SetGroup The groups to which the FAC will report to.
|
||||
-- @field Functional.Detection#DETECTION_BASE Detection The DETECTION_BASE object that is used to report the detected objects.
|
||||
-- @field Tasking.CommandCenter#COMMANDCENTER CC The command center that is used to communicate with the players.
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
--- DETECTION_MANAGER class.
|
||||
@@ -56,6 +57,9 @@ do -- DETECTION MANAGER
|
||||
Detection = nil,
|
||||
}
|
||||
|
||||
--- @field Tasking.CommandCenter#COMMANDCENTER
|
||||
DETECTION_MANAGER.CC = nil
|
||||
|
||||
--- FAC constructor.
|
||||
-- @param #DETECTION_MANAGER self
|
||||
-- @param Core.Set#SET_GROUP SetGroup
|
||||
@@ -217,13 +221,95 @@ do -- DETECTION MANAGER
|
||||
|
||||
return self._ReportDisplayTime
|
||||
end
|
||||
|
||||
|
||||
--- Set a command center to communicate actions to the players reporting to the command center.
|
||||
-- @param #DETECTION_MANAGER self
|
||||
-- @return #DETECTION_MANGER self
|
||||
function DETECTION_MANAGER:SetTacticalMenu( DispatcherMainMenuText, DispatcherMenuText )
|
||||
|
||||
local DispatcherMainMenu = MENU_MISSION:New( DispatcherMainMenuText, nil )
|
||||
local DispatcherMenu = MENU_MISSION_COMMAND:New( DispatcherMenuText, DispatcherMainMenu,
|
||||
function()
|
||||
self:ShowTacticalDisplay( self.Detection )
|
||||
end
|
||||
)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
--- Set a command center to communicate actions to the players reporting to the command center.
|
||||
-- @param #DETECTION_MANAGER self
|
||||
-- @param Tasking.CommandCenter#COMMANDCENTER CommandCenter The command center.
|
||||
-- @return #DETECTION_MANGER self
|
||||
function DETECTION_MANAGER:SetCommandCenter( CommandCenter )
|
||||
|
||||
self.CC = CommandCenter
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Get the command center to communicate actions to the players.
|
||||
-- @param #DETECTION_MANAGER self
|
||||
-- @return Tasking.CommandCenter#COMMANDCENTER The command center.
|
||||
function DETECTION_MANAGER:GetCommandCenter()
|
||||
|
||||
return self.CC
|
||||
end
|
||||
|
||||
|
||||
--- Send an information message to the players reporting to the command center.
|
||||
-- @param #DETECTION_MANAGER self
|
||||
-- @param #table Squadron The squadron table.
|
||||
-- @param #string Message The message to be sent.
|
||||
-- @param #string SoundFile The name of the sound file .wav or .ogg.
|
||||
-- @param #number SoundDuration The duration of the sound.
|
||||
-- @param #string SoundPath The path pointing to the folder in the mission file.
|
||||
-- @param Wrapper.Group#GROUP DefenderGroup The defender group sending the message.
|
||||
-- @return #DETECTION_MANGER self
|
||||
function DETECTION_MANAGER:MessageToPlayers( Squadron, Message, DefenderGroup )
|
||||
|
||||
self:F( { Message = Message } )
|
||||
|
||||
-- if not self.PreviousMessage or self.PreviousMessage ~= Message then
|
||||
-- self.PreviousMessage = Message
|
||||
-- if self.CC then
|
||||
-- self.CC:MessageToCoalition( Message )
|
||||
-- end
|
||||
-- end
|
||||
|
||||
if self.CC then
|
||||
self.CC:MessageToCoalition( Message )
|
||||
end
|
||||
|
||||
Message = Message:gsub( "°", " degrees " )
|
||||
Message = Message:gsub( "(%d)%.(%d)", "%1 dot %2" )
|
||||
|
||||
-- Here we handle the transmission of the voice over.
|
||||
-- If for a certain reason the Defender does not exist, we use the coordinate of the airbase to send the message from.
|
||||
local RadioQueue = Squadron.RadioQueue -- Core.RadioSpeech#RADIOSPEECH
|
||||
if RadioQueue then
|
||||
local DefenderUnit = DefenderGroup:GetUnit(1)
|
||||
if DefenderUnit and DefenderUnit:IsAlive() then
|
||||
RadioQueue:SetSenderUnitName( DefenderUnit:GetName() )
|
||||
end
|
||||
RadioQueue:Speak( Message, Squadron.Language )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Reports the detected items to the @{Core.Set#SET_GROUP}.
|
||||
-- @param #DETECTION_MANAGER self
|
||||
-- @param Functional.Detection#DETECTION_BASE Detection
|
||||
-- @return #DETECTION_MANAGER self
|
||||
function DETECTION_MANAGER:ProcessDetected( Detection )
|
||||
self:E()
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -968,18 +968,19 @@ function MISSION:ReportPlayersProgress( ReportGroup )
|
||||
-- Determine how many tasks are remaining.
|
||||
for TaskID, Task in pairs( self:GetTasks() ) do
|
||||
local Task = Task -- Tasking.Task#TASK
|
||||
local TaskGoalTotal = Task:GetGoalTotal() or 0
|
||||
local TaskName = Task:GetName()
|
||||
local Goal = Task:GetGoal()
|
||||
PlayerList[TaskName] = PlayerList[TaskName] or {}
|
||||
if TaskGoalTotal ~= 0 then
|
||||
local PlayerNames = self:GetPlayerNames()
|
||||
for PlayerName, PlayerData in pairs( PlayerNames ) do
|
||||
PlayerList[TaskName][PlayerName] = string.format( 'Player (%s): Task "%s": %d%%', PlayerName, TaskName, Task:GetPlayerProgress( PlayerName ) * 100 / TaskGoalTotal )
|
||||
if Goal then
|
||||
local TotalContributions = Goal:GetTotalContributions()
|
||||
local PlayerContributions = Goal:GetPlayerContributions()
|
||||
self:F( { TotalContributions = TotalContributions, PlayerContributions = PlayerContributions } )
|
||||
for PlayerName, PlayerContribution in pairs( PlayerContributions ) do
|
||||
PlayerList[TaskName][PlayerName] = string.format( 'Player (%s): Task "%s": %d%%', PlayerName, TaskName, PlayerContributions[PlayerName] * 100 / TotalContributions )
|
||||
end
|
||||
else
|
||||
PlayerList[TaskName]["_"] = string.format( 'Player (---): Task "%s": %d%%', TaskName, 0 )
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
for TaskName, TaskData in pairs( PlayerList ) do
|
||||
|
||||
@@ -856,6 +856,8 @@ do -- Group Assignment
|
||||
|
||||
CommandCenter:SetMenu()
|
||||
|
||||
self:MenuFlashTaskStatus( TaskGroup, self:GetMission():GetCommandCenter().FlashStatus )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -1111,6 +1113,11 @@ function TASK:SetAssignedMenuForGroup( TaskGroup, MenuTime )
|
||||
end
|
||||
local MarkMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Mark Task Location on Map" ), TaskControl, self.MenuMarkToGroup, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" )
|
||||
local TaskTypeMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Report Task Details" ), TaskControl, self.MenuTaskStatus, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" )
|
||||
if not self.FlashTaskStatus then
|
||||
local TaskFlashStatusMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Flash Task Details" ), TaskControl, self.MenuFlashTaskStatus, self, TaskGroup, true ):SetTime( MenuTime ):SetTag( "Tasking" )
|
||||
else
|
||||
local TaskFlashStatusMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Stop Flash Task Details" ), TaskControl, self.MenuFlashTaskStatus, self, TaskGroup, nil ):SetTime( MenuTime ):SetTag( "Tasking" )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1225,12 +1232,34 @@ end
|
||||
|
||||
--- Report the task status.
|
||||
-- @param #TASK self
|
||||
-- @param Wrapper.Group#GROUP TaskGroup
|
||||
function TASK:MenuTaskStatus( TaskGroup )
|
||||
|
||||
local ReportText = self:ReportDetails( TaskGroup )
|
||||
|
||||
self:T( ReportText )
|
||||
self:GetMission():GetCommandCenter():MessageTypeToGroup( ReportText, TaskGroup, MESSAGE.Type.Detailed )
|
||||
if TaskGroup:IsAlive() then
|
||||
|
||||
local ReportText = self:ReportDetails( TaskGroup )
|
||||
|
||||
self:T( ReportText )
|
||||
self:GetMission():GetCommandCenter():MessageTypeToGroup( ReportText, TaskGroup, MESSAGE.Type.Detailed )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Report the task status.
|
||||
-- @param #TASK self
|
||||
function TASK:MenuFlashTaskStatus( TaskGroup, Flash )
|
||||
|
||||
self.FlashTaskStatus = Flash
|
||||
|
||||
if self.FlashTaskStatus then
|
||||
self.FlashTaskScheduler, self.FlashTaskScheduleID = SCHEDULER:New( self, self.MenuTaskStatus, { TaskGroup }, 0, 60 )
|
||||
else
|
||||
if self.FlashTaskScheduler then
|
||||
self.FlashTaskScheduler:Stop( self.FlashTaskScheduleID )
|
||||
self.FlashTaskScheduler = nil
|
||||
self.FlashTaskScheduleID = nil
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1709,6 +1738,23 @@ end
|
||||
|
||||
do -- Links
|
||||
|
||||
--- Set goal of a task
|
||||
-- @param #TASK self
|
||||
-- @param Core.Goal#GOAL Goal
|
||||
-- @return #TASK
|
||||
function TASK:SetGoal( Goal )
|
||||
self.Goal = Goal
|
||||
end
|
||||
|
||||
|
||||
--- Get goal of a task
|
||||
-- @param #TASK self
|
||||
-- @return Core.Goal#GOAL The Goal
|
||||
function TASK:GetGoal()
|
||||
return self.Goal
|
||||
end
|
||||
|
||||
|
||||
--- Set dispatcher of a task
|
||||
-- @param #TASK self
|
||||
-- @param Tasking.DetectionManager#DETECTION_MANAGER Dispatcher
|
||||
@@ -1786,7 +1832,7 @@ function TASK:GetPlayerCount() --R2.1 Get a count of the players.
|
||||
if PlayerGroup:IsAlive() == true then
|
||||
if self:IsGroupAssigned( PlayerGroup ) then
|
||||
local PlayerNames = PlayerGroup:GetPlayerNames()
|
||||
PlayerCount = PlayerCount + #PlayerNames
|
||||
PlayerCount = PlayerCount + ((PlayerNames) and #PlayerNames or 0) -- PlayerNames can be nil when there are no players.
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1808,7 +1854,7 @@ function TASK:GetPlayerNames() --R2.1 Get a map of the players.
|
||||
if PlayerGroup:IsAlive() == true then
|
||||
if self:IsGroupAssigned( PlayerGroup ) then
|
||||
local PlayerNames = PlayerGroup:GetPlayerNames()
|
||||
for PlayerNameID, PlayerName in pairs( PlayerNames ) do
|
||||
for PlayerNameID, PlayerName in pairs( PlayerNames or {} ) do
|
||||
PlayerNameMap[PlayerName] = PlayerGroup
|
||||
end
|
||||
end
|
||||
|
||||
@@ -24,7 +24,7 @@ TASKINFO = {
|
||||
ClassName = "TASKINFO",
|
||||
}
|
||||
|
||||
--- @type #TASKINFO.Detail #string A string that flags to document which level of detail needs to be shown in the report.
|
||||
--- @type TASKINFO.Detail #string A string that flags to document which level of detail needs to be shown in the report.
|
||||
--
|
||||
-- - "M" for Markings on the Map (F10).
|
||||
-- - "S" for Summary Reports.
|
||||
@@ -52,16 +52,16 @@ end
|
||||
|
||||
--- Add taskinfo.
|
||||
-- @param #TASKINFO self
|
||||
-- @param #string The info key.
|
||||
-- @param #string Key The info key.
|
||||
-- @param Data The data of the info.
|
||||
-- @param #number Order The display order, which is a number from 0 to 100.
|
||||
-- @param #TASKINFO.Detail Detail The detail Level.
|
||||
-- @param #boolean Keep (optional) If true, this would indicate that the planned taskinfo would be persistent when the task is completed, so that the original planned task info is used at the completed reports.
|
||||
-- @return #TASKINFO self
|
||||
function TASKINFO:AddInfo( Key, Data, Order, Detail, Keep )
|
||||
self.VolatileInfo:Add( Key, { Data = Data, Order = Order, Detail = Detail } )
|
||||
function TASKINFO:AddInfo( Key, Data, Order, Detail, Keep, ShowKey, Type )
|
||||
self.VolatileInfo:Add( Key, { Data = Data, Order = Order, Detail = Detail, ShowKey = ShowKey, Type = Type } )
|
||||
if Keep == true then
|
||||
self.PersistentInfo:Add( Key, { Data = Data, Order = Order, Detail = Detail } )
|
||||
self.PersistentInfo:Add( Key, { Data = Data, Order = Order, Detail = Detail, ShowKey = ShowKey, Type = Type } )
|
||||
end
|
||||
return self
|
||||
end
|
||||
@@ -124,12 +124,21 @@ end
|
||||
-- @param #TASKINFO.Detail Detail The detail Level.
|
||||
-- @param #boolean Keep (optional) If true, this would indicate that the planned taskinfo would be persistent when the task is completed, so that the original planned task info is used at the completed reports.
|
||||
-- @return #TASKINFO self
|
||||
function TASKINFO:AddCoordinate( Coordinate, Order, Detail, Keep )
|
||||
self:AddInfo( "Coordinate", Coordinate, Order, Detail, Keep )
|
||||
function TASKINFO:AddCoordinate( Coordinate, Order, Detail, Keep, ShowKey, Name )
|
||||
self:AddInfo( Name or "Coordinate", Coordinate, Order, Detail, Keep, ShowKey, "Coordinate" )
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Get the Coordinate.
|
||||
-- @param #TASKINFO self
|
||||
-- @return Core.Point#COORDINATE Coordinate
|
||||
function TASKINFO:GetCoordinate( Name )
|
||||
return self:GetData( Name or "Coordinate" )
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Add Coordinates.
|
||||
-- @param #TASKINFO self
|
||||
-- @param #list<Core.Point#COORDINATE> Coordinates
|
||||
@@ -153,7 +162,7 @@ end
|
||||
-- @param #boolean Keep (optional) If true, this would indicate that the planned taskinfo would be persistent when the task is completed, so that the original planned task info is used at the completed reports.
|
||||
-- @return #TASKINFO self
|
||||
function TASKINFO:AddThreat( ThreatText, ThreatLevel, Order, Detail, Keep )
|
||||
self:AddInfo( "Threat", ThreatText .. " [" .. string.rep( "■", ThreatLevel ) .. string.rep( "□", 10 - ThreatLevel ) .. "]", Order, Detail, Keep )
|
||||
self:AddInfo( "Threat", " [" .. string.rep( "■", ThreatLevel ) .. string.rep( "□", 10 - ThreatLevel ) .. "]:" .. ThreatText, Order, Detail, Keep )
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -297,75 +306,63 @@ function TASKINFO:Report( Report, Detail, ReportGroup, Task )
|
||||
|
||||
for Key, Data in UTILS.spairs( self.Info.Set, function( t, a, b ) return t[a].Order < t[b].Order end ) do
|
||||
|
||||
self:F( { Key = Key, Detail = Detail, Data = Data } )
|
||||
|
||||
if Data.Detail:find( Detail ) then
|
||||
local Text = ""
|
||||
if Key == "TaskName" then
|
||||
local ShowKey = ( Data.ShowKey == nil or Data.ShowKey == true )
|
||||
if Key == "TaskName" then
|
||||
Key = nil
|
||||
Text = Data.Data
|
||||
end
|
||||
if Key == "Coordinate" then
|
||||
elseif Data.Type and Data.Type == "Coordinate" then
|
||||
local Coordinate = Data.Data -- Core.Point#COORDINATE
|
||||
Text = Coordinate:ToString( ReportGroup:GetUnit(1), nil, Task )
|
||||
end
|
||||
if Key == "Threat" then
|
||||
elseif Key == "Threat" then
|
||||
local DataText = Data.Data -- #string
|
||||
Text = DataText
|
||||
end
|
||||
if Key == "Counting" then
|
||||
elseif Key == "Counting" then
|
||||
local DataText = Data.Data -- #string
|
||||
Text = DataText
|
||||
end
|
||||
if Key == "Targets" then
|
||||
elseif Key == "Targets" then
|
||||
local DataText = Data.Data -- #string
|
||||
Text = DataText
|
||||
end
|
||||
if Key == "QFE" then
|
||||
elseif Key == "QFE" then
|
||||
local Coordinate = Data.Data -- Core.Point#COORDINATE
|
||||
Text = Coordinate:ToStringPressure( ReportGroup:GetUnit(1), nil, Task )
|
||||
end
|
||||
if Key == "Temperature" then
|
||||
elseif Key == "Temperature" then
|
||||
local Coordinate = Data.Data -- Core.Point#COORDINATE
|
||||
Text = Coordinate:ToStringTemperature( ReportGroup:GetUnit(1), nil, Task )
|
||||
end
|
||||
if Key == "Wind" then
|
||||
elseif Key == "Wind" then
|
||||
local Coordinate = Data.Data -- Core.Point#COORDINATE
|
||||
Text = Coordinate:ToStringWind( ReportGroup:GetUnit(1), nil, Task )
|
||||
end
|
||||
if Key == "Cargo" then
|
||||
elseif Key == "Cargo" then
|
||||
local DataText = Data.Data -- #string
|
||||
Text = DataText
|
||||
elseif Key == "Friendlies" then
|
||||
local DataText = Data.Data -- #string
|
||||
Text = DataText
|
||||
elseif Key == "Players" then
|
||||
local DataText = Data.Data -- #string
|
||||
Text = DataText
|
||||
else
|
||||
local DataText = Data.Data -- #string
|
||||
Text = DataText
|
||||
end
|
||||
if Key == "Friendlies" then
|
||||
local DataText = Data.Data -- #string
|
||||
Text = DataText
|
||||
end
|
||||
if Key == "Players" then
|
||||
local DataText = Data.Data -- #string
|
||||
Text = DataText
|
||||
end
|
||||
|
||||
|
||||
if Line < math.floor( Data.Order / 10 ) then
|
||||
if Line == 0 then
|
||||
if Text ~= "" then
|
||||
Report:AddIndent( LineReport:Text( ", " ), "-" )
|
||||
end
|
||||
Report:AddIndent( LineReport:Text( ", " ), "-" )
|
||||
else
|
||||
if Text ~= "" then
|
||||
Report:AddIndent( LineReport:Text( ", " ) )
|
||||
end
|
||||
Report:AddIndent( LineReport:Text( ", " ) )
|
||||
end
|
||||
LineReport = REPORT:New()
|
||||
Line = math.floor( Data.Order / 10 )
|
||||
end
|
||||
|
||||
if Text ~= "" then
|
||||
LineReport:Add( ( Key and ( Key .. ":" ) or "" ) .. Text )
|
||||
LineReport:Add( ( ( Key and ShowKey == true ) and ( Key .. ": " ) or "" ) .. Text )
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
Report:AddIndent( LineReport:Text( ", " ) )
|
||||
|
||||
end
|
||||
|
||||
@@ -292,7 +292,9 @@ do -- TASK_A2A
|
||||
|
||||
--- Return the relative distance to the target vicinity from the player, in order to sort the targets in the reports per distance from the threats.
|
||||
-- @param #TASK_A2A self
|
||||
function TASK_A2A:ReportOrder( ReportGroup )
|
||||
function TASK_A2A:ReportOrder( ReportGroup )
|
||||
self:UpdateTaskInfo( self.DetectedItem )
|
||||
|
||||
local Coordinate = self.TaskInfo:GetData( "Coordinate" )
|
||||
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
|
||||
|
||||
@@ -351,6 +353,26 @@ do -- TASK_A2A
|
||||
end
|
||||
end
|
||||
|
||||
--- This function is called from the @{Tasking.CommandCenter#COMMANDCENTER} to determine the method of automatic task selection.
|
||||
-- @param #TASK_A2A self
|
||||
-- @param #number AutoAssignMethod The method to be applied to the task.
|
||||
-- @param Tasking.CommandCenter#COMMANDCENTER CommandCenter The command center.
|
||||
-- @param Wrapper.Group#GROUP TaskGroup The player group.
|
||||
function TASK_A2A:GetAutoAssignPriority( AutoAssignMethod, CommandCenter, TaskGroup )
|
||||
|
||||
if AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Random then
|
||||
return math.random( 1, 9 )
|
||||
elseif AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Distance then
|
||||
local Coordinate = self.TaskInfo:GetData( "Coordinate" )
|
||||
local Distance = Coordinate:Get2DDistance( CommandCenter:GetPositionable():GetCoordinate() )
|
||||
return math.floor( Distance )
|
||||
elseif AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Priority then
|
||||
return 1
|
||||
end
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -295,6 +295,8 @@ do -- TASK_A2G
|
||||
--- Return the relative distance to the target vicinity from the player, in order to sort the targets in the reports per distance from the threats.
|
||||
-- @param #TASK_A2G self
|
||||
function TASK_A2G:ReportOrder( ReportGroup )
|
||||
self:UpdateTaskInfo( self.DetectedItem )
|
||||
|
||||
local Coordinate = self.TaskInfo:GetData( "Coordinate" )
|
||||
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
|
||||
|
||||
@@ -355,6 +357,27 @@ do -- TASK_A2G
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- This function is called from the @{Tasking.CommandCenter#COMMANDCENTER} to determine the method of automatic task selection.
|
||||
-- @param #TASK_A2G self
|
||||
-- @param #number AutoAssignMethod The method to be applied to the task.
|
||||
-- @param Tasking.CommandCenter#COMMANDCENTER CommandCenter The command center.
|
||||
-- @param Wrapper.Group#GROUP TaskGroup The player group.
|
||||
function TASK_A2G:GetAutoAssignPriority( AutoAssignMethod, CommandCenter, TaskGroup )
|
||||
|
||||
if AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Random then
|
||||
return math.random( 1, 9 )
|
||||
elseif AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Distance then
|
||||
local Coordinate = self.TaskInfo:GetData( "Coordinate" )
|
||||
local Distance = Coordinate:Get2DDistance( CommandCenter:GetPositionable():GetCoordinate() )
|
||||
self:F({Distance=Distance})
|
||||
return math.floor( Distance )
|
||||
elseif AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Priority then
|
||||
return 1
|
||||
end
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -633,7 +633,7 @@ do -- TASK_A2G_DISPATCHER
|
||||
--DetectedSet:Flush( self )
|
||||
|
||||
local DetectedItemID = DetectedItem.ID
|
||||
local TaskIndex = DetectedItem.ID
|
||||
local TaskIndex = DetectedItem.Index
|
||||
local DetectedItemChanged = DetectedItem.Changed
|
||||
|
||||
self:F( { DetectedItemChanged = DetectedItemChanged, DetectedItemID = DetectedItemID, TaskIndex = TaskIndex } )
|
||||
@@ -649,6 +649,7 @@ do -- TASK_A2G_DISPATCHER
|
||||
if TargetSetUnit then
|
||||
if Task:IsInstanceOf( TASK_A2G_SEAD ) then
|
||||
Task:SetTargetSetUnit( TargetSetUnit )
|
||||
Task:SetDetection( Detection, DetectedItem )
|
||||
Task:UpdateTaskInfo( DetectedItem )
|
||||
TargetsReport:Add( Detection:GetChangeText( DetectedItem ) )
|
||||
else
|
||||
@@ -659,7 +660,7 @@ do -- TASK_A2G_DISPATCHER
|
||||
if TargetSetUnit then
|
||||
if Task:IsInstanceOf( TASK_A2G_CAS ) then
|
||||
Task:SetTargetSetUnit( TargetSetUnit )
|
||||
Task:SetDetection( Detection, TaskIndex )
|
||||
Task:SetDetection( Detection, DetectedItem )
|
||||
Task:UpdateTaskInfo( DetectedItem )
|
||||
TargetsReport:Add( Detection:GetChangeText( DetectedItem ) )
|
||||
else
|
||||
@@ -671,7 +672,7 @@ do -- TASK_A2G_DISPATCHER
|
||||
if TargetSetUnit then
|
||||
if Task:IsInstanceOf( TASK_A2G_BAI ) then
|
||||
Task:SetTargetSetUnit( TargetSetUnit )
|
||||
Task:SetDetection( Detection, TaskIndex )
|
||||
Task:SetDetection( Detection, DetectedItem )
|
||||
Task:UpdateTaskInfo( DetectedItem )
|
||||
TargetsReport:Add( Detection:GetChangeText( DetectedItem ) )
|
||||
else
|
||||
|
||||
@@ -1381,6 +1381,23 @@ do -- TASK_CARGO
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
--- This function is called from the @{Tasking.CommandCenter#COMMANDCENTER} to determine the method of automatic task selection.
|
||||
-- @param #TASK_CARGO self
|
||||
-- @param #number AutoAssignMethod The method to be applied to the task.
|
||||
-- @param Wrapper.Group#GROUP TaskGroup The player group.
|
||||
function TASK_CARGO:GetAutoAssignPriority( AutoAssignMethod, TaskGroup )
|
||||
|
||||
if AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Random then
|
||||
return math.random( 1, 9 )
|
||||
elseif AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Distance then
|
||||
return 0
|
||||
elseif AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Priority then
|
||||
return 1
|
||||
end
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
396
Moose Development/Moose/Tasking/Task_Capture_Dispatcher.lua
Normal file
396
Moose Development/Moose/Tasking/Task_Capture_Dispatcher.lua
Normal file
@@ -0,0 +1,396 @@
|
||||
--- **Tasking** - Creates and manages player TASK_ZONE_CAPTURE tasks.
|
||||
--
|
||||
-- The **TASK_CAPTURE_DISPATCHER** allows you to setup various tasks for let human
|
||||
-- players capture zones in a co-operation effort.
|
||||
--
|
||||
-- The dispatcher will implement for you mechanisms to create capture zone tasks:
|
||||
--
|
||||
-- * As setup by the mission designer.
|
||||
-- * Dynamically capture zone tasks.
|
||||
--
|
||||
--
|
||||
--
|
||||
-- **Specific features:**
|
||||
--
|
||||
-- * Creates a task to capture zones and achieve mission goals.
|
||||
-- * Orchestrate the task flow, so go from Planned to Assigned to Success, Failed or Cancelled.
|
||||
-- * Co-operation tasking, so a player joins a group of players executing the same task.
|
||||
--
|
||||
--
|
||||
-- **A complete task menu system to allow players to:**
|
||||
--
|
||||
-- * Join the task, abort the task.
|
||||
-- * Mark the location of the zones to capture on the map.
|
||||
-- * Provide details of the zones.
|
||||
-- * Route to the zones.
|
||||
-- * Display the task briefing.
|
||||
--
|
||||
--
|
||||
-- **A complete mission menu system to allow players to:**
|
||||
--
|
||||
-- * Join a task, abort the task.
|
||||
-- * Display task reports.
|
||||
-- * Display mission statistics.
|
||||
-- * Mark the task locations on the map.
|
||||
-- * Provide details of the zones.
|
||||
-- * Display the mission briefing.
|
||||
-- * Provide status updates as retrieved from the command center.
|
||||
-- * Automatically assign a random task as part of a mission.
|
||||
-- * Manually assign a specific task as part of a mission.
|
||||
--
|
||||
--
|
||||
-- **A settings system, using the settings menu:**
|
||||
--
|
||||
-- * Tweak the duration of the display of messages.
|
||||
-- * Switch between metric and imperial measurement system.
|
||||
-- * Switch between coordinate formats used in messages: BR, BRA, LL DMS, LL DDM, MGRS.
|
||||
-- * Various other options.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module Tasking.Task_Zone_Capture_Dispatcher
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
do -- TASK_CAPTURE_DISPATCHER
|
||||
|
||||
--- TASK_CAPTURE_DISPATCHER class.
|
||||
-- @type TASK_CAPTURE_DISPATCHER
|
||||
-- @extends Tasking.Task_Manager#TASK_MANAGER
|
||||
-- @field TASK_CAPTURE_DISPATCHER.ZONE ZONE
|
||||
|
||||
--- @type TASK_CAPTURE_DISPATCHER.CSAR
|
||||
-- @field Wrapper.Unit#UNIT PilotUnit
|
||||
-- @field Tasking.Task#TASK Task
|
||||
|
||||
|
||||
--- Implements the dynamic dispatching of capture zone tasks.
|
||||
--
|
||||
-- The **TASK_CAPTURE_DISPATCHER** allows you to setup various tasks for let human
|
||||
-- players capture zones in a co-operation effort.
|
||||
--
|
||||
-- Let's explore **step by step** how to setup the task capture zone dispatcher.
|
||||
--
|
||||
-- # 1. Setup a mission environment.
|
||||
--
|
||||
-- It is easy, as it works just like any other task setup, so setup a command center and a mission.
|
||||
--
|
||||
-- ## 1.1. Create a command center.
|
||||
--
|
||||
-- First you need to create a command center using the @{Tasking.CommandCenter#COMMANDCENTER.New}() constructor.
|
||||
-- The command assumes that you´ve setup a group in the mission editor with the name HQ.
|
||||
-- This group will act as the command center object.
|
||||
-- It is a good practice to mark this group as invisible and invulnerable.
|
||||
--
|
||||
-- local CommandCenter = COMMANDCENTER
|
||||
-- :New( GROUP:FindByName( "HQ" ), "HQ" ) -- Create the CommandCenter.
|
||||
--
|
||||
-- ## 1.2. Create a mission.
|
||||
--
|
||||
-- Tasks work in a **mission**, which groups these tasks to achieve a joint **mission goal**. A command center can **govern multiple missions**.
|
||||
--
|
||||
-- Create a new mission, using the @{Tasking.Mission#MISSION.New}() constructor.
|
||||
--
|
||||
-- -- Declare the Mission for the Command Center.
|
||||
-- local Mission = MISSION
|
||||
-- :New( CommandCenter,
|
||||
-- "Overlord",
|
||||
-- "High",
|
||||
-- "Capture the blue zones.",
|
||||
-- coalition.side.RED
|
||||
-- )
|
||||
--
|
||||
--
|
||||
-- # 2. Dispatch a **capture zone** task.
|
||||
--
|
||||
-- So, now that we have a command center and a mission, we now create the capture zone task.
|
||||
-- We create the capture zone task using the @{#TASK_CAPTURE_DISPATCHER.AddCaptureZoneTask}() constructor.
|
||||
--
|
||||
-- ## 2.1. Create the capture zones.
|
||||
--
|
||||
-- Because a capture zone task will not generate the capture zones, you'll need to create them first.
|
||||
--
|
||||
--
|
||||
-- -- We define here a capture zone; of the type ZONE_CAPTURE_COALITION.
|
||||
-- -- The zone to be captured has the name Alpha, and was defined in the mission editor as a trigger zone.
|
||||
-- CaptureZone = ZONE:New( "Alpha" )
|
||||
-- CaptureZoneCoalitionApha = ZONE_CAPTURE_COALITION:New( CaptureZone, coalition.side.RED )
|
||||
--
|
||||
-- ## 2.2. Create a set of player groups.
|
||||
--
|
||||
-- What is also needed, is to have a set of @{Core.Group}s defined that contains the clients of the players.
|
||||
--
|
||||
-- -- Allocate the player slots, which must be aircraft (airplanes or helicopters), that can be manned by players.
|
||||
-- -- We use the method FilterPrefixes to filter those player groups that have client slots, as defined in the mission editor.
|
||||
-- -- In this example, we filter the groups where the name starts with "Blue Player", which captures the blue player slots.
|
||||
-- local PlayerGroupSet = SET_GROUP:New():FilterPrefixes( "Blue Player" ):FilterStart()
|
||||
--
|
||||
-- ## 2.3. Setup the capture zone task.
|
||||
--
|
||||
-- First, we need to create a TASK_CAPTURE_DISPATCHER object.
|
||||
--
|
||||
-- TaskCaptureZoneDispatcher = TASK_CAPTURE_DISPATCHER:New( Mission, PilotGroupSet )
|
||||
--
|
||||
-- So, the variable `TaskCaptureZoneDispatcher` will contain the object of class TASK_CAPTURE_DISPATCHER,
|
||||
-- which will allow you to dispatch capture zone tasks:
|
||||
--
|
||||
-- * for mission `Mission`, as was defined in section 1.2.
|
||||
-- * for the group set `PilotGroupSet`, as was defined in section 2.2.
|
||||
--
|
||||
-- Now that we have `TaskDispatcher` object, we can now **create the TaskCaptureZone**, using the @{#TASK_CAPTURE_DISPATCHER.AddCaptureZoneTask}() method!
|
||||
--
|
||||
-- local TaskCaptureZone = TaskCaptureZoneDispatcher:AddCaptureZoneTask(
|
||||
-- "Capture zone Alpha",
|
||||
-- CaptureZoneCoalitionAlpha,
|
||||
-- "Fly to zone Alpha and eliminate all enemy forces to capture it." )
|
||||
--
|
||||
-- As a result of this code, the `TaskCaptureZone` (returned) variable will contain an object of @{#TASK_CAPTURE_ZONE}!
|
||||
-- We pass to the method the title of the task, and the `CaptureZoneCoalitionAlpha`, which is the zone to be captured, as defined in section 2.1!
|
||||
-- This returned `TaskCaptureZone` object can now be used to setup additional task configurations, or to control this specific task with special events.
|
||||
--
|
||||
-- And you're done! As you can see, it is a small bit of work, but the reward is great.
|
||||
-- And, because all this is done using program interfaces, you can easily build a mission to capture zones yourself!
|
||||
-- Based on various events happening within your mission, you can use the above methods to create new capture zones,
|
||||
-- and setup a new capture zone task and assign it to a group of players, while your mission is running!
|
||||
--
|
||||
--
|
||||
--
|
||||
-- @field #TASK_CAPTURE_DISPATCHER
|
||||
TASK_CAPTURE_DISPATCHER = {
|
||||
ClassName = "TASK_CAPTURE_DISPATCHER",
|
||||
Mission = nil,
|
||||
Tasks = {},
|
||||
Zones = {},
|
||||
ZoneCount = 0,
|
||||
}
|
||||
|
||||
|
||||
|
||||
TASK_CAPTURE_DISPATCHER.AI_A2G_Dispatcher = nil -- AI.AI_A2G_Dispatcher#AI_A2G_DISPATCHER
|
||||
|
||||
--- TASK_CAPTURE_DISPATCHER constructor.
|
||||
-- @param #TASK_CAPTURE_DISPATCHER self
|
||||
-- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done.
|
||||
-- @param Core.Set#SET_GROUP SetGroup The set of groups that can join the tasks within the mission.
|
||||
-- @return #TASK_CAPTURE_DISPATCHER self
|
||||
function TASK_CAPTURE_DISPATCHER:New( Mission, SetGroup )
|
||||
|
||||
-- Inherits from DETECTION_MANAGER
|
||||
local self = BASE:Inherit( self, TASK_MANAGER:New( SetGroup ) ) -- #TASK_CAPTURE_DISPATCHER
|
||||
|
||||
self.Mission = Mission
|
||||
|
||||
self:AddTransition( "Started", "Assign", "Started" )
|
||||
self:AddTransition( "Started", "ZoneCaptured", "Started" )
|
||||
|
||||
self:__StartTasks( 5 )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Link a task capture dispatcher from the other coalition to understand its plan for defenses.
|
||||
-- This is used for the tactical overview, so the players also know the zones attacked by the other coalition!
|
||||
-- @param #TASK_CAPTURE_DISPATCHER self
|
||||
-- @param #TASK_CAPTURE_DISPATCHER DefenseTaskCaptureDispatcher
|
||||
function TASK_CAPTURE_DISPATCHER:SetDefenseTaskCaptureDispatcher( DefenseTaskCaptureDispatcher )
|
||||
|
||||
self.DefenseTaskCaptureDispatcher = DefenseTaskCaptureDispatcher
|
||||
end
|
||||
|
||||
|
||||
--- Get the linked task capture dispatcher from the other coalition to understand its plan for defenses.
|
||||
-- This is used for the tactical overview, so the players also know the zones attacked by the other coalition!
|
||||
-- @param #TASK_CAPTURE_DISPATCHER self
|
||||
-- @return #TASK_CAPTURE_DISPATCHER
|
||||
function TASK_CAPTURE_DISPATCHER:GetDefenseTaskCaptureDispatcher()
|
||||
|
||||
return self.DefenseTaskCaptureDispatcher
|
||||
end
|
||||
|
||||
|
||||
--- Link an AI A2G dispatcher from the other coalition to understand its plan for defenses.
|
||||
-- This is used for the tactical overview, so the players also know the zones attacked by the other AI A2G dispatcher!
|
||||
-- @param #TASK_CAPTURE_DISPATCHER self
|
||||
-- @param AI.AI_A2G_Dispatcher#AI_A2G_DISPATCHER DefenseAIA2GDispatcher
|
||||
function TASK_CAPTURE_DISPATCHER:SetDefenseAIA2GDispatcher( DefenseAIA2GDispatcher )
|
||||
|
||||
self.DefenseAIA2GDispatcher = DefenseAIA2GDispatcher
|
||||
end
|
||||
|
||||
|
||||
--- Get the linked AI A2G dispatcher from the other coalition to understand its plan for defenses.
|
||||
-- This is used for the tactical overview, so the players also know the zones attacked by the AI A2G dispatcher!
|
||||
-- @param #TASK_CAPTURE_DISPATCHER self
|
||||
-- @return AI.AI_A2G_Dispatcher#AI_A2G_DISPATCHER
|
||||
function TASK_CAPTURE_DISPATCHER:GetDefenseAIA2GDispatcher()
|
||||
|
||||
return self.DefenseAIA2GDispatcher
|
||||
end
|
||||
|
||||
|
||||
--- Add a capture zone task.
|
||||
-- @param #TASK_CAPTURE_DISPATCHER self
|
||||
-- @param #string TaskPrefix (optional) The prefix of the capture zone task.
|
||||
-- If no TaskPrefix is given, then "Capture" will be used as the TaskPrefix.
|
||||
-- The TaskPrefix will be appended with a . + a number of 3 digits, if the TaskPrefix already exists in the task collection.
|
||||
-- @param Functional.CaptureZoneCoalition#ZONE_CAPTURE_COALITION CaptureZone The zone of the coalition to be captured as the task goal.
|
||||
-- @param #string Briefing The briefing of the task to be shown to the player.
|
||||
-- @return Tasking.Task_Capture_Zone#TASK_CAPTURE_ZONE
|
||||
-- @usage
|
||||
--
|
||||
--
|
||||
function TASK_CAPTURE_DISPATCHER:AddCaptureZoneTask( TaskPrefix, CaptureZone, Briefing )
|
||||
|
||||
local TaskName = TaskPrefix or "Capture"
|
||||
if self.Zones[TaskName] then
|
||||
self.ZoneCount = self.ZoneCount + 1
|
||||
TaskName = string.format( "%s.%03d", TaskName, self.ZoneCount )
|
||||
end
|
||||
|
||||
self.Zones[TaskName] = {}
|
||||
self.Zones[TaskName].CaptureZone = CaptureZone
|
||||
self.Zones[TaskName].Briefing = Briefing
|
||||
self.Zones[TaskName].Task = nil
|
||||
self.Zones[TaskName].TaskPrefix = TaskPrefix
|
||||
|
||||
self:ManageTasks()
|
||||
|
||||
return self.Zones[TaskName] and self.Zones[TaskName].Task
|
||||
end
|
||||
|
||||
|
||||
--- Link an AI_A2G_DISPATCHER to the TASK_CAPTURE_DISPATCHER.
|
||||
-- @param #TASK_CAPTURE_DISPATCHER self
|
||||
-- @param AI.AI_A2G_Dispatcher#AI_A2G_DISPATCHER AI_A2G_Dispatcher The AI Dispatcher to be linked to the tasking.
|
||||
-- @return Tasking.Task_Capture_Zone#TASK_CAPTURE_ZONE
|
||||
function TASK_CAPTURE_DISPATCHER:Link_AI_A2G_Dispatcher( AI_A2G_Dispatcher )
|
||||
|
||||
self.AI_A2G_Dispatcher = AI_A2G_Dispatcher -- AI.AI_A2G_Dispatcher#AI_A2G_DISPATCHER
|
||||
AI_A2G_Dispatcher.Detection:LockDetectedItems()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Assigns tasks to the @{Core.Set#SET_GROUP}.
|
||||
-- @param #TASK_CAPTURE_DISPATCHER self
|
||||
-- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop.
|
||||
function TASK_CAPTURE_DISPATCHER:ManageTasks()
|
||||
self:F()
|
||||
|
||||
local AreaMsg = {}
|
||||
local TaskMsg = {}
|
||||
local ChangeMsg = {}
|
||||
|
||||
local Mission = self.Mission
|
||||
|
||||
if Mission:IsIDLE() or Mission:IsENGAGED() then
|
||||
|
||||
local TaskReport = REPORT:New()
|
||||
|
||||
-- Checking the task queue for the dispatcher, and removing any obsolete task!
|
||||
for TaskIndex, TaskData in pairs( self.Tasks ) do
|
||||
local Task = TaskData -- Tasking.Task#TASK
|
||||
if Task:IsStatePlanned() then
|
||||
-- Here we need to check if the pilot is still existing.
|
||||
-- Task = self:RemoveTask( TaskIndex )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- Now that all obsolete tasks are removed, loop through the Zone tasks.
|
||||
for TaskName, CaptureZone in pairs( self.Zones ) do
|
||||
|
||||
if not CaptureZone.Task then
|
||||
-- New Transport Task
|
||||
CaptureZone.Task = TASK_CAPTURE_ZONE:New( Mission, self.SetGroup, TaskName, CaptureZone.CaptureZone, CaptureZone.Briefing )
|
||||
CaptureZone.Task.TaskPrefix = CaptureZone.TaskPrefix -- We keep the TaskPrefix for further reference!
|
||||
Mission:AddTask( CaptureZone.Task )
|
||||
TaskReport:Add( TaskName )
|
||||
|
||||
-- Link the Task Dispatcher to the capture zone task, because it is used on the UpdateTaskInfo.
|
||||
CaptureZone.Task:SetDispatcher( self )
|
||||
CaptureZone.Task:UpdateTaskInfo()
|
||||
|
||||
function CaptureZone.Task.OnEnterAssigned( Task, From, Event, To )
|
||||
if self.AI_A2G_Dispatcher then
|
||||
self.AI_A2G_Dispatcher:Unlock( Task.TaskZoneName ) -- This will unlock the zone to be defended by AI.
|
||||
end
|
||||
CaptureZone.Task:UpdateTaskInfo()
|
||||
CaptureZone.Task.ZoneGoal.Attacked = true
|
||||
end
|
||||
|
||||
function CaptureZone.Task.OnEnterSuccess( Task, From, Event, To )
|
||||
--self:Success( Task )
|
||||
if self.AI_A2G_Dispatcher then
|
||||
self.AI_A2G_Dispatcher:Lock( Task.TaskZoneName ) -- This will lock the zone from being defended by AI.
|
||||
end
|
||||
CaptureZone.Task:UpdateTaskInfo()
|
||||
CaptureZone.Task.ZoneGoal.Attacked = false
|
||||
end
|
||||
|
||||
function CaptureZone.Task.OnEnterCancelled( Task, From, Event, To )
|
||||
self:Cancelled( Task )
|
||||
if self.AI_A2G_Dispatcher then
|
||||
self.AI_A2G_Dispatcher:Lock( Task.TaskZoneName ) -- This will lock the zone from being defended by AI.
|
||||
end
|
||||
CaptureZone.Task:UpdateTaskInfo()
|
||||
CaptureZone.Task.ZoneGoal.Attacked = false
|
||||
end
|
||||
|
||||
function CaptureZone.Task.OnEnterFailed( Task, From, Event, To )
|
||||
self:Failed( Task )
|
||||
if self.AI_A2G_Dispatcher then
|
||||
self.AI_A2G_Dispatcher:Lock( Task.TaskZoneName ) -- This will lock the zone from being defended by AI.
|
||||
end
|
||||
CaptureZone.Task:UpdateTaskInfo()
|
||||
CaptureZone.Task.ZoneGoal.Attacked = false
|
||||
end
|
||||
|
||||
function CaptureZone.Task.OnEnterAborted( Task, From, Event, To )
|
||||
self:Aborted( Task )
|
||||
if self.AI_A2G_Dispatcher then
|
||||
self.AI_A2G_Dispatcher:Lock( Task.TaskZoneName ) -- This will lock the zone from being defended by AI.
|
||||
end
|
||||
CaptureZone.Task:UpdateTaskInfo()
|
||||
CaptureZone.Task.ZoneGoal.Attacked = false
|
||||
end
|
||||
|
||||
-- Now broadcast the onafterCargoPickedUp event to the Task Cargo Dispatcher.
|
||||
function CaptureZone.Task.OnAfterCaptured( Task, From, Event, To, TaskUnit )
|
||||
self:Captured( Task, Task.TaskPrefix, TaskUnit )
|
||||
if self.AI_A2G_Dispatcher then
|
||||
self.AI_A2G_Dispatcher:Lock( Task.TaskZoneName ) -- This will lock the zone from being defended by AI.
|
||||
end
|
||||
CaptureZone.Task:UpdateTaskInfo()
|
||||
CaptureZone.Task.ZoneGoal.Attacked = false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
-- TODO set menus using the HQ coordinator
|
||||
Mission:GetCommandCenter():SetMenu()
|
||||
|
||||
local TaskText = TaskReport:Text(", ")
|
||||
|
||||
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
|
||||
if ( not Mission:IsGroupAssigned(TaskGroup) ) and TaskText ~= "" then
|
||||
Mission:GetCommandCenter():MessageToGroup( string.format( "%s has tasks %s. Subscribe to a task using the radio menu.", Mission:GetShortText(), TaskText ), TaskGroup )
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
end
|
||||
@@ -47,7 +47,7 @@ do -- TASK_ZONE_GOAL
|
||||
-- @param Tasking.Mission#MISSION Mission
|
||||
-- @param Core.Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned.
|
||||
-- @param #string TaskName The name of the Task.
|
||||
-- @param Core.ZoneGoal#ZONE_GOAL ZoneGoal
|
||||
-- @param Core.ZoneGoalCoalition#ZONE_GOAL_COALITION ZoneGoal
|
||||
-- @return #TASK_ZONE_GOAL self
|
||||
function TASK_ZONE_GOAL:New( Mission, SetGroup, TaskName, ZoneGoal, TaskType, TaskBriefing )
|
||||
local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, TaskType, TaskBriefing ) ) -- #TASK_ZONE_GOAL
|
||||
@@ -59,19 +59,25 @@ do -- TASK_ZONE_GOAL
|
||||
local Fsm = self:GetUnitProcess()
|
||||
|
||||
|
||||
Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "StartMonitoring", Rejected = "Reject" } )
|
||||
|
||||
Fsm:AddTransition( "Assigned", "StartMonitoring", "Monitoring" )
|
||||
Fsm:AddTransition( "Monitoring", "Monitor", "Monitoring", {} )
|
||||
Fsm:AddTransition( "Monitoring", "RouteTo", "Monitoring" )
|
||||
Fsm:AddProcess( "Monitoring", "RouteToZone", ACT_ROUTE_ZONE:New(), {} )
|
||||
|
||||
--Fsm:AddTransition( "Accounted", "DestroyedAll", "Accounted" )
|
||||
--Fsm:AddTransition( "Accounted", "Success", "Success" )
|
||||
Fsm:AddTransition( "Rejected", "Reject", "Aborted" )
|
||||
Fsm:AddTransition( "Failed", "Fail", "Failed" )
|
||||
|
||||
self:SetTargetZone( self.ZoneGoal:GetZone() )
|
||||
|
||||
--- Test
|
||||
-- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task#TASK Task
|
||||
function Fsm:OnAfterAssigned( TaskUnit, Task )
|
||||
self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
||||
|
||||
self:__StartMonitoring( 0.1 )
|
||||
self:__RouteToZone( 0.1 )
|
||||
end
|
||||
|
||||
--- Test
|
||||
-- @param #FSM_PROCESS self
|
||||
@@ -80,7 +86,6 @@ do -- TASK_ZONE_GOAL
|
||||
function Fsm:onafterStartMonitoring( TaskUnit, Task )
|
||||
self:F( { self } )
|
||||
self:__Monitor( 0.1 )
|
||||
self:__RouteTo( 0.1 )
|
||||
end
|
||||
|
||||
--- Monitor Loop
|
||||
@@ -101,7 +106,7 @@ do -- TASK_ZONE_GOAL
|
||||
-- Determine the first Unit from the self.TargetSetUnit
|
||||
|
||||
if Task:GetTargetZone( TaskUnit ) then
|
||||
self:__RouteTo( 0.1 )
|
||||
self:__RouteToZone( 0.1 )
|
||||
end
|
||||
end
|
||||
|
||||
@@ -160,37 +165,37 @@ do -- TASK_ZONE_GOAL
|
||||
end
|
||||
|
||||
|
||||
do -- TASK_ZONE_CAPTURE
|
||||
do -- TASK_CAPTURE_ZONE
|
||||
|
||||
--- The TASK_ZONE_CAPTURE class
|
||||
-- @type TASK_ZONE_CAPTURE
|
||||
--- The TASK_CAPTURE_ZONE class
|
||||
-- @type TASK_CAPTURE_ZONE
|
||||
-- @field Core.ZoneGoalCoalition#ZONE_GOAL_COALITION ZoneGoal
|
||||
-- @extends #TASK_ZONE_GOAL
|
||||
|
||||
--- # TASK_ZONE_CAPTURE class, extends @{Tasking.TaskZoneGoal#TASK_ZONE_GOAL}
|
||||
--- # TASK_CAPTURE_ZONE class, extends @{Tasking.TaskZoneGoal#TASK_ZONE_GOAL}
|
||||
--
|
||||
-- The TASK_ZONE_CAPTURE class defines an Suppression or Extermination of Air Defenses task for a human player to be executed.
|
||||
-- The TASK_CAPTURE_ZONE class defines an Suppression or Extermination of Air Defenses task for a human player to be executed.
|
||||
-- These tasks are important to be executed as they will help to achieve air superiority at the vicinity.
|
||||
--
|
||||
-- The TASK_ZONE_CAPTURE is used by the @{Tasking.Task_A2G_Dispatcher#TASK_A2G_DISPATCHER} to automatically create SEAD tasks
|
||||
-- The TASK_CAPTURE_ZONE is used by the @{Tasking.Task_A2G_Dispatcher#TASK_A2G_DISPATCHER} to automatically create SEAD tasks
|
||||
-- based on detected enemy ground targets.
|
||||
--
|
||||
-- @field #TASK_ZONE_CAPTURE
|
||||
TASK_ZONE_CAPTURE = {
|
||||
ClassName = "TASK_ZONE_CAPTURE",
|
||||
-- @field #TASK_CAPTURE_ZONE
|
||||
TASK_CAPTURE_ZONE = {
|
||||
ClassName = "TASK_CAPTURE_ZONE",
|
||||
}
|
||||
|
||||
|
||||
--- Instantiates a new TASK_ZONE_CAPTURE.
|
||||
-- @param #TASK_ZONE_CAPTURE self
|
||||
--- Instantiates a new TASK_CAPTURE_ZONE.
|
||||
-- @param #TASK_CAPTURE_ZONE self
|
||||
-- @param Tasking.Mission#MISSION Mission
|
||||
-- @param Core.Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned.
|
||||
-- @param #string TaskName The name of the Task.
|
||||
-- @param Core.ZoneGoalCoalition#ZONE_GOAL_COALITION ZoneGoalCoalition
|
||||
-- @param #string TaskBriefing The briefing of the task.
|
||||
-- @return #TASK_ZONE_CAPTURE self
|
||||
function TASK_ZONE_CAPTURE:New( Mission, SetGroup, TaskName, ZoneGoalCoalition, TaskBriefing)
|
||||
local self = BASE:Inherit( self, TASK_ZONE_GOAL:New( Mission, SetGroup, TaskName, ZoneGoalCoalition, "CAPTURE", TaskBriefing ) ) -- #TASK_ZONE_CAPTURE
|
||||
-- @return #TASK_CAPTURE_ZONE self
|
||||
function TASK_CAPTURE_ZONE:New( Mission, SetGroup, TaskName, ZoneGoalCoalition, TaskBriefing)
|
||||
local self = BASE:Inherit( self, TASK_ZONE_GOAL:New( Mission, SetGroup, TaskName, ZoneGoalCoalition, "CAPTURE", TaskBriefing ) ) -- #TASK_CAPTURE_ZONE
|
||||
self:F()
|
||||
|
||||
Mission:AddTask( self )
|
||||
@@ -206,42 +211,84 @@ do -- TASK_ZONE_CAPTURE
|
||||
"Capture Zone " .. self.TaskZoneName
|
||||
)
|
||||
|
||||
self:UpdateTaskInfo()
|
||||
self:UpdateTaskInfo( true )
|
||||
|
||||
self:SetGoal( self.ZoneGoal.Goal )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Instantiates a new TASK_ZONE_CAPTURE.
|
||||
-- @param #TASK_ZONE_CAPTURE self
|
||||
function TASK_ZONE_CAPTURE:UpdateTaskInfo()
|
||||
|
||||
|
||||
--- Instantiates a new TASK_CAPTURE_ZONE.
|
||||
-- @param #TASK_CAPTURE_ZONE self
|
||||
function TASK_CAPTURE_ZONE:UpdateTaskInfo( Persist )
|
||||
|
||||
Persist = Persist or false
|
||||
|
||||
local ZoneCoordinate = self.ZoneGoal:GetZone():GetCoordinate()
|
||||
self.TaskInfo:AddCoordinate( ZoneCoordinate, 0, "SOD" )
|
||||
self.TaskInfo:AddText( "Zone Name", self.ZoneGoal:GetZoneName(), 10, "MOD" )
|
||||
self.TaskInfo:AddText( "Zone Coalition", self.ZoneGoal:GetCoalitionName(), 11, "MOD" )
|
||||
self.TaskInfo:AddTaskName( 0, "MSOD", Persist )
|
||||
self.TaskInfo:AddCoordinate( ZoneCoordinate, 1, "SOD", Persist )
|
||||
-- self.TaskInfo:AddText( "Zone Name", self.ZoneGoal:GetZoneName(), 10, "MOD", Persist )
|
||||
-- self.TaskInfo:AddText( "Zone Coalition", self.ZoneGoal:GetCoalitionName(), 11, "MOD", Persist )
|
||||
local SetUnit = self.ZoneGoal:GetScannedSetUnit()
|
||||
local ThreatLevel, ThreatText = SetUnit:CalculateThreatLevelA2G()
|
||||
local ThreatCount = SetUnit:Count()
|
||||
self.TaskInfo:AddThreat( ThreatText, ThreatLevel, 20, "MOD", Persist )
|
||||
self.TaskInfo:AddInfo( "Remaining Units", ThreatCount, 21, "MOD", Persist, true)
|
||||
|
||||
if self.Dispatcher then
|
||||
local DefenseTaskCaptureDispatcher = self.Dispatcher:GetDefenseTaskCaptureDispatcher() -- Tasking.Task_Capture_Dispatcher#TASK_CAPTURE_DISPATCHER
|
||||
|
||||
if DefenseTaskCaptureDispatcher then
|
||||
-- Loop through all zones of the player Defenses, and check which zone has an assigned task!
|
||||
-- The Zones collection contains a Task. This Task is checked if it is assigned.
|
||||
-- If Assigned, then this task will be the task that is the closest to the defense zone.
|
||||
for TaskName, CaptureZone in pairs( DefenseTaskCaptureDispatcher.Zones or {} ) do
|
||||
local Task = CaptureZone.Task -- Tasking.Task_Capture_Zone#TASK_CAPTURE_ZONE
|
||||
if Task and Task:IsStateAssigned() then -- We also check assigned.
|
||||
-- Now we register the defense player zone information to the task report.
|
||||
self.TaskInfo:AddInfo( "Defense Player Zone", Task.ZoneGoal:GetName(), 30, "MOD", Persist )
|
||||
self.TaskInfo:AddCoordinate( Task.ZoneGoal:GetZone():GetCoordinate(), 31, "MOD", Persist, false, "Defense Player Coordinate" )
|
||||
end
|
||||
end
|
||||
end
|
||||
local DefenseAIA2GDispatcher = self.Dispatcher:GetDefenseAIA2GDispatcher() -- AI.AI_A2G_Dispatcher#AI_A2G_DISPATCHER
|
||||
|
||||
if DefenseAIA2GDispatcher then
|
||||
-- Loop through all the tasks of the AI Defenses, and check which zone is involved in the defenses and is active!
|
||||
for Defender, Task in pairs( DefenseAIA2GDispatcher:GetDefenderTasks() or {} ) do
|
||||
local DetectedItem = DefenseAIA2GDispatcher:GetDefenderTaskTarget( Defender )
|
||||
if DetectedItem then
|
||||
local DetectedZone = DefenseAIA2GDispatcher.Detection:GetDetectedItemZone( DetectedItem )
|
||||
if DetectedZone then
|
||||
self.TaskInfo:AddInfo( "Defense AI Zone", DetectedZone:GetName(), 40, "MOD", Persist )
|
||||
self.TaskInfo:AddCoordinate( DetectedZone:GetCoordinate(), 41, "MOD", Persist, false, "Defense AI Coordinate" )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
function TASK_ZONE_CAPTURE:ReportOrder( ReportGroup )
|
||||
local Coordinate = self:GetData( "Coordinate" )
|
||||
--local Coordinate = self.TaskInfo.Coordinates.TaskInfoText
|
||||
function TASK_CAPTURE_ZONE:ReportOrder( ReportGroup )
|
||||
|
||||
local Coordinate = self.TaskInfo:GetCoordinate()
|
||||
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
|
||||
|
||||
return Distance
|
||||
end
|
||||
|
||||
|
||||
--- @param #TASK_ZONE_CAPTURE self
|
||||
--- @param #TASK_CAPTURE_ZONE self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
function TASK_ZONE_CAPTURE:OnAfterGoal( From, Event, To, PlayerUnit, PlayerName )
|
||||
function TASK_CAPTURE_ZONE:OnAfterGoal( From, Event, To, PlayerUnit, PlayerName )
|
||||
|
||||
self:F( { PlayerUnit = PlayerUnit } )
|
||||
self:F( { PlayerUnit = PlayerUnit, Achieved = self.ZoneGoal.Goal:IsAchieved() } )
|
||||
|
||||
if self.ZoneGoal then
|
||||
if self.ZoneGoal.Goal:IsAchieved() then
|
||||
self:Success()
|
||||
local TotalContributions = self.ZoneGoal.Goal:GetTotalContributions()
|
||||
local PlayerContributions = self.ZoneGoal.Goal:GetPlayerContributions()
|
||||
self:F( { TotalContributions = TotalContributions, PlayerContributions = PlayerContributions } )
|
||||
@@ -251,11 +298,32 @@ do -- TASK_ZONE_CAPTURE
|
||||
Scoring:_AddMissionGoalScore( self.Mission, PlayerName, "Zone " .. self.ZoneGoal:GetZoneName() .." captured", PlayerContribution * 200 / TotalContributions )
|
||||
end
|
||||
end
|
||||
self:Success()
|
||||
end
|
||||
end
|
||||
|
||||
self:__Goal( -10, PlayerUnit, PlayerName )
|
||||
end
|
||||
|
||||
--- This function is called from the @{Tasking.CommandCenter#COMMANDCENTER} to determine the method of automatic task selection.
|
||||
-- @param #TASK_CAPTURE_ZONE self
|
||||
-- @param #number AutoAssignMethod The method to be applied to the task.
|
||||
-- @param Tasking.CommandCenter#COMMANDCENTER CommandCenter The command center.
|
||||
-- @param Wrapper.Group#GROUP TaskGroup The player group.
|
||||
function TASK_CAPTURE_ZONE:GetAutoAssignPriority( AutoAssignMethod, CommandCenter, TaskGroup, AutoAssignReference )
|
||||
|
||||
if AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Random then
|
||||
return math.random( 1, 9 )
|
||||
elseif AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Distance then
|
||||
local Coordinate = self.TaskInfo:GetCoordinate()
|
||||
local Distance = Coordinate:Get2DDistance( CommandCenter:GetPositionable():GetCoordinate() )
|
||||
return math.floor( Distance )
|
||||
elseif AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Priority then
|
||||
return 1
|
||||
end
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -185,7 +185,6 @@ do -- TASK_MANAGER
|
||||
-- @param #TASK_MANAGER self
|
||||
-- @return #TASK_MANAGER self
|
||||
function TASK_MANAGER:ManageTasks()
|
||||
self:E()
|
||||
|
||||
end
|
||||
|
||||
|
||||
245
Moose Development/Moose/Utilities/Enums.lua
Normal file
245
Moose Development/Moose/Utilities/Enums.lua
Normal file
@@ -0,0 +1,245 @@
|
||||
--- **Utilities** Enumerators.
|
||||
--
|
||||
-- An enumerator is a variable that holds a constant value. Enumerators are very useful because they make the code easier to read and to change in general.
|
||||
--
|
||||
-- For example, instead of using the same value at multiple different places in your code, you should use a variable set to that value.
|
||||
-- If, for whatever reason, the value needs to be changed, you only have to change the variable once and do not have to search through you code and reset
|
||||
-- every value by hand.
|
||||
--
|
||||
-- Another big advantage is that the LDT intellisense "knows" the enumerators. So you can use the autocompletion feature and do not have to keep all the
|
||||
-- values in your head or look them up in the docs.
|
||||
--
|
||||
-- DCS itself provides a lot of enumerators for various things. See [Enumerators](https://wiki.hoggitworld.com/view/Category:Enumerators) on Hoggit.
|
||||
--
|
||||
-- Other Moose classe also have enumerators. For example, the AIRBASE class has enumerators for airbase names.
|
||||
--
|
||||
-- @module ENUMS
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
--- [DCS Enum world](https://wiki.hoggitworld.com/view/DCS_enum_world)
|
||||
-- @type ENUMS
|
||||
|
||||
--- Because ENUMS are just better practice.
|
||||
--
|
||||
-- The ENUMS class adds some handy variables, which help you to make your code better and more general.
|
||||
--
|
||||
-- @field #ENUMS
|
||||
ENUMS = {}
|
||||
|
||||
--- Rules of Engagement.
|
||||
-- @type ENUMS.ROE
|
||||
-- @field #number WeaponFree AI will engage any enemy group it detects. Target prioritization is based based on the threat of the target.
|
||||
-- @field #number OpenFireWeaponFree AI will engage any enemy group it detects, but will prioritize targets specified in the groups tasking.
|
||||
-- @field #number OpenFire AI will engage only targets specified in its taskings.
|
||||
-- @field #number ReturnFire AI will only engage threats that shoot first.
|
||||
-- @field #number WeaponHold AI will hold fire under all circumstances.
|
||||
ENUMS.ROE = {
|
||||
WeaponFree=0,
|
||||
OpenFireWeaponFree=1,
|
||||
OpenFire=2,
|
||||
ReturnFire=3,
|
||||
WeaponHold=4,
|
||||
}
|
||||
|
||||
--- Reaction On Threat.
|
||||
-- @type ENUMS.ROT
|
||||
-- @field #number NoReaction No defensive actions will take place to counter threats.
|
||||
-- @field #number PassiveDefense AI will use jammers and other countermeasures in an attempt to defeat the threat. AI will not attempt a maneuver to defeat a threat.
|
||||
-- @field #number EvadeFire AI will react by performing defensive maneuvers against incoming threats. AI will also use passive defense.
|
||||
-- @field #number BypassAndEscape AI will attempt to avoid enemy threat zones all together. This includes attempting to fly above or around threats.
|
||||
-- @field #number AllowAbortMission If a threat is deemed severe enough the AI will abort its mission and return to base.
|
||||
ENUMS.ROT = {
|
||||
NoReaction=0,
|
||||
PassiveDefense=1,
|
||||
EvadeFire=2,
|
||||
BypassAndEscape=3,
|
||||
AllowAbortMission=4,
|
||||
}
|
||||
|
||||
--- Weapon types. See the [Weapon Flag](https://wiki.hoggitworld.com/view/DCS_enum_weapon_flag) enumerotor on hoggit wiki.
|
||||
-- @type ENUMS.WeaponFlag
|
||||
ENUMS.WeaponFlag={
|
||||
-- Bombs
|
||||
LGB = 2,
|
||||
TvGB = 4,
|
||||
SNSGB = 8,
|
||||
HEBomb = 16,
|
||||
Penetrator = 32,
|
||||
NapalmBomb = 64,
|
||||
FAEBomb = 128,
|
||||
ClusterBomb = 256,
|
||||
Dispencer = 512,
|
||||
CandleBomb = 1024,
|
||||
ParachuteBomb = 2147483648,
|
||||
-- Rockets
|
||||
LightRocket = 2048,
|
||||
MarkerRocket = 4096,
|
||||
CandleRocket = 8192,
|
||||
HeavyRocket = 16384,
|
||||
-- Air-To-Surface Missiles
|
||||
AntiRadarMissile = 32768,
|
||||
AntiShipMissile = 65536,
|
||||
AntiTankMissile = 131072,
|
||||
FireAndForgetASM = 262144,
|
||||
LaserASM = 524288,
|
||||
TeleASM = 1048576,
|
||||
CruiseMissile = 2097152,
|
||||
AntiRadarMissile2 = 1073741824,
|
||||
-- Air-To-Air Missiles
|
||||
SRAM = 4194304,
|
||||
MRAAM = 8388608,
|
||||
LRAAM = 16777216,
|
||||
IR_AAM = 33554432,
|
||||
SAR_AAM = 67108864,
|
||||
AR_AAM = 134217728,
|
||||
--- Guns
|
||||
GunPod = 268435456,
|
||||
BuiltInCannon = 536870912,
|
||||
---
|
||||
-- Combinations
|
||||
--
|
||||
-- Bombs
|
||||
GuidedBomb = 14, -- (LGB + TvGB + SNSGB)
|
||||
AnyUnguidedBomb = 2147485680, -- (HeBomb + Penetrator + NapalmBomb + FAEBomb + ClusterBomb + Dispencer + CandleBomb + ParachuteBomb)
|
||||
AnyBomb = 2147485694, -- (GuidedBomb + AnyUnguidedBomb)
|
||||
--- Rockets
|
||||
AnyRocket = 30720, -- LightRocket + MarkerRocket + CandleRocket + HeavyRocket
|
||||
--- Air-To-Surface Missiles
|
||||
GuidedASM = 1572864, -- (LaserASM + TeleASM)
|
||||
TacticalASM = 1835008, -- (GuidedASM + FireAndForgetASM)
|
||||
AnyASM = 4161536, -- (AntiRadarMissile + AntiShipMissile + AntiTankMissile + FireAndForgetASM + GuidedASM + CruiseMissile)
|
||||
AnyASM2 = 1077903360, -- 4161536+1073741824,
|
||||
--- Air-To-Air Missiles
|
||||
AnyAAM = 264241152, -- IR_AAM + SAR_AAM + AR_AAM + SRAAM + MRAAM + LRAAM
|
||||
AnyAutonomousMissile = 36012032, -- IR_AAM + AntiRadarMissile + AntiShipMissile + FireAndForgetASM + CruiseMissile
|
||||
AnyMissile = 268402688, -- AnyASM + AnyAAM
|
||||
--- Guns
|
||||
Cannons = 805306368, -- GUN_POD + BuiltInCannon
|
||||
---
|
||||
-- Even More Genral
|
||||
Auto = 3221225470, -- Any Weapon (AnyBomb + AnyRocket + AnyMissile + Cannons)
|
||||
AutoDCS = 1073741822, -- Something if often see
|
||||
AnyAG = 2956984318, -- Any Air-To-Ground Weapon
|
||||
AnyAA = 264241152, -- Any Air-To-Air Weapon
|
||||
AnyUnguided = 2952822768, -- Any Unguided Weapon
|
||||
AnyGuided = 268402702, -- Any Guided Weapon
|
||||
}
|
||||
|
||||
--- Mission tasks.
|
||||
-- @type ENUMS.MissionTask
|
||||
-- @field #string NOTHING No special task. Group can perform the minimal tasks: Orbit, Refuelling, Follow and Aerobatics.
|
||||
-- @field #string AFAC Forward Air Controller Air. Can perform the tasks: Attack Group, Attack Unit, FAC assign group, Bombing, Attack Map Object.
|
||||
-- @field #string ANTISHIPSTRIKE Naval ops. Can perform the tasks: Attack Group, Attack Unit.
|
||||
-- @field #string AWACS AWACS.
|
||||
-- @field #string CAP Combat Air Patrol.
|
||||
-- @field #string CAS Close Air Support.
|
||||
-- @field #string ESCORT Escort another group.
|
||||
-- @field #string FIGHTERSWEEP Fighter sweep.
|
||||
-- @field #string GROUNDATTACK Ground attack.
|
||||
-- @field #string INTERCEPT Intercept.
|
||||
-- @field #string PINPOINTSTRIKE Pinpoint strike.
|
||||
-- @field #string RECONNAISSANCE Reconnaissance mission.
|
||||
-- @field #string REFUELING Refueling mission.
|
||||
-- @field #string RUNWAYATTACK Attack the runway of an airdrome.
|
||||
-- @field #string SEAD Suppression of Enemy Air Defenses.
|
||||
-- @field #string TRANSPORT Troop transport.
|
||||
ENUMS.MissionTask={
|
||||
NOTHING="Nothing",
|
||||
AFAC="AFAC",
|
||||
ANTISHIPSTRIKE="Antiship Strike",
|
||||
AWACS="AWACS",
|
||||
CAP="CAP",
|
||||
CAS="CAS",
|
||||
ESCORT="Escort",
|
||||
FIGHTERSWEEP="Fighter Sweep",
|
||||
GROUNDATTACK="Ground Attack",
|
||||
INTERCEPT="Intercept",
|
||||
PINPOINTSTRIKE="Pinpoint Strike",
|
||||
RECONNAISSANCE="Reconnaissance",
|
||||
REFUELING="Refueling",
|
||||
RUNWAYATTACK="Runway Attack",
|
||||
SEAD="SEAD",
|
||||
TRANSPORT="Transport",
|
||||
}
|
||||
|
||||
--- Formations (new). See the [Formations](https://wiki.hoggitworld.com/view/DCS_enum_formation) on hoggit wiki.
|
||||
-- @type ENUMS.Formation
|
||||
ENUMS.Formation={}
|
||||
ENUMS.Formation.FixedWing={}
|
||||
ENUMS.Formation.FixedWing.LineAbreast={}
|
||||
ENUMS.Formation.FixedWing.LineAbreast.Close = 65537
|
||||
ENUMS.Formation.FixedWing.LineAbreast.Open = 65538
|
||||
ENUMS.Formation.FixedWing.LineAbreast.Group = 65539
|
||||
ENUMS.Formation.FixedWing.Trail={}
|
||||
ENUMS.Formation.FixedWing.Trail.Close = 131073
|
||||
ENUMS.Formation.FixedWing.Trail.Open = 131074
|
||||
ENUMS.Formation.FixedWing.Trail.Group = 131075
|
||||
ENUMS.Formation.FixedWing.Wedge={}
|
||||
ENUMS.Formation.FixedWing.Wedge.Close = 196609
|
||||
ENUMS.Formation.FixedWing.Wedge.Open = 196610
|
||||
ENUMS.Formation.FixedWing.Wedge.Group = 196611
|
||||
ENUMS.Formation.FixedWing.EchelonRight={}
|
||||
ENUMS.Formation.FixedWing.EchelonRight.Close = 262145
|
||||
ENUMS.Formation.FixedWing.EchelonRight.Open = 262146
|
||||
ENUMS.Formation.FixedWing.EchelonRight.Group = 262147
|
||||
ENUMS.Formation.FixedWing.EchelonLeft={}
|
||||
ENUMS.Formation.FixedWing.EchelonLeft.Close = 327681
|
||||
ENUMS.Formation.FixedWing.EchelonLeft.Open = 327682
|
||||
ENUMS.Formation.FixedWing.EchelonLeft.Group = 327683
|
||||
ENUMS.Formation.FixedWing.FingerFour={}
|
||||
ENUMS.Formation.FixedWing.FingerFour.Close = 393217
|
||||
ENUMS.Formation.FixedWing.FingerFour.Open = 393218
|
||||
ENUMS.Formation.FixedWing.FingerFour.Group = 393219
|
||||
ENUMS.Formation.FixedWing.Spread={}
|
||||
ENUMS.Formation.FixedWing.Spread.Close = 458753
|
||||
ENUMS.Formation.FixedWing.Spread.Open = 458754
|
||||
ENUMS.Formation.FixedWing.Spread.Group = 458755
|
||||
ENUMS.Formation.FixedWing.BomberElement={}
|
||||
ENUMS.Formation.FixedWing.BomberElement.Close = 786433
|
||||
ENUMS.Formation.FixedWing.BomberElement.Open = 786434
|
||||
ENUMS.Formation.FixedWing.BomberElement.Group = 786435
|
||||
ENUMS.Formation.FixedWing.BomberElementHeight={}
|
||||
ENUMS.Formation.FixedWing.BomberElementHeight.Close = 851968
|
||||
ENUMS.Formation.FixedWing.FighterVic={}
|
||||
ENUMS.Formation.FixedWing.FighterVic.Close = 917505
|
||||
ENUMS.Formation.FixedWing.FighterVic.Open = 917506
|
||||
ENUMS.Formation.RotaryWing={}
|
||||
ENUMS.Formation.RotaryWing.Column={}
|
||||
ENUMS.Formation.RotaryWing.Column.D70=720896
|
||||
ENUMS.Formation.RotaryWing.Wedge={}
|
||||
ENUMS.Formation.RotaryWing.Wedge.D70=8
|
||||
ENUMS.Formation.RotaryWing.FrontRight={}
|
||||
ENUMS.Formation.RotaryWing.FrontRight.D300=655361
|
||||
ENUMS.Formation.RotaryWing.FrontRight.D600=655362
|
||||
ENUMS.Formation.RotaryWing.FrontLeft={}
|
||||
ENUMS.Formation.RotaryWing.FrontLeft.D300=655617
|
||||
ENUMS.Formation.RotaryWing.FrontLeft.D600=655618
|
||||
ENUMS.Formation.RotaryWing.EchelonRight={}
|
||||
ENUMS.Formation.RotaryWing.EchelonRight.D70 =589825
|
||||
ENUMS.Formation.RotaryWing.EchelonRight.D300=589826
|
||||
ENUMS.Formation.RotaryWing.EchelonRight.D600=589827
|
||||
ENUMS.Formation.RotaryWing.EchelonLeft={}
|
||||
ENUMS.Formation.RotaryWing.EchelonLeft.D70 =590081
|
||||
ENUMS.Formation.RotaryWing.EchelonLeft.D300=590082
|
||||
ENUMS.Formation.RotaryWing.EchelonLeft.D600=590083
|
||||
|
||||
--- Formations (old). The old format is a simplified version of the new formation enums, which allow more sophisticated settings.
|
||||
-- See the [Formations](https://wiki.hoggitworld.com/view/DCS_enum_formation) on hoggit wiki.
|
||||
-- @type ENUMS.FormationOld
|
||||
ENUMS.FormationOld={}
|
||||
ENUMS.FormationOld.FixedWing={}
|
||||
ENUMS.FormationOld.FixedWing.LineAbreast=1
|
||||
ENUMS.FormationOld.FixedWing.Trail=2
|
||||
ENUMS.FormationOld.FixedWing.Wedge=3
|
||||
ENUMS.FormationOld.FixedWing.EchelonRight=4
|
||||
ENUMS.FormationOld.FixedWing.EchelonLeft=5
|
||||
ENUMS.FormationOld.FixedWing.FingerFour=6
|
||||
ENUMS.FormationOld.FixedWing.SpreadFour=7
|
||||
ENUMS.FormationOld.FixedWing.BomberElement=12
|
||||
ENUMS.FormationOld.FixedWing.BomberElementHeight=13
|
||||
ENUMS.FormationOld.FixedWing.FighterVic=14
|
||||
ENUMS.FormationOld.RotaryWing={}
|
||||
ENUMS.FormationOld.RotaryWing.Wedge=8
|
||||
ENUMS.FormationOld.RotaryWing.Echelon=9
|
||||
ENUMS.FormationOld.RotaryWing.Front=10
|
||||
ENUMS.FormationOld.RotaryWing.Column=11
|
||||
@@ -21,6 +21,11 @@ routines.build = 22
|
||||
-- Utils- conversion, Lua utils, etc.
|
||||
routines.utils = {}
|
||||
|
||||
routines.utils.round = function(number, decimals)
|
||||
local power = 10^decimals
|
||||
return math.floor(number * power) / power
|
||||
end
|
||||
|
||||
--from http://lua-users.org/wiki/CopyTable
|
||||
routines.utils.deepCopy = function(object)
|
||||
local lookup_table = {}
|
||||
@@ -1626,6 +1631,11 @@ function routines.getGroupRoute(groupIdent, task) -- same as getGroupPoints bu
|
||||
|
||||
for point_num, point in pairs(group_data.route.points) do
|
||||
local routeData = {}
|
||||
if env.mission.version > 7 then
|
||||
routeData.name = env.getValueDictByKey(point.name)
|
||||
else
|
||||
routeData.name = point.name
|
||||
end
|
||||
if not point.point then
|
||||
routeData.x = point.x
|
||||
routeData.y = point.y
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user