mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-08-15 10:47:21 +00:00
Compare commits
438 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a406604bd | ||
|
|
55cee46a8d | ||
|
|
607c52c0b7 | ||
|
|
2694321256 | ||
|
|
e8e790102a | ||
|
|
9c5561921b | ||
|
|
78fab9ab0c | ||
|
|
058c750bc6 | ||
|
|
f29da39dff | ||
|
|
624a7c70c9 | ||
|
|
0447ee2d9e | ||
|
|
456fcd38d0 | ||
|
|
a3cab7097a | ||
|
|
848e2f1294 | ||
|
|
ef4dc48ea1 | ||
|
|
2138a33292 | ||
|
|
a59343b987 | ||
|
|
a4ca4bdc99 | ||
|
|
a57b9a9081 | ||
|
|
249a6af456 | ||
|
|
18685d1a94 | ||
|
|
493b090534 | ||
|
|
389adab9b8 | ||
|
|
6360b8c58f | ||
|
|
b0818977cf | ||
|
|
32deb160ef | ||
|
|
2ba5215036 | ||
|
|
73ea4c7b32 | ||
|
|
46c37ff06a | ||
|
|
ff4708b624 | ||
|
|
82b2c84f13 | ||
|
|
48e8b1a9b3 | ||
|
|
1ad7c54ace | ||
|
|
9998c86c1f | ||
|
|
01a707ae0a | ||
|
|
570e8388fc | ||
|
|
9c5b5d4633 | ||
|
|
822bf13626 | ||
|
|
c520de0087 | ||
|
|
0e9076efa3 | ||
|
|
74cd5e3387 | ||
|
|
65c92be09e | ||
|
|
8e776cb3ab | ||
|
|
ab6cd2b751 | ||
|
|
18c3d990fc | ||
|
|
19d5cb8ecb | ||
|
|
2b56a78255 | ||
|
|
176d9df476 | ||
|
|
c94275cb8b | ||
|
|
45dbce3677 | ||
|
|
18745158a3 | ||
|
|
98c6c88391 | ||
|
|
2e4fd72781 | ||
|
|
80ced88ef1 | ||
|
|
8af3f89c14 | ||
|
|
fe3079caad | ||
|
|
61ac6b4131 | ||
|
|
36cb189512 | ||
|
|
15f9843878 | ||
|
|
67f847dd16 | ||
|
|
8b9143d3f1 | ||
|
|
0388d47f23 | ||
|
|
de9b173d9b | ||
|
|
2cecc526fb | ||
|
|
968d178317 | ||
|
|
3c477b872a | ||
|
|
77e6088114 | ||
|
|
edd6594953 | ||
|
|
f8c05c99d0 | ||
|
|
50f6d98b49 | ||
|
|
147eeb05f6 | ||
|
|
d8cb15a577 | ||
|
|
0daac876ea | ||
|
|
1832125022 | ||
|
|
c311c40b72 | ||
|
|
db516a2077 | ||
|
|
ff8766669c | ||
|
|
06dc9a732e | ||
|
|
50c74d0852 | ||
|
|
1c97eb6f3c | ||
|
|
69449430d1 | ||
|
|
663cd34aa3 | ||
|
|
cfed6f5153 | ||
|
|
2b22d5288c | ||
|
|
a64424ecc8 | ||
|
|
fd1b2ecb86 | ||
|
|
6cae3e62cf | ||
|
|
05ce7e4513 | ||
|
|
136bd19f19 | ||
|
|
8873504daf | ||
|
|
a844a5d697 | ||
|
|
a49f4eaa21 | ||
|
|
e6e2651f8c | ||
|
|
b93ba13644 | ||
|
|
e4a51951b0 | ||
|
|
ad56e39942 | ||
|
|
ea09dc5a6e | ||
|
|
8ecfd913a3 | ||
|
|
53367c786e | ||
|
|
db5797bb4e | ||
|
|
5e8fe97752 | ||
|
|
393fa0bfbb | ||
|
|
4f51884b9d | ||
|
|
4c5c320073 | ||
|
|
9098590568 | ||
|
|
555bb7e68b | ||
|
|
c0a18957f0 | ||
|
|
2cf939560e | ||
|
|
9d3a7aae78 | ||
|
|
f6ed592f92 | ||
|
|
c98757d13c | ||
|
|
17378f509e | ||
|
|
7f18ea0e7a | ||
|
|
5172619cb1 | ||
|
|
3962529698 | ||
|
|
6481d5d41e | ||
|
|
6cc3d73c04 | ||
|
|
e541e39403 | ||
|
|
c7ea45e5fd | ||
|
|
20f28b3d2c | ||
|
|
f3f63ab8aa | ||
|
|
e91090cfff | ||
|
|
1a7fb3c13e | ||
|
|
59857ed79d | ||
|
|
4797665939 | ||
|
|
b89749036d | ||
|
|
c6268488de | ||
|
|
de04369703 | ||
|
|
05b6f19a87 | ||
|
|
2753df8216 | ||
|
|
33d761503d | ||
|
|
b52272e18e | ||
|
|
70fa1cf19f | ||
|
|
7ca7caea75 | ||
|
|
277c26821e | ||
|
|
22826b4cd1 | ||
|
|
8cc1c24b64 | ||
|
|
0db35a0e9f | ||
|
|
b4707bb3eb | ||
|
|
bbfc2e9ed7 | ||
|
|
2fb0ab1aed | ||
|
|
6fbf050b72 | ||
|
|
29210f670c | ||
|
|
18b45c9621 | ||
|
|
9356d67d74 | ||
|
|
5bb2b05f71 | ||
|
|
89308f7d06 | ||
|
|
353d6dfec0 | ||
|
|
3f5e322948 | ||
|
|
0e8732fd44 | ||
|
|
77a3c7369d | ||
|
|
8d8070bbd7 | ||
|
|
72df687ce7 | ||
|
|
246935622c | ||
|
|
078573629d | ||
|
|
973b8323c9 | ||
|
|
6d61c5ee94 | ||
|
|
3e8db6a1fa | ||
|
|
832d6b1c08 | ||
|
|
e7936950f4 | ||
|
|
831e986ee8 | ||
|
|
8a44fae3d4 | ||
|
|
0c9390914a | ||
|
|
4fa525a4ae | ||
|
|
858b00336b | ||
|
|
82d78c98bb | ||
|
|
06c3f7998b | ||
|
|
e03e87f501 | ||
|
|
cf83abfe90 | ||
|
|
5a00f461e9 | ||
|
|
bb43b08190 | ||
|
|
05f95796f6 | ||
|
|
cfcd7d7588 | ||
|
|
4c3d44a63b | ||
|
|
fcd75a34eb | ||
|
|
4827b73bb1 | ||
|
|
2d7e7d55a9 | ||
|
|
3129c8c8ea | ||
|
|
6e37300d9b | ||
|
|
6b747e924b | ||
|
|
85fef96d00 | ||
|
|
e55bb21401 | ||
|
|
7a3fb95851 | ||
|
|
d39074bf3e | ||
|
|
47d814e409 | ||
|
|
41b01a508d | ||
|
|
0410ae6877 | ||
|
|
1ae41319fa | ||
|
|
0265152c12 | ||
|
|
a893d46cb9 | ||
|
|
b68f271fb7 | ||
|
|
057e231a9d | ||
|
|
b6fedbd97d | ||
|
|
7cd29501a9 | ||
|
|
59e4f48726 | ||
|
|
20fe2ee505 | ||
|
|
8b4ba430fb | ||
|
|
3b0949de72 | ||
|
|
ebb4623bb5 | ||
|
|
2309487429 | ||
|
|
a3decc33c9 | ||
|
|
b2d87a8780 | ||
|
|
5034005f6b | ||
|
|
c045ba0c6d | ||
|
|
d0fb4d44ef | ||
|
|
d1f3e3f4bb | ||
|
|
45f578b5a3 | ||
|
|
620f4b96b9 | ||
|
|
39d3f85b71 | ||
|
|
9a7df65faa | ||
|
|
596eb4c7e4 | ||
|
|
2bce305451 | ||
|
|
2bcc531e73 | ||
|
|
2e66a854b1 | ||
|
|
57f6096aa1 | ||
|
|
dc64b98c59 | ||
|
|
e6dde7335a | ||
|
|
bbb2174957 | ||
|
|
b37b50a0c6 | ||
|
|
459ff8038b | ||
|
|
f2da862442 | ||
|
|
9c6c2ec5ca | ||
|
|
aa13583aa7 | ||
|
|
002f1e09ef | ||
|
|
5091ed7d6d | ||
|
|
2475f22937 | ||
|
|
f4f12f786c | ||
|
|
eb659446df | ||
|
|
62c42adebc | ||
|
|
0fc91595b8 | ||
|
|
18fca6461b | ||
|
|
869b819a4f | ||
|
|
8ae6192a5f | ||
|
|
3b2af6ea07 | ||
|
|
82da7a8f24 | ||
|
|
3749686882 | ||
|
|
b2eba4911a | ||
|
|
40700a387c | ||
|
|
0577940d58 | ||
|
|
1d6be07cff | ||
|
|
2d6545429a | ||
|
|
9f095a1d28 | ||
|
|
91b6da70af | ||
|
|
235e40cb26 | ||
|
|
8ba3a30278 | ||
|
|
3675ddaf04 | ||
|
|
62a59e7b2a | ||
|
|
1f949463aa | ||
|
|
6d53a30b1f | ||
|
|
5aeb480069 | ||
|
|
4b6aa3b56f | ||
|
|
4209569f29 | ||
|
|
7106efbc04 | ||
|
|
4e878e49a7 | ||
|
|
3e470e557d | ||
|
|
5f567fcf66 | ||
|
|
2289e16d91 | ||
|
|
40415ef933 | ||
|
|
6519c30b3a | ||
|
|
42d253a9ca | ||
|
|
38b40e2931 | ||
|
|
f7a7ea14a3 | ||
|
|
41e0f92cc0 | ||
|
|
2c5e9789db | ||
|
|
e198aae1dd | ||
|
|
d040cc0d03 | ||
|
|
f078e94a7a | ||
|
|
9973099f57 | ||
|
|
9ca7785a64 | ||
|
|
e7d5fd6c1b | ||
|
|
9eed35ace7 | ||
|
|
c7ec1b4e5e | ||
|
|
34ffebb46a | ||
|
|
e8a2167934 | ||
|
|
fa342a5b6b | ||
|
|
4ebcaf913c | ||
|
|
7a4c9806a4 | ||
|
|
bddc1d7fdc | ||
|
|
cb813b70e1 | ||
|
|
f161f08fc1 | ||
|
|
3e22411328 | ||
|
|
b00778bb86 | ||
|
|
564f95781e | ||
|
|
6c2f8aa6ff | ||
|
|
2e342e4341 | ||
|
|
3c6089884e | ||
|
|
884ea866e2 | ||
|
|
1af1ada7dc | ||
|
|
c72c3fd091 | ||
|
|
0848718313 | ||
|
|
832abd7860 | ||
|
|
12e745af54 | ||
|
|
f18c818b8e | ||
|
|
4deca4f816 | ||
|
|
b023ecfb99 | ||
|
|
cdb491bb5f | ||
|
|
3ac47c967e | ||
|
|
e577062720 | ||
|
|
95dc7ad2b4 | ||
|
|
fccf58cd6c | ||
|
|
6791e4d4ba | ||
|
|
11f8ac4820 | ||
|
|
8f2672f928 | ||
|
|
fb66d74ab7 | ||
|
|
37c061bcbd | ||
|
|
9491b18ff4 | ||
|
|
8cff2eaac0 | ||
|
|
f64550f7c8 | ||
|
|
0addafa627 | ||
|
|
b643d73f7f | ||
|
|
798c5b292a | ||
|
|
688c57d91d | ||
|
|
d4c8d5d91b | ||
|
|
154cc9fbeb | ||
|
|
327bac3058 | ||
|
|
c0e58e9c92 | ||
|
|
e8bfab515c | ||
|
|
695f204493 | ||
|
|
a7202ccd0d | ||
|
|
c71c6c5178 | ||
|
|
155b06edc8 | ||
|
|
64b8f2fb46 | ||
|
|
7a07e15032 | ||
|
|
3d74312d43 | ||
|
|
6baf85d429 | ||
|
|
311ab19389 | ||
|
|
e9f92d2250 | ||
|
|
d469374ed5 | ||
|
|
a6ff84c09a | ||
|
|
3ed8ca63a8 | ||
|
|
89aa08829e | ||
|
|
5146106885 | ||
|
|
f05fc956db | ||
|
|
d10adb5de8 | ||
|
|
45aac21b63 | ||
|
|
b52643bb36 | ||
|
|
036010c39d | ||
|
|
9440d2b2c3 | ||
|
|
29727ec9a6 | ||
|
|
eb0b43cea8 | ||
|
|
a6e16fb7ad | ||
|
|
616b80710a | ||
|
|
94f206af68 | ||
|
|
ae6613fae1 | ||
|
|
b61cfc7390 | ||
|
|
e2c5688670 | ||
|
|
1d016d9fce | ||
|
|
cbc5b5bb89 | ||
|
|
218c7736e0 | ||
|
|
8e46e41b34 | ||
|
|
bcf639d076 | ||
|
|
697d12aefc | ||
|
|
89afd2b4c0 | ||
|
|
3358f98bc4 | ||
|
|
417af6a93c | ||
|
|
9a2f6c7faa | ||
|
|
e52b0edc4e | ||
|
|
c51a65f058 | ||
|
|
5b2e67df19 | ||
|
|
7b7605e501 | ||
|
|
49d7d9ee2e | ||
|
|
774c8971c8 | ||
|
|
545c0886fa | ||
|
|
721d027fb3 | ||
|
|
5185ea35fe | ||
|
|
ec039f2999 | ||
|
|
c0fc649f8b | ||
|
|
c49497e68c | ||
|
|
57bc6de12b | ||
|
|
1b80d68f50 | ||
|
|
787151597c | ||
|
|
f725039da5 | ||
|
|
a4f15a0830 | ||
|
|
084f00afad | ||
|
|
87f4ebee64 | ||
|
|
a0276821d9 | ||
|
|
01a5b523da | ||
|
|
13477e17ec | ||
|
|
c32cec4c5b | ||
|
|
e08df3f9ce | ||
|
|
f958130b92 | ||
|
|
9a19dd254e | ||
|
|
4869ae4baa | ||
|
|
1fd41eaa55 | ||
|
|
25a7260d7f | ||
|
|
478934ddd1 | ||
|
|
3d7234de19 | ||
|
|
fed1fb2839 | ||
|
|
08ba001b45 | ||
|
|
ce5aaf0b48 | ||
|
|
ee6954a4b9 | ||
|
|
6b6e845a18 | ||
|
|
c01b94518f | ||
|
|
9cf5f74e5e | ||
|
|
4fda0414a0 | ||
|
|
effa4af3a1 | ||
|
|
80b7e49eed | ||
|
|
63748c3841 | ||
|
|
1c68b66047 | ||
|
|
6c9dce70e7 | ||
|
|
5515aa43d6 | ||
|
|
47c461e504 | ||
|
|
65174ceaae | ||
|
|
2500cfb3c7 | ||
|
|
7e673271ee | ||
|
|
e165cb156c | ||
|
|
82bfb0cc55 | ||
|
|
b0fe0663f1 | ||
|
|
1bd5193786 | ||
|
|
64c3fb985a | ||
|
|
1536c94319 | ||
|
|
e7efd89d7a | ||
|
|
c486167b01 | ||
|
|
e5201f3363 | ||
|
|
ff4927dbb7 | ||
|
|
5842562ce1 | ||
|
|
5f545fd3c5 | ||
|
|
aab2f20280 | ||
|
|
76ac68ce51 | ||
|
|
f034e3680c | ||
|
|
e5c57269bb | ||
|
|
ab5167717d | ||
|
|
176abf74a1 | ||
|
|
da4f13d0d7 | ||
|
|
7ddec7609a | ||
|
|
53c3996aef | ||
|
|
bf61f22dd1 | ||
|
|
5e8a36802c | ||
|
|
b34b537a26 | ||
|
|
812fb99e66 | ||
|
|
9cb3111dd5 | ||
|
|
1312555690 | ||
|
|
aa4f8452fa | ||
|
|
e2c1097ec5 | ||
|
|
4b8b13dd68 | ||
|
|
18fd9cdc3d | ||
|
|
fdcdf52d9a | ||
|
|
5dcefff28d |
@@ -26,24 +26,25 @@ init:
|
|||||||
# - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
# - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
||||||
|
|
||||||
install:
|
install:
|
||||||
|
- cmd:
|
||||||
# Outcomment if lua environment invalidates and needs to be reinstalled, otherwise all will run from the cache.
|
# Outcomment if lua environment invalidates and needs to be reinstalled, otherwise all will run from the cache.
|
||||||
# - call choco install 7zip.commandline
|
call choco install 7zip.commandline
|
||||||
# - call choco install lua51
|
call choco install lua51
|
||||||
# - call choco install luarocks
|
call choco install luarocks
|
||||||
# - call refreshenv
|
call refreshenv
|
||||||
# - call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"
|
call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"
|
||||||
# - cmd: PATH = %PATH%;C:\ProgramData\chocolatey\lib\luarocks\luarocks-2.4.3-win32\systree\bin
|
cmd: PATH = %PATH%;C:\ProgramData\chocolatey\lib\luarocks\luarocks-2.4.3-win32\systree\bin
|
||||||
# - cmd: set LUA_PATH = %LUA_PATH%;C:\ProgramData\chocolatey\lib\luarocks\luarocks-2.4.3-win32\systree\share\lua\5.1\?.lua;C:\ProgramData\chocolatey\lib\luarocks\luarocks-2.4.3-win32\systree\share\lua\5.1\?\init.lua
|
cmd: set LUA_PATH = %LUA_PATH%;C:\ProgramData\chocolatey\lib\luarocks\luarocks-2.4.3-win32\systree\share\lua\5.1\?.lua;C:\ProgramData\chocolatey\lib\luarocks\luarocks-2.4.3-win32\systree\share\lua\5.1\?\init.lua
|
||||||
# - cmd: set LUA_CPATH = %LUA_CPATH%;C:\ProgramData\chocolatey\lib\luarocks\luarocks-2.4.3-win32\systree\lib\lua\5.1\?.dll
|
cmd: set LUA_CPATH = %LUA_CPATH%;C:\ProgramData\chocolatey\lib\luarocks\luarocks-2.4.3-win32\systree\lib\lua\5.1\?.dll
|
||||||
# - call luarocks install luasrcdiet
|
call luarocks install luasrcdiet
|
||||||
# - call luarocks install checks
|
call luarocks install checks
|
||||||
# - call luarocks install luadocumentor
|
call luarocks install luadocumentor
|
||||||
# - call luarocks install luacheck
|
call luarocks install luacheck
|
||||||
|
|
||||||
|
|
||||||
#cache:
|
cache:
|
||||||
# - C:\ProgramData\chocolatey\lib
|
C:\ProgramData\chocolatey\lib
|
||||||
# - C:\ProgramData\chocolatey\bin
|
C:\ProgramData\chocolatey\bin
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -51,8 +52,9 @@ build_script:
|
|||||||
- ps: |
|
- ps: |
|
||||||
if( $env:appveyor_repo_branch -eq 'master' -or $env:appveyor_repo_branch -eq 'develop' )
|
if( $env:appveyor_repo_branch -eq 'master' -or $env:appveyor_repo_branch -eq 'develop' )
|
||||||
{
|
{
|
||||||
|
echo "Hello World!"
|
||||||
$apiUrl = 'https://ci.appveyor.com/api'
|
$apiUrl = 'https://ci.appveyor.com/api'
|
||||||
$token = 'qts80b5kpq0ooj4x6vvw'
|
$token = 'v2.6hcv3ige78kg3yvg4ge8'
|
||||||
$headers = @{
|
$headers = @{
|
||||||
"Authorization" = "Bearer $token"
|
"Authorization" = "Bearer $token"
|
||||||
"Content-type" = "application/json"
|
"Content-type" = "application/json"
|
||||||
@@ -65,7 +67,7 @@ build_script:
|
|||||||
if( $env:appveyor_repo_branch -eq 'master' -or $env:appveyor_repo_branch -eq 'develop' )
|
if( $env:appveyor_repo_branch -eq 'master' -or $env:appveyor_repo_branch -eq 'develop' )
|
||||||
{
|
{
|
||||||
$apiUrl = 'https://ci.appveyor.com/api'
|
$apiUrl = 'https://ci.appveyor.com/api'
|
||||||
$token = 'qts80b5kpq0ooj4x6vvw'
|
$token = 'v2.6hcv3ige78kg3yvg4ge8'
|
||||||
$headers = @{
|
$headers = @{
|
||||||
"Authorization" = "Bearer $token"
|
"Authorization" = "Bearer $token"
|
||||||
"Content-type" = "application/json"
|
"Content-type" = "application/json"
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -18,6 +18,8 @@ local.properties
|
|||||||
# External tool builders
|
# External tool builders
|
||||||
.externalToolBuilders/
|
.externalToolBuilders/
|
||||||
|
|
||||||
|
# AppVeyor
|
||||||
|
.appveyor/
|
||||||
|
|
||||||
# CDT-specific
|
# CDT-specific
|
||||||
.cproject
|
.cproject
|
||||||
|
|||||||
54
.luacheckrc
Normal file
54
.luacheckrc
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
ignore = {
|
||||||
|
"011", -- A syntax error.
|
||||||
|
"021", -- An invalid inline option.
|
||||||
|
"022", -- An unpaired inline push directive.
|
||||||
|
"023", -- An unpaired inline pop directive.
|
||||||
|
"111", -- Setting an undefined global variable.
|
||||||
|
"112", -- Mutating an undefined global variable.
|
||||||
|
"113", -- Accessing an undefined global variable.
|
||||||
|
"121", -- Setting a read-only global variable.
|
||||||
|
"122", -- Setting a read-only field of a global variable.
|
||||||
|
"131", -- Unused implicitly defined global variable.
|
||||||
|
"142", -- Setting an undefined field of a global variable.
|
||||||
|
"143", -- Accessing an undefined field of a global variable.
|
||||||
|
"211", -- Unused local variable.
|
||||||
|
"212", -- Unused argument.
|
||||||
|
"213", -- Unused loop variable.
|
||||||
|
"221", -- Local variable is accessed but never set.
|
||||||
|
"231", -- Local variable is set but never accessed.
|
||||||
|
"232", -- An argument is set but never accessed.
|
||||||
|
"233", -- Loop variable is set but never accessed.
|
||||||
|
"241", -- Local variable is mutated but never accessed.
|
||||||
|
"311", -- Value assigned to a local variable is unused.
|
||||||
|
"312", -- Value of an argument is unused.
|
||||||
|
"313", -- Value of a loop variable is unused.
|
||||||
|
"314", -- Value of a field in a table literal is unused.
|
||||||
|
"321", -- Accessing uninitialized local variable.
|
||||||
|
"331", -- Value assigned to a local variable is mutated but never accessed.
|
||||||
|
"341", -- Mutating uninitialized local variable.
|
||||||
|
"411", -- Redefining a local variable.
|
||||||
|
"412", -- Redefining an argument.
|
||||||
|
"413", -- Redefining a loop variable.
|
||||||
|
"421", -- Shadowing a local variable.
|
||||||
|
"422", -- Shadowing an argument.
|
||||||
|
"423", -- Shadowing a loop variable.
|
||||||
|
"431", -- Shadowing an upvalue.
|
||||||
|
"432", -- Shadowing an upvalue argument.
|
||||||
|
"433", -- Shadowing an upvalue loop variable.
|
||||||
|
"511", -- Unreachable code.
|
||||||
|
"512", -- Loop can be executed at most once.
|
||||||
|
"521", -- Unused label.
|
||||||
|
"531", -- Left-hand side of an assignment is too short.
|
||||||
|
"532", -- Left-hand side of an assignment is too long.
|
||||||
|
"541", -- An empty do end block.
|
||||||
|
"542", -- An empty if branch.
|
||||||
|
"551", -- An empty statement.
|
||||||
|
"561", -- Cyclomatic complexity of a function is too high.
|
||||||
|
"571", -- A numeric for loop goes from #(expr) down to 1 or less without negative step.
|
||||||
|
"611", -- A line consists of nothing but whitespace.
|
||||||
|
"612", -- A line contains trailing whitespace.
|
||||||
|
"613", -- Trailing whitespace in a string.
|
||||||
|
"614", -- Trailing whitespace in a comment.
|
||||||
|
"621", -- Inconsistent indentation (SPACE followed by TAB).
|
||||||
|
"631", -- Line is too long.
|
||||||
|
}
|
||||||
33
Moose Development/Moose/.lua-format
Normal file
33
Moose Development/Moose/.lua-format
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# See https://github.com/Koihik/LuaFormatter
|
||||||
|
# Use '-- LuaFormatter off' and '-- LuaFormatter on' around code blocks to inhibit formatting
|
||||||
|
|
||||||
|
column_limit: 500
|
||||||
|
indent_width: 2
|
||||||
|
use_tab: false
|
||||||
|
continuation_indent_width: 2
|
||||||
|
keep_simple_control_block_one_line: false
|
||||||
|
keep_simple_function_one_line: false
|
||||||
|
align_args: true
|
||||||
|
break_after_functioncall_lp: false
|
||||||
|
break_before_functioncall_rp: false
|
||||||
|
align_parameter: true
|
||||||
|
chop_down_parameter: true
|
||||||
|
break_after_functiondef_lp: false
|
||||||
|
break_before_functiondef_rp: false
|
||||||
|
align_table_field: true
|
||||||
|
break_after_table_lb: true
|
||||||
|
break_before_table_rb: true
|
||||||
|
chop_down_table: true
|
||||||
|
chop_down_kv_table: true
|
||||||
|
column_table_limit: 500
|
||||||
|
table_sep: ','
|
||||||
|
extra_sep_at_table_end: true
|
||||||
|
break_after_operator: true
|
||||||
|
single_quote_to_double_quote: false
|
||||||
|
double_quote_to_single_quote: false
|
||||||
|
spaces_before_call: 1
|
||||||
|
spaces_inside_functiondef_parens: true
|
||||||
|
spaces_inside_functioncall_parens: true
|
||||||
|
spaces_inside_table_braces: true
|
||||||
|
spaces_around_equals_in_field: true
|
||||||
|
line_breaks_after_function_body: 1
|
||||||
@@ -143,7 +143,7 @@ end
|
|||||||
-- @return #AI_A2A_CAP
|
-- @return #AI_A2A_CAP
|
||||||
function AI_A2A_CAP:New( AICap, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageMinSpeed, EngageMaxSpeed, PatrolAltType )
|
function AI_A2A_CAP:New( AICap, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageMinSpeed, EngageMaxSpeed, PatrolAltType )
|
||||||
|
|
||||||
return self:New2( AICap, EngageMinSpeed, EngageMaxSpeed, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolZone, PatrolMinSpeed, PatrolMaxSpeed, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolAltType, PatrolAltType )
|
return self:New2( AICap, EngageMinSpeed, EngageMaxSpeed, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolAltType, PatrolZone, PatrolMinSpeed, PatrolMaxSpeed, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolAltType )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -658,7 +658,9 @@ do -- AI_A2A_DISPATCHER
|
|||||||
-- of the race track will randomly selected between 90 (West to East) and 180 (North to South) degrees.
|
-- of the race track will randomly selected between 90 (West to East) and 180 (North to South) degrees.
|
||||||
-- After a random duration between 10 and 20 minutes, the flight will get a new random orbit location.
|
-- After a random duration between 10 and 20 minutes, the flight will get a new random orbit location.
|
||||||
--
|
--
|
||||||
-- Note that all parameters except the squadron name are optional. If not specified, default values are taken. Speed and altitude are taken from the
|
-- Note that all parameters except the squadron name are optional. If not specified, default values are taken. Speed and altitude are taken from the CAP command used earlier on, e.g.
|
||||||
|
--
|
||||||
|
-- A2ADispatcher:SetSquadronCap( "Mineralnye", CAPZoneEast, 4000, 10000, 500, 600, 800, 900 )
|
||||||
--
|
--
|
||||||
-- Also note that the center of the race track pattern is chosen randomly within the patrol zone and can be close the the boarder of the zone. Hence, it cannot be guaranteed that the
|
-- Also note that the center of the race track pattern is chosen randomly within the patrol zone and can be close the the boarder of the zone. Hence, it cannot be guaranteed that the
|
||||||
-- whole pattern lies within the patrol zone.
|
-- whole pattern lies within the patrol zone.
|
||||||
@@ -2774,7 +2776,7 @@ do -- AI_A2A_DISPATCHER
|
|||||||
-- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection )
|
-- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection )
|
||||||
--
|
--
|
||||||
-- -- Now Setup the default fuel treshold.
|
-- -- Now Setup the default fuel treshold.
|
||||||
-- A2ADispatcher:SetSquadronRefuelThreshold( "SquadronName", 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
|
-- A2ADispatcher:SetSquadronFuelThreshold( "SquadronName", 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
|
||||||
--
|
--
|
||||||
function AI_A2A_DISPATCHER:SetSquadronFuelThreshold( SquadronName, FuelThreshold )
|
function AI_A2A_DISPATCHER:SetSquadronFuelThreshold( SquadronName, FuelThreshold )
|
||||||
|
|
||||||
@@ -2817,7 +2819,7 @@ do -- AI_A2A_DISPATCHER
|
|||||||
-- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection )
|
-- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection )
|
||||||
--
|
--
|
||||||
-- -- Now Setup the squadron fuel treshold.
|
-- -- Now Setup the squadron fuel treshold.
|
||||||
-- A2ADispatcher:SetSquadronRefuelThreshold( "SquadronName", 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
|
-- A2ADispatcher:SetSquadronFuelThreshold( "SquadronName", 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
|
||||||
--
|
--
|
||||||
-- -- Now Setup the squadron tanker.
|
-- -- Now Setup the squadron tanker.
|
||||||
-- A2ADispatcher:SetSquadronTanker( "SquadronName", "Tanker" ) -- The group name of the tanker is "Tanker" in the Mission Editor.
|
-- A2ADispatcher:SetSquadronTanker( "SquadronName", "Tanker" ) -- The group name of the tanker is "Tanker" in the Mission Editor.
|
||||||
@@ -2919,9 +2921,13 @@ do -- AI_A2A_DISPATCHER
|
|||||||
-- @return #AI_A2A_DISPATCHER.Squadron Squadron The squadron.
|
-- @return #AI_A2A_DISPATCHER.Squadron Squadron The squadron.
|
||||||
function AI_A2A_DISPATCHER:GetSquadronFromDefender( Defender )
|
function AI_A2A_DISPATCHER:GetSquadronFromDefender( Defender )
|
||||||
self.Defenders = self.Defenders or {}
|
self.Defenders = self.Defenders or {}
|
||||||
local DefenderName = Defender:GetName()
|
if Defender ~= nil then
|
||||||
self:F( { DefenderName = DefenderName } )
|
local DefenderName = Defender:GetName()
|
||||||
return self.Defenders[ DefenderName ]
|
self:F( { DefenderName = DefenderName } )
|
||||||
|
return self.Defenders[ DefenderName ]
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -585,7 +585,7 @@ function AI_AIR:onafterRTB( AIGroup, From, Event, To )
|
|||||||
|
|
||||||
if AIGroup and AIGroup:IsAlive() then
|
if AIGroup and AIGroup:IsAlive() then
|
||||||
|
|
||||||
self:I( "Group " .. AIGroup:GetName() .. " ... RTB! ( " .. self:GetState() .. " )" )
|
self:T( "Group " .. AIGroup:GetName() .. " ... RTB! ( " .. self:GetState() .. " )" )
|
||||||
|
|
||||||
self:ClearTargetDistance()
|
self:ClearTargetDistance()
|
||||||
--AIGroup:ClearTasks()
|
--AIGroup:ClearTasks()
|
||||||
@@ -595,19 +595,24 @@ function AI_AIR:onafterRTB( AIGroup, From, Event, To )
|
|||||||
--- Calculate the target route point.
|
--- Calculate the target route point.
|
||||||
|
|
||||||
local FromCoord = AIGroup:GetCoordinate()
|
local FromCoord = AIGroup:GetCoordinate()
|
||||||
local ToTargetCoord = self.HomeAirbase:GetCoordinate()
|
local ToTargetCoord = self.HomeAirbase:GetCoordinate() -- coordinate is on land height(!)
|
||||||
|
local ToTargetVec3 = ToTargetCoord:GetVec3()
|
||||||
|
ToTargetVec3.y = ToTargetCoord:GetLandHeight()+1000 -- let's set this 1000m/3000 feet above ground
|
||||||
|
local ToTargetCoord2 = COORDINATE:NewFromVec3( ToTargetVec3 )
|
||||||
|
|
||||||
if not self.RTBMinSpeed and not self.RTBMaxSpeed then
|
if not self.RTBMinSpeed or not self.RTBMaxSpeed then
|
||||||
local RTBSpeedMax = AIGroup:GetSpeedMax()
|
local RTBSpeedMax = AIGroup:GetSpeedMax()
|
||||||
self:SetRTBSpeed( RTBSpeedMax * 0.25, RTBSpeedMax * 0.25 )
|
self:SetRTBSpeed( RTBSpeedMax * 0.5, RTBSpeedMax * 0.6 )
|
||||||
end
|
end
|
||||||
|
|
||||||
local RTBSpeed = math.random( self.RTBMinSpeed, self.RTBMaxSpeed )
|
local RTBSpeed = math.random( self.RTBMinSpeed, self.RTBMaxSpeed )
|
||||||
local ToAirbaseAngle = FromCoord:GetAngleDegrees( FromCoord:GetDirectionVec3( ToTargetCoord ) )
|
--local ToAirbaseAngle = FromCoord:GetAngleDegrees( FromCoord:GetDirectionVec3( ToTargetCoord2 ) )
|
||||||
|
|
||||||
local Distance = FromCoord:Get2DDistance( ToTargetCoord )
|
local Distance = FromCoord:Get2DDistance( ToTargetCoord2 )
|
||||||
|
|
||||||
|
--local ToAirbaseCoord = FromCoord:Translate( 5000, ToAirbaseAngle )
|
||||||
|
local ToAirbaseCoord = ToTargetCoord2
|
||||||
|
|
||||||
local ToAirbaseCoord = FromCoord:Translate( 5000, ToAirbaseAngle )
|
|
||||||
if Distance < 5000 then
|
if Distance < 5000 then
|
||||||
self:I( "RTB and near the airbase!" )
|
self:I( "RTB and near the airbase!" )
|
||||||
self:Home()
|
self:Home()
|
||||||
|
|||||||
@@ -418,6 +418,7 @@ end
|
|||||||
-- @param #string From The From State string.
|
-- @param #string From The From State string.
|
||||||
-- @param #string Event The Event string.
|
-- @param #string Event The Event string.
|
||||||
-- @param #string To The To State string.
|
-- @param #string To The To State string.
|
||||||
|
-- @param Core.Set#SET_UNIT AttackSetUnit Unit set to be attacked.
|
||||||
function AI_AIR_ENGAGE:onafterEngageRoute( DefenderGroup, From, Event, To, AttackSetUnit )
|
function AI_AIR_ENGAGE:onafterEngageRoute( DefenderGroup, From, Event, To, AttackSetUnit )
|
||||||
self:I( { DefenderGroup, From, Event, To, AttackSetUnit } )
|
self:I( { DefenderGroup, From, Event, To, AttackSetUnit } )
|
||||||
|
|
||||||
@@ -425,7 +426,7 @@ function AI_AIR_ENGAGE:onafterEngageRoute( DefenderGroup, From, Event, To, Attac
|
|||||||
|
|
||||||
self.AttackSetUnit = AttackSetUnit -- Kept in memory in case of resume from refuel in air!
|
self.AttackSetUnit = AttackSetUnit -- Kept in memory in case of resume from refuel in air!
|
||||||
|
|
||||||
local AttackCount = AttackSetUnit:Count()
|
local AttackCount = AttackSetUnit:CountAlive()
|
||||||
|
|
||||||
if AttackCount > 0 then
|
if AttackCount > 0 then
|
||||||
|
|
||||||
@@ -510,6 +511,7 @@ end
|
|||||||
-- @param #string From The From State string.
|
-- @param #string From The From State string.
|
||||||
-- @param #string Event The Event string.
|
-- @param #string Event The Event string.
|
||||||
-- @param #string To The To State string.
|
-- @param #string To The To State string.
|
||||||
|
-- @param Core.Set#SET_UNIT AttackSetUnit Set of units to be attacked.
|
||||||
function AI_AIR_ENGAGE:onafterEngage( DefenderGroup, From, Event, To, AttackSetUnit )
|
function AI_AIR_ENGAGE:onafterEngage( DefenderGroup, From, Event, To, AttackSetUnit )
|
||||||
self:F( { DefenderGroup, From, Event, To, AttackSetUnit} )
|
self:F( { DefenderGroup, From, Event, To, AttackSetUnit} )
|
||||||
|
|
||||||
@@ -517,8 +519,8 @@ function AI_AIR_ENGAGE:onafterEngage( DefenderGroup, From, Event, To, AttackSetU
|
|||||||
|
|
||||||
self.AttackSetUnit = AttackSetUnit -- Kept in memory in case of resume from refuel in air!
|
self.AttackSetUnit = AttackSetUnit -- Kept in memory in case of resume from refuel in air!
|
||||||
|
|
||||||
local AttackCount = AttackSetUnit:Count()
|
local AttackCount = AttackSetUnit:CountAlive()
|
||||||
self:I({AttackCount = AttackCount})
|
self:T({AttackCount = AttackCount})
|
||||||
|
|
||||||
if AttackCount > 0 then
|
if AttackCount > 0 then
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
--- **AI** -- (R2.4) - Models the intelligent transportation of infantry and other cargo.
|
--- **AI** - Models the intelligent transportation of infantry and other cargo.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
@@ -35,10 +35,9 @@ AI_CARGO = {
|
|||||||
|
|
||||||
--- Creates a new AI_CARGO object.
|
--- Creates a new AI_CARGO object.
|
||||||
-- @param #AI_CARGO self
|
-- @param #AI_CARGO self
|
||||||
-- @param Wrapper.Group#GROUP Carrier
|
-- @param Wrapper.Group#GROUP Carrier Cargo carrier group.
|
||||||
-- @param Core.Set#SET_CARGO CargoSet
|
-- @param Core.Set#SET_CARGO CargoSet Set of cargo(s) to transport.
|
||||||
-- @param #number CombatRadius
|
-- @return #AI_CARGO self
|
||||||
-- @return #AI_CARGO
|
|
||||||
function AI_CARGO:New( Carrier, CargoSet )
|
function AI_CARGO:New( Carrier, CargoSet )
|
||||||
|
|
||||||
local self = BASE:Inherit( self, FSM_CONTROLLABLE:New( Carrier ) ) -- #AI_CARGO
|
local self = BASE:Inherit( self, FSM_CONTROLLABLE:New( Carrier ) ) -- #AI_CARGO
|
||||||
@@ -48,18 +47,21 @@ function AI_CARGO:New( Carrier, CargoSet )
|
|||||||
|
|
||||||
self:SetStartState( "Unloaded" )
|
self:SetStartState( "Unloaded" )
|
||||||
|
|
||||||
self:AddTransition( "Unloaded", "Pickup", "*" )
|
-- Board
|
||||||
self:AddTransition( "Loaded", "Deploy", "*" )
|
self:AddTransition( "Unloaded", "Pickup", "Unloaded" )
|
||||||
|
self:AddTransition( "*", "Load", "*" )
|
||||||
|
self:AddTransition( "*", "Reload", "*" )
|
||||||
|
self:AddTransition( "*", "Board", "*" )
|
||||||
|
self:AddTransition( "*", "Loaded", "Loaded" )
|
||||||
|
self:AddTransition( "Loaded", "PickedUp", "Loaded" )
|
||||||
|
|
||||||
self:AddTransition( "*", "Load", "Boarding" )
|
-- Unload
|
||||||
self:AddTransition( { "Boarding", "Loaded" }, "Board", "Boarding" )
|
self:AddTransition( "Loaded", "Deploy", "*" )
|
||||||
self:AddTransition( "Boarding", "Loaded", "Boarding" )
|
self:AddTransition( "*", "Unload", "*" )
|
||||||
self:AddTransition( "Boarding", "PickedUp", "Loaded" )
|
self:AddTransition( "*", "Unboard", "*" )
|
||||||
|
self:AddTransition( "*", "Unloaded", "Unloaded" )
|
||||||
|
self:AddTransition( "Unloaded", "Deployed", "Unloaded" )
|
||||||
|
|
||||||
self:AddTransition( "Loaded", "Unload", "Unboarding" )
|
|
||||||
self:AddTransition( "Unboarding", "Unboard", "Unboarding" )
|
|
||||||
self:AddTransition( "Unboarding", "Unloaded", "Unboarding" )
|
|
||||||
self:AddTransition( "Unboarding", "Deployed", "Unloaded" )
|
|
||||||
|
|
||||||
--- Pickup Handler OnBefore for AI_CARGO
|
--- Pickup Handler OnBefore for AI_CARGO
|
||||||
-- @function [parent=#AI_CARGO] OnBeforePickup
|
-- @function [parent=#AI_CARGO] OnBeforePickup
|
||||||
@@ -580,4 +582,3 @@ function AI_CARGO:onafterDeployed( Carrier, From, Event, To, DeployZone, Defend
|
|||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
--- **AI** -- (R2.4) - Models the intelligent transportation of infantry and other cargo.
|
--- **AI** - Models the intelligent transportation of cargo using ground vehicles.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
@@ -98,7 +98,8 @@ function AI_CARGO_APC:New( APC, CargoSet, CombatRadius )
|
|||||||
self:AddTransition( "*", "Guard", "Unloaded" )
|
self:AddTransition( "*", "Guard", "Unloaded" )
|
||||||
self:AddTransition( "*", "Home", "*" )
|
self:AddTransition( "*", "Home", "*" )
|
||||||
self:AddTransition( "*", "Reload", "Boarding" )
|
self:AddTransition( "*", "Reload", "Boarding" )
|
||||||
|
self:AddTransition( "*", "Deployed", "*" )
|
||||||
|
self:AddTransition( "*", "PickedUp", "*" )
|
||||||
self:AddTransition( "*", "Destroyed", "Destroyed" )
|
self:AddTransition( "*", "Destroyed", "Destroyed" )
|
||||||
|
|
||||||
self:SetCombatRadius( CombatRadius )
|
self:SetCombatRadius( CombatRadius )
|
||||||
@@ -157,6 +158,45 @@ function AI_CARGO_APC:SetCarrier( CargoCarrier )
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Set whether or not the carrier will use roads to *pickup* and *deploy* the cargo.
|
||||||
|
-- @param #AI_CARGO_APC self
|
||||||
|
-- @param #boolean Offroad If true, carrier will not use roads. If `nil` or `false` the carrier will use roads when available.
|
||||||
|
-- @param #number Formation Offroad formation used. Default is `ENUMS.Formation.Vehicle.Offroad`.
|
||||||
|
-- @return #AI_CARGO_APC self
|
||||||
|
function AI_CARGO_APC:SetOffRoad(Offroad, Formation)
|
||||||
|
|
||||||
|
self:SetPickupOffRoad(Offroad, Formation)
|
||||||
|
self:SetDeployOffRoad(Offroad, Formation)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set whether the carrier will *not* use roads to *pickup* the cargo.
|
||||||
|
-- @param #AI_CARGO_APC self
|
||||||
|
-- @param #boolean Offroad If true, carrier will not use roads.
|
||||||
|
-- @param #number Formation Offroad formation used. Default is `ENUMS.Formation.Vehicle.Offroad`.
|
||||||
|
-- @return #AI_CARGO_APC self
|
||||||
|
function AI_CARGO_APC:SetPickupOffRoad(Offroad, Formation)
|
||||||
|
|
||||||
|
self.pickupOffroad=Offroad
|
||||||
|
self.pickupFormation=Formation or ENUMS.Formation.Vehicle.OffRoad
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set whether the carrier will *not* use roads to *deploy* the cargo.
|
||||||
|
-- @param #AI_CARGO_APC self
|
||||||
|
-- @param #boolean Offroad If true, carrier will not use roads.
|
||||||
|
-- @param #number Formation Offroad formation used. Default is `ENUMS.Formation.Vehicle.Offroad`.
|
||||||
|
-- @return #AI_CARGO_APC self
|
||||||
|
function AI_CARGO_APC:SetDeployOffRoad(Offroad, Formation)
|
||||||
|
|
||||||
|
self.deployOffroad=Offroad
|
||||||
|
self.deployFormation=Formation or ENUMS.Formation.Vehicle.OffRoad
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Find a free Carrier within a radius.
|
--- Find a free Carrier within a radius.
|
||||||
-- @param #AI_CARGO_APC self
|
-- @param #AI_CARGO_APC self
|
||||||
@@ -350,10 +390,13 @@ function AI_CARGO_APC:onafterFollow( APC, From, Event, To )
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Pickup task function. Triggers Load event.
|
||||||
--- @param #AI_CARGO_APC
|
-- @param Wrapper.Group#GROUP APC The cargo carrier group.
|
||||||
-- @param Wrapper.Group#GROUP APC
|
-- @param #AI_CARGO_APC sel `AI_CARGO_APC` class.
|
||||||
function AI_CARGO_APC._Pickup( APC, self, Coordinate, Speed, PickupZone )
|
-- @param Core.Point#COORDINATE Coordinate. The coordinate (not used).
|
||||||
|
-- @param #number Speed Speed (not used).
|
||||||
|
-- @param Core.Zone#ZONE PickupZone Pickup zone.
|
||||||
|
function AI_CARGO_APC._Pickup(APC, self, Coordinate, Speed, PickupZone)
|
||||||
|
|
||||||
APC:F( { "AI_CARGO_APC._Pickup:", APC:GetName() } )
|
APC:F( { "AI_CARGO_APC._Pickup:", APC:GetName() } )
|
||||||
|
|
||||||
@@ -362,8 +405,12 @@ function AI_CARGO_APC._Pickup( APC, self, Coordinate, Speed, PickupZone )
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Deploy task function. Triggers Unload event.
|
||||||
function AI_CARGO_APC._Deploy( APC, self, Coordinate, DeployZone )
|
-- @param Wrapper.Group#GROUP APC The cargo carrier group.
|
||||||
|
-- @param #AI_CARGO_APC self `AI_CARGO_APC` class.
|
||||||
|
-- @param Core.Point#COORDINATE Coordinate. The coordinate (not used).
|
||||||
|
-- @param Core.Zone#ZONE DeployZone Deploy zone.
|
||||||
|
function AI_CARGO_APC._Deploy(APC, self, Coordinate, DeployZone)
|
||||||
|
|
||||||
APC:F( { "AI_CARGO_APC._Deploy:", APC } )
|
APC:F( { "AI_CARGO_APC._Deploy:", APC } )
|
||||||
|
|
||||||
@@ -393,11 +440,19 @@ function AI_CARGO_APC:onafterPickup( APC, From, Event, To, Coordinate, Speed, He
|
|||||||
|
|
||||||
local _speed=Speed or APC:GetSpeedMax()*0.5
|
local _speed=Speed or APC:GetSpeedMax()*0.5
|
||||||
|
|
||||||
local Waypoints = APC:TaskGroundOnRoad( Coordinate, _speed, "Line abreast", true )
|
-- Route on road.
|
||||||
|
local Waypoints = {}
|
||||||
|
|
||||||
|
if self.pickupOffroad then
|
||||||
|
Waypoints[1]=APC:GetCoordinate():WaypointGround(Speed, self.pickupFormation)
|
||||||
|
Waypoints[2]=Coordinate:WaypointGround(_speed, self.pickupFormation, DCSTasks)
|
||||||
|
else
|
||||||
|
Waypoints=APC:TaskGroundOnRoad(Coordinate, _speed, ENUMS.Formation.Vehicle.OffRoad, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Pickup", self, Coordinate, Speed, PickupZone )
|
local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Pickup", self, Coordinate, Speed, PickupZone )
|
||||||
|
|
||||||
self:F({Waypoints = Waypoints})
|
|
||||||
local Waypoint = Waypoints[#Waypoints]
|
local Waypoint = Waypoints[#Waypoints]
|
||||||
APC:SetTaskWaypoint( Waypoint, TaskFunction ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone.
|
APC:SetTaskWaypoint( Waypoint, TaskFunction ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone.
|
||||||
|
|
||||||
@@ -428,18 +483,34 @@ function AI_CARGO_APC:onafterDeploy( APC, From, Event, To, Coordinate, Speed, He
|
|||||||
|
|
||||||
self.RouteDeploy = true
|
self.RouteDeploy = true
|
||||||
|
|
||||||
local _speed=Speed or APC:GetSpeedMax()*0.5
|
-- Set speed in km/h.
|
||||||
|
local speedmax=APC:GetSpeedMax()
|
||||||
|
local _speed=Speed or speedmax*0.5
|
||||||
|
_speed=math.min(_speed, speedmax)
|
||||||
|
|
||||||
local Waypoints = APC:TaskGroundOnRoad( Coordinate, _speed, "Line abreast", true )
|
-- Route on road.
|
||||||
|
local Waypoints = {}
|
||||||
|
|
||||||
|
if self.deployOffroad then
|
||||||
|
Waypoints[1]=APC:GetCoordinate():WaypointGround(Speed, self.deployFormation)
|
||||||
|
Waypoints[2]=Coordinate:WaypointGround(_speed, self.deployFormation, DCSTasks)
|
||||||
|
else
|
||||||
|
Waypoints=APC:TaskGroundOnRoad(Coordinate, _speed, ENUMS.Formation.Vehicle.OffRoad, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Task function
|
||||||
local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Deploy", self, Coordinate, DeployZone )
|
local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Deploy", self, Coordinate, DeployZone )
|
||||||
|
|
||||||
self:F({Waypoints = Waypoints})
|
-- Last waypoint
|
||||||
local Waypoint = Waypoints[#Waypoints]
|
local Waypoint = Waypoints[#Waypoints]
|
||||||
APC:SetTaskWaypoint( Waypoint, TaskFunction ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone.
|
|
||||||
|
|
||||||
|
-- Set task function
|
||||||
|
APC:SetTaskWaypoint(Waypoint, TaskFunction) -- Set for the given Route at Waypoint 2 the TaskRouteToZone.
|
||||||
|
|
||||||
|
-- Route group
|
||||||
APC:Route( Waypoints, 1 ) -- Move after a random seconds to the Route. See the Route method for details.
|
APC:Route( Waypoints, 1 ) -- Move after a random seconds to the Route. See the Route method for details.
|
||||||
|
|
||||||
|
-- Call parent function.
|
||||||
self:GetParent( self, AI_CARGO_APC ).onafterDeploy( self, APC, From, Event, To, Coordinate, Speed, Height, DeployZone )
|
self:GetParent( self, AI_CARGO_APC ).onafterDeploy( self, APC, From, Event, To, Coordinate, Speed, Height, DeployZone )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
--- **AI** -- (R2.4) - Models the intelligent transportation of infantry (cargo).
|
--- **AI** - Models the intelligent transportation of cargo using airplanes.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
@@ -408,9 +408,6 @@ function AI_CARGO_AIRPLANE:onafterUnload( Airplane, From, Event, To, DeployZone
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- Route the airplane from one airport or it's current position to another airbase.
|
--- Route the airplane from one airport or it's current position to another airbase.
|
||||||
-- @param #AI_CARGO_AIRPLANE self
|
-- @param #AI_CARGO_AIRPLANE self
|
||||||
-- @param Wrapper.Group#GROUP Airplane Airplane group to be routed.
|
-- @param Wrapper.Group#GROUP Airplane Airplane group to be routed.
|
||||||
@@ -438,14 +435,10 @@ function AI_CARGO_AIRPLANE:Route( Airplane, Airbase, Speed, Height, Uncontrolled
|
|||||||
|
|
||||||
-- To point.
|
-- To point.
|
||||||
local AirbasePointVec2 = Airbase:GetPointVec2()
|
local AirbasePointVec2 = Airbase:GetPointVec2()
|
||||||
local ToWaypoint = AirbasePointVec2:WaypointAir(
|
local ToWaypoint = AirbasePointVec2:WaypointAir(POINT_VEC3.RoutePointAltType.BARO, "Land", "Landing", Speed or Airplane:GetSpeedMax()*0.8, true, Airbase)
|
||||||
POINT_VEC3.RoutePointAltType.BARO,
|
|
||||||
"Land",
|
--ToWaypoint["airdromeId"] = Airbase:GetID()
|
||||||
"Landing",
|
--ToWaypoint["speed_locked"] = true
|
||||||
Speed or Airplane:GetSpeedMax()*0.8
|
|
||||||
)
|
|
||||||
ToWaypoint["airdromeId"] = Airbase:GetID()
|
|
||||||
ToWaypoint["speed_locked"] = true
|
|
||||||
|
|
||||||
|
|
||||||
-- If self.Airbase~=nil then group is currently at an airbase, where it should be respawned.
|
-- If self.Airbase~=nil then group is currently at an airbase, where it should be respawned.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
--- **AI** -- (R2.4) - Models the intelligent transportation of infantry and other cargo.
|
--- **AI** - Models the intelligent transportation of infantry and other cargo.
|
||||||
--
|
--
|
||||||
-- ## Features:
|
-- ## Features:
|
||||||
--
|
--
|
||||||
@@ -1104,7 +1104,7 @@ function AI_CARGO_DISPATCHER:onafterMonitor()
|
|||||||
-- The Pickup sequence ...
|
-- The Pickup sequence ...
|
||||||
-- Check if this Carrier need to go and Pickup something...
|
-- Check if this Carrier need to go and Pickup something...
|
||||||
-- So, if the cargo bay is not full yet with cargo to be loaded ...
|
-- 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() } )
|
self:T( { Carrier = CarrierGroupName, IsRelocating = AI_Cargo:IsRelocating(), IsTransporting = AI_Cargo:IsTransporting() } )
|
||||||
if AI_Cargo:IsRelocating() == false and AI_Cargo:IsTransporting() == false then
|
if AI_Cargo:IsRelocating() == false and AI_Cargo:IsTransporting() == false then
|
||||||
-- ok, so there is a free Carrier
|
-- ok, so there is a free Carrier
|
||||||
-- now find the first cargo that is Unloaded
|
-- now find the first cargo that is Unloaded
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
--- **AI** -- (2.4) - Models the intelligent transportation of infantry and other cargo using APCs.
|
--- **AI** - Models the intelligent transportation of infantry and other cargo using APCs.
|
||||||
--
|
--
|
||||||
-- ## Features:
|
-- ## Features:
|
||||||
--
|
--
|
||||||
@@ -181,25 +181,36 @@ function AI_CARGO_DISPATCHER_APC:New( APCSet, CargoSet, PickupZoneSet, DeployZon
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- AI cargo
|
||||||
|
-- @param #AI_CARGO_DISPATCHER_APC self
|
||||||
|
-- @param Wrapper.Group#GROUP APC The APC carrier.
|
||||||
|
-- @param Core.Set#SET_CARGO CargoSet Cargo set.
|
||||||
|
-- @return AI.AI_Cargo_APC#AI_CARGO_DISPATCHER_APC AI cargo APC object.
|
||||||
function AI_CARGO_DISPATCHER_APC:AICargo( APC, CargoSet )
|
function AI_CARGO_DISPATCHER_APC:AICargo( APC, CargoSet )
|
||||||
|
|
||||||
return AI_CARGO_APC:New( APC, CargoSet, self.CombatRadius )
|
local aicargoapc=AI_CARGO_APC:New(APC, CargoSet, self.CombatRadius)
|
||||||
|
|
||||||
|
aicargoapc:SetDeployOffRoad(self.deployOffroad, self.deployFormation)
|
||||||
|
aicargoapc:SetPickupOffRoad(self.pickupOffroad, self.pickupFormation)
|
||||||
|
|
||||||
|
return aicargoapc
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Enable/Disable unboarding of cargo (infantry) when enemies are nearby (to help defend the carrier).
|
--- Enable/Disable unboarding of cargo (infantry) when enemies are nearby (to help defend the carrier).
|
||||||
-- This is only valid for APCs and trucks etc, thus ground vehicles.
|
-- This is only valid for APCs and trucks etc, thus ground vehicles.
|
||||||
-- @param #AI_CARGO_DISPATCHER_APC self
|
-- @param #AI_CARGO_DISPATCHER_APC self
|
||||||
-- @param #number CombatRadius Provide the combat radius to defend the carrier by unboarding the cargo when enemies are nearby.
|
-- @param #number CombatRadius Provide the combat radius to defend the carrier by unboarding the cargo when enemies are nearby.
|
||||||
-- When the combat radius is 0, no defense will happen of the carrier.
|
-- When the combat radius is 0 (default), no defense will happen of the carrier.
|
||||||
-- When the combat radius is not provided, no defense will happen!
|
-- When the combat radius is not provided, no defense will happen!
|
||||||
-- @return #AI_CARGO_DISPATCHER_APC
|
-- @return #AI_CARGO_DISPATCHER_APC
|
||||||
-- @usage
|
-- @usage
|
||||||
--
|
--
|
||||||
-- -- Disembark the infantry when the carrier is under attack.
|
-- -- Disembark the infantry when the carrier is under attack.
|
||||||
-- AICargoDispatcher:SetCombatRadius( true )
|
-- AICargoDispatcher:SetCombatRadius( 500 )
|
||||||
--
|
--
|
||||||
-- -- Keep the cargo in the carrier when the carrier is under attack.
|
-- -- Keep the cargo in the carrier when the carrier is under attack.
|
||||||
-- AICargoDispatcher:SetCombatRadius( false )
|
-- AICargoDispatcher:SetCombatRadius( 0 )
|
||||||
function AI_CARGO_DISPATCHER_APC:SetCombatRadius( CombatRadius )
|
function AI_CARGO_DISPATCHER_APC:SetCombatRadius( CombatRadius )
|
||||||
|
|
||||||
self.CombatRadius = CombatRadius or 0
|
self.CombatRadius = CombatRadius or 0
|
||||||
@@ -207,3 +218,41 @@ function AI_CARGO_DISPATCHER_APC:SetCombatRadius( CombatRadius )
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Set whether the carrier will *not* use roads to *pickup* and *deploy* the cargo.
|
||||||
|
-- @param #AI_CARGO_DISPATCHER_APC self
|
||||||
|
-- @param #boolean Offroad If true, carrier will not use roads.
|
||||||
|
-- @param #number Formation Offroad formation used. Default is `ENUMS.Formation.Vehicle.Offroad`.
|
||||||
|
-- @return #AI_CARGO_DISPATCHER_APC self
|
||||||
|
function AI_CARGO_DISPATCHER_APC:SetOffRoad(Offroad, Formation)
|
||||||
|
|
||||||
|
self:SetPickupOffRoad(Offroad, Formation)
|
||||||
|
self:SetDeployOffRoad(Offroad, Formation)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set whether the carrier will *not* use roads to *pickup* the cargo.
|
||||||
|
-- @param #AI_CARGO_DISPATCHER_APC self
|
||||||
|
-- @param #boolean Offroad If true, carrier will not use roads.
|
||||||
|
-- @param #number Formation Offroad formation used. Default is `ENUMS.Formation.Vehicle.Offroad`.
|
||||||
|
-- @return #AI_CARGO_DISPATCHER_APC self
|
||||||
|
function AI_CARGO_DISPATCHER_APC:SetPickupOffRoad(Offroad, Formation)
|
||||||
|
|
||||||
|
self.pickupOffroad=Offroad
|
||||||
|
self.pickupFormation=Formation or ENUMS.Formation.Vehicle.OffRoad
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set whether the carrier will *not* use roads to *deploy* the cargo.
|
||||||
|
-- @param #AI_CARGO_DISPATCHER_APC self
|
||||||
|
-- @param #boolean Offroad If true, carrier will not use roads.
|
||||||
|
-- @param #number Formation Offroad formation used. Default is `ENUMS.Formation.Vehicle.Offroad`.
|
||||||
|
-- @return #AI_CARGO_DISPATCHER_APC self
|
||||||
|
function AI_CARGO_DISPATCHER_APC:SetDeployOffRoad(Offroad, Formation)
|
||||||
|
|
||||||
|
self.deployOffroad=Offroad
|
||||||
|
self.deployFormation=Formation or ENUMS.Formation.Vehicle.OffRoad
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
193
Moose Development/Moose/AI/AI_Cargo_Dispatcher_Ship.lua
Normal file
193
Moose Development/Moose/AI/AI_Cargo_Dispatcher_Ship.lua
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
--- **AI** -- (2.5.1) - Models the intelligent transportation of infantry and other cargo using Ships
|
||||||
|
--
|
||||||
|
-- ## Features:
|
||||||
|
--
|
||||||
|
-- * Transport cargo to various deploy zones using naval vehicles.
|
||||||
|
-- * Various @{Cargo.Cargo#CARGO} types can be transported, including infantry, vehicles, and crates.
|
||||||
|
-- * Define a deploy zone of various types to determine the destination of the cargo.
|
||||||
|
-- * Ships will follow shipping lanes as defined in the Mission Editor.
|
||||||
|
-- * Multiple ships can transport multiple cargo as a single group.
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ## Test Missions:
|
||||||
|
--
|
||||||
|
-- NEED TO DO
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ### Author: **acrojason** (derived from AI_Cargo_Dispatcher_APC by FlightControl)
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- @module AI.AI_Cargo_Dispatcher_Ship
|
||||||
|
-- @image AI_Cargo_Dispatching_For_Ship.JPG
|
||||||
|
|
||||||
|
--- @type AI_CARGO_DISPATCHER_SHIP
|
||||||
|
-- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER
|
||||||
|
|
||||||
|
|
||||||
|
--- A dynamic cargo transportation capability for AI groups.
|
||||||
|
--
|
||||||
|
-- Naval vessels can be mobilized to semi-intelligently transport cargo within the simulation.
|
||||||
|
--
|
||||||
|
-- The AI_CARGO_DISPATCHER_SHIP module is derived from the AI_CARGO_DISPATCHER module.
|
||||||
|
--
|
||||||
|
-- ## Note! In order to fully understand the mechanisms of the AI_CARGO_DISPATCHER_SHIP class, it is recommended that you first consult and READ the documentation of the @{AI.AI_Cargo_Dispatcher} module!!!
|
||||||
|
--
|
||||||
|
-- This will be particularly helpful in order to determine how to **Tailor the different cargo handling events**.
|
||||||
|
--
|
||||||
|
-- The AI_CARGO_DISPATCHER_SHIP class uses the @{Cargo.Cargo} capabilities within the MOOSE framwork.
|
||||||
|
-- Also ensure that you fully understand how to declare and setup Cargo objects within the MOOSE framework before using this class.
|
||||||
|
-- CARGO derived objects must generally be declared within the mission to make the AI_CARGO_DISPATCHER_SHIP object recognize the cargo.
|
||||||
|
--
|
||||||
|
--
|
||||||
|
-- # 1) AI_CARGO_DISPATCHER_SHIP constructor.
|
||||||
|
--
|
||||||
|
-- * @{AI_CARGO_DISPATCHER_SHIP.New}(): Creates a new AI_CARGO_DISPATCHER_SHIP object.
|
||||||
|
--
|
||||||
|
-- ---
|
||||||
|
--
|
||||||
|
-- # 2) AI_CARGO_DISPATCHER_SHIP 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.
|
||||||
|
--
|
||||||
|
-- So, each of the rows have the following structure.
|
||||||
|
--
|
||||||
|
-- * **From** => **Event** => **To**
|
||||||
|
--
|
||||||
|
-- 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.
|
||||||
|
--
|
||||||
|
-- These are the different possible state transitions of this state machine implementation:
|
||||||
|
--
|
||||||
|
-- * Idle => Start => Monitoring
|
||||||
|
-- * Monitoring => Monitor => Monitoring
|
||||||
|
-- * Monitoring => Stop => Idle
|
||||||
|
--
|
||||||
|
-- * Monitoring => Pickup => Monitoring
|
||||||
|
-- * Monitoring => Load => Monitoring
|
||||||
|
-- * Monitoring => Loading => Monitoring
|
||||||
|
-- * Monitoring => Loaded => Monitoring
|
||||||
|
-- * Monitoring => PickedUp => Monitoring
|
||||||
|
-- * Monitoring => Deploy => Monitoring
|
||||||
|
-- * Monitoring => Unload => Monitoring
|
||||||
|
-- * Monitoring => Unloaded => Monitoring
|
||||||
|
-- * Monitoring => Deployed => Monitoring
|
||||||
|
-- * Monitoring => Home => Monitoring
|
||||||
|
--
|
||||||
|
--
|
||||||
|
-- ## 2.1) AI_CARGO_DISPATCHER States.
|
||||||
|
--
|
||||||
|
-- * **Monitoring**: The process is dispatching.
|
||||||
|
-- * **Idle**: The process is idle.
|
||||||
|
--
|
||||||
|
-- ## 2.2) AI_CARGO_DISPATCHER Events.
|
||||||
|
--
|
||||||
|
-- * **Start**: Start the transport process.
|
||||||
|
-- * **Stop**: Stop the transport process.
|
||||||
|
-- * **Monitor**: Monitor and take action.
|
||||||
|
--
|
||||||
|
-- * **Pickup**: Pickup cargo.
|
||||||
|
-- * **Load**: Load the cargo.
|
||||||
|
-- * **Loading**: The dispatcher is coordinating the loading of a cargo.
|
||||||
|
-- * **Loaded**: Flag that the cargo is loaded.
|
||||||
|
-- * **PickedUp**: The dispatcher has loaded all requested cargo into the CarrierGroup.
|
||||||
|
-- * **Deploy**: Deploy cargo to a location.
|
||||||
|
-- * **Unload**: Unload the cargo.
|
||||||
|
-- * **Unloaded**: Flag that the cargo is unloaded.
|
||||||
|
-- * **Deployed**: All cargo is unloaded from the carriers in the group.
|
||||||
|
-- * **Home**: A Carrier is going home.
|
||||||
|
--
|
||||||
|
-- ## 2.3) Enhance your mission scripts with **Tailored** Event Handling!
|
||||||
|
--
|
||||||
|
-- Within your mission, you can capture these events when triggered, and tailor the events with your own code!
|
||||||
|
-- Check out the @{AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER} class at chapter 3 for details on the different event handlers that are available and how to use them.
|
||||||
|
--
|
||||||
|
-- **There are a lot of templates available that allows you to quickly setup an event handler for a specific event type!**
|
||||||
|
--
|
||||||
|
-- ---
|
||||||
|
--
|
||||||
|
-- # 3) Set the pickup parameters.
|
||||||
|
--
|
||||||
|
-- Several parameters can be set to pickup cargo:
|
||||||
|
--
|
||||||
|
-- * @{#AI_CARGO_DISPATCHER_SHIP.SetPickupRadius}(): Sets or randomizes the pickup location for the Ship around the cargo coordinate in a radius defined an outer and optional inner radius.
|
||||||
|
-- * @{#AI_CARGO_DISPATCHER_SHIP.SetPickupSpeed}(): Set the speed or randomizes the speed in km/h to pickup the cargo.
|
||||||
|
--
|
||||||
|
-- # 4) Set the deploy parameters.
|
||||||
|
--
|
||||||
|
-- Several parameters can be set to deploy cargo:
|
||||||
|
--
|
||||||
|
-- * @{#AI_CARGO_DISPATCHER_SHIP.SetDeployRadius}(): Sets or randomizes the deploy location for the Ship around the cargo coordinate in a radius defined an outer and an optional inner radius.
|
||||||
|
-- * @{#AI_CARGO_DISPATCHER_SHIP.SetDeploySpeed}(): Set the speed or randomizes the speed in km/h to deploy the cargo.
|
||||||
|
--
|
||||||
|
-- # 5) Set the home zone when there isn't any more cargo to pickup.
|
||||||
|
--
|
||||||
|
-- A home zone can be specified to where the Ship will move when there isn't any cargo left for pickup.
|
||||||
|
-- Use @{#AI_CARGO_DISPATCHER_SHIP.SetHomeZone}() to specify the home zone.
|
||||||
|
--
|
||||||
|
-- If no home zone is specified, the Ship will wait near the deploy zone for a new pickup command.
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- @field #AI_CARGO_DISPATCHER_SHIP
|
||||||
|
AI_CARGO_DISPATCHER_SHIP = {
|
||||||
|
ClassName = "AI_CARGO_DISPATCHER_SHIP"
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Creates a new AI_CARGO_DISPATCHER_SHIP object.
|
||||||
|
-- @param #AI_CARGO_DISPATCHER_SHIP self
|
||||||
|
-- @param Core.Set#SET_GROUP ShipSet The set of @{Wrapper.Group#GROUP} objects of Ships that will transport the cargo
|
||||||
|
-- @param Core.Set#SET_CARGO CargoSet The set of @{Cargo.Cargo#CARGO} objects, which can be CARGO_GROUP, CARGO_CRATE, or CARGO_SLINGLOAD objects.
|
||||||
|
-- @param Core.Set#SET_ZONE PickupZoneSet The set of pickup zones which are used to determine from where the cargo can be picked up by the Ship.
|
||||||
|
-- @param Core.Set#SET_ZONE DeployZoneSet The set of deploy zones which determine where the cargo will be deployed by the Ship.
|
||||||
|
-- @param #table ShippingLane Table containing list of Shipping Lanes to be used
|
||||||
|
-- @return #AI_CARGO_DISPATCHER_SHIP
|
||||||
|
-- @usage
|
||||||
|
--
|
||||||
|
-- -- An AI dispatcher object for a naval group, moving cargo from pickup zones to deploy zones via a predetermined Shipping Lane
|
||||||
|
--
|
||||||
|
-- local SetCargoInfantry = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart()
|
||||||
|
-- local SetShip = SET_GROUP:New():FilterPrefixes( "Ship" ):FilterStart()
|
||||||
|
-- local SetPickupZones = SET_ZONE:New():FilterPrefixes( "Pickup" ):FilterStart()
|
||||||
|
-- local SetDeployZones = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart()
|
||||||
|
-- NEED MORE THOUGHT - ShippingLane is part of Warehouse.......
|
||||||
|
-- local ShippingLane = GROUP:New():FilterPrefixes( "ShippingLane" ):FilterStart()
|
||||||
|
--
|
||||||
|
-- AICargoDispatcherShip = AI_CARGO_DISPATCHER_SHIP:New( SetShip, SetCargoInfantry, SetPickupZones, SetDeployZones, ShippingLane )
|
||||||
|
-- AICargoDispatcherShip:Start()
|
||||||
|
--
|
||||||
|
function AI_CARGO_DISPATCHER_SHIP:New( ShipSet, CargoSet, PickupZoneSet, DeployZoneSet, ShippingLane )
|
||||||
|
|
||||||
|
local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:New( ShipSet, CargoSet, PickupZoneSet, DeployZoneSet ) )
|
||||||
|
|
||||||
|
self:SetPickupSpeed( 60, 10 )
|
||||||
|
self:SetDeploySpeed( 60, 10 )
|
||||||
|
|
||||||
|
self:SetPickupRadius( 500, 6000 )
|
||||||
|
self:SetDeployRadius( 500, 6000 )
|
||||||
|
|
||||||
|
self:SetPickupHeight( 0, 0 )
|
||||||
|
self:SetDeployHeight( 0, 0 )
|
||||||
|
|
||||||
|
self:SetShippingLane( ShippingLane )
|
||||||
|
|
||||||
|
self:SetMonitorTimeInterval( 600 )
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function AI_CARGO_DISPATCHER_SHIP:SetShippingLane( ShippingLane )
|
||||||
|
self.ShippingLane = ShippingLane
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function AI_CARGO_DISPATCHER_SHIP:AICargo( Ship, CargoSet )
|
||||||
|
|
||||||
|
return AI_CARGO_SHIP:New( Ship, CargoSet, 0, self.ShippingLane )
|
||||||
|
end
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
--- **AI** -- (R2.4) - Models the intelligent transportation of infantry (cargo).
|
--- **AI** - Models the intelligent transportation of cargo using helicopters.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
--
|
--
|
||||||
-- ## Infantry health.
|
-- ## Infantry health.
|
||||||
--
|
--
|
||||||
-- When infantry is unboarded from the APCs, the infantry is actually respawned into the battlefield.
|
-- When infantry is unboarded from the helicopters, the infantry is actually respawned into the battlefield.
|
||||||
-- As a result, the unboarding infantry is very _healthy_ every time it unboards.
|
-- As a result, the unboarding infantry is very _healthy_ every time it unboards.
|
||||||
-- This is due to the limitation of the DCS simulator, which is not able to specify the health of new spawned units as a parameter.
|
-- This is due to the limitation of the DCS simulator, which is not able to specify the health of new spawned units as a parameter.
|
||||||
-- However, infantry that was destroyed when unboarded, won't be respawned again. Destroyed is destroyed.
|
-- However, infantry that was destroyed when unboarded, won't be respawned again. Destroyed is destroyed.
|
||||||
@@ -64,26 +64,25 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet )
|
|||||||
self.Zone = ZONE_GROUP:New( Helicopter:GetName(), Helicopter, 300 )
|
self.Zone = ZONE_GROUP:New( Helicopter:GetName(), Helicopter, 300 )
|
||||||
|
|
||||||
self:SetStartState( "Unloaded" )
|
self:SetStartState( "Unloaded" )
|
||||||
|
-- Boarding
|
||||||
|
self:AddTransition( "Unloaded", "Pickup", "Unloaded" )
|
||||||
|
self:AddTransition( "*", "Landed", "*" )
|
||||||
|
self:AddTransition( "*", "Load", "*" )
|
||||||
|
self:AddTransition( "*", "Loaded", "Loaded" )
|
||||||
|
self:AddTransition( "Loaded", "PickedUp", "Loaded" )
|
||||||
|
|
||||||
self:AddTransition( "Unloaded", "Pickup", "*" )
|
-- Unboarding
|
||||||
self:AddTransition( "Loaded", "Deploy", "*" )
|
self:AddTransition( "Loaded", "Deploy", "*" )
|
||||||
|
self:AddTransition( "*", "Queue", "*" )
|
||||||
|
self:AddTransition( "*", "Orbit" , "*" )
|
||||||
|
self:AddTransition( "*", "Destroyed", "*" )
|
||||||
|
self:AddTransition( "*", "Unload", "*" )
|
||||||
|
self:AddTransition( "*", "Unloaded", "Unloaded" )
|
||||||
|
self:AddTransition( "Unloaded", "Deployed", "Unloaded" )
|
||||||
|
|
||||||
self:AddTransition( { "Unloaded", "Loading" }, "Load", "Boarding" )
|
-- RTB
|
||||||
self:AddTransition( "Boarding", "Board", "Boarding" )
|
|
||||||
self:AddTransition( "Boarding", "Loaded", "Boarding" )
|
|
||||||
self:AddTransition( "Boarding", "PickedUp", "Loaded" )
|
|
||||||
self:AddTransition( "Loaded", "Unload", "Unboarding" )
|
|
||||||
self:AddTransition( "Unboarding", "Unboard", "Unboarding" )
|
|
||||||
self:AddTransition( "Unboarding", "Unloaded", "Unboarding" )
|
|
||||||
self:AddTransition( "Unboarding", "Deployed", "Unloaded" )
|
|
||||||
|
|
||||||
self:AddTransition( "*", "Landed", "*" )
|
|
||||||
self:AddTransition( "*", "Queue", "*" )
|
|
||||||
self:AddTransition( "*", "Orbit" , "*" )
|
|
||||||
self:AddTransition( "*", "Home" , "*" )
|
self:AddTransition( "*", "Home" , "*" )
|
||||||
|
|
||||||
self:AddTransition( "*", "Destroyed", "Destroyed" )
|
|
||||||
|
|
||||||
--- Pickup Handler OnBefore for AI_CARGO_HELICOPTER
|
--- Pickup Handler OnBefore for AI_CARGO_HELICOPTER
|
||||||
-- @function [parent=#AI_CARGO_HELICOPTER] OnBeforePickup
|
-- @function [parent=#AI_CARGO_HELICOPTER] OnBeforePickup
|
||||||
-- @param #AI_CARGO_HELICOPTER self
|
-- @param #AI_CARGO_HELICOPTER self
|
||||||
@@ -100,13 +99,31 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet )
|
|||||||
-- @param #string Event
|
-- @param #string Event
|
||||||
-- @param #string To
|
-- @param #string To
|
||||||
-- @param Core.Point#COORDINATE Coordinate
|
-- @param Core.Point#COORDINATE Coordinate
|
||||||
-- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go.
|
-- @param #number Speed Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
|
||||||
|
|
||||||
|
--- PickedUp Handler OnAfter for AI_CARGO_HELICOPTER - Cargo set has been picked up, ready to deploy
|
||||||
|
-- @function [parent=#AI_CARGO_HELICOPTER] OnAfterPickedUp
|
||||||
|
-- @param #AI_CARGO_HELICOPTER self
|
||||||
|
-- @param Wrapper.Group#GROUP Helicopter The helicopter #GROUP object
|
||||||
|
-- @param #string From
|
||||||
|
-- @param #string Event
|
||||||
|
-- @param #string To
|
||||||
|
-- @param Wrapper.Unit#UNIT Unit The helicopter #UNIT object
|
||||||
|
|
||||||
|
--- Unloaded Handler OnAfter for AI_CARGO_HELICOPTER - Cargo unloaded, carrier is empty
|
||||||
|
-- @function [parent=#AI_CARGO_HELICOPTER] OnAfterUnloaded
|
||||||
|
-- @param #AI_CARGO_HELICOPTER self
|
||||||
|
-- @param #string From
|
||||||
|
-- @param #string Event
|
||||||
|
-- @param #string To
|
||||||
|
-- @param Cargo.CargoGroup#CARGO_GROUP Cargo The #CARGO_GROUP object.
|
||||||
|
-- @param Wrapper.Unit#UNIT Unit The helicopter #UNIT object
|
||||||
|
|
||||||
--- Pickup Trigger for AI_CARGO_HELICOPTER
|
--- Pickup Trigger for AI_CARGO_HELICOPTER
|
||||||
-- @function [parent=#AI_CARGO_HELICOPTER] Pickup
|
-- @function [parent=#AI_CARGO_HELICOPTER] Pickup
|
||||||
-- @param #AI_CARGO_HELICOPTER self
|
-- @param #AI_CARGO_HELICOPTER self
|
||||||
-- @param Core.Point#COORDINATE Coordinate
|
-- @param Core.Point#COORDINATE Coordinate
|
||||||
-- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go.
|
-- @param #number Speed Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
|
||||||
|
|
||||||
--- Pickup Asynchronous Trigger for AI_CARGO_HELICOPTER
|
--- Pickup Asynchronous Trigger for AI_CARGO_HELICOPTER
|
||||||
-- @function [parent=#AI_CARGO_HELICOPTER] __Pickup
|
-- @function [parent=#AI_CARGO_HELICOPTER] __Pickup
|
||||||
@@ -122,7 +139,7 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet )
|
|||||||
-- @param #string Event
|
-- @param #string Event
|
||||||
-- @param #string To
|
-- @param #string To
|
||||||
-- @param Core.Point#COORDINATE Coordinate Place at which cargo is deployed.
|
-- @param Core.Point#COORDINATE Coordinate Place at which cargo is deployed.
|
||||||
-- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go.
|
-- @param #number Speed Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
|
||||||
-- @return #boolean
|
-- @return #boolean
|
||||||
|
|
||||||
--- Deploy Handler OnAfter for AI_CARGO_HELICOPTER
|
--- Deploy Handler OnAfter for AI_CARGO_HELICOPTER
|
||||||
@@ -132,21 +149,42 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet )
|
|||||||
-- @param #string Event
|
-- @param #string Event
|
||||||
-- @param #string To
|
-- @param #string To
|
||||||
-- @param Core.Point#COORDINATE Coordinate
|
-- @param Core.Point#COORDINATE Coordinate
|
||||||
-- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go.
|
-- @param #number Speed Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
|
||||||
|
|
||||||
|
--- Deployed Handler OnAfter for AI_CARGO_HELICOPTER
|
||||||
|
-- @function [parent=#AI_CARGO_HELICOPTER] OnAfterDeployed
|
||||||
|
-- @param #AI_CARGO_HELICOPTER self
|
||||||
|
-- @param #string From
|
||||||
|
-- @param #string Event
|
||||||
|
-- @param #string To
|
||||||
|
|
||||||
--- Deploy Trigger for AI_CARGO_HELICOPTER
|
--- Deploy Trigger for AI_CARGO_HELICOPTER
|
||||||
-- @function [parent=#AI_CARGO_HELICOPTER] Deploy
|
-- @function [parent=#AI_CARGO_HELICOPTER] Deploy
|
||||||
-- @param #AI_CARGO_HELICOPTER self
|
-- @param #AI_CARGO_HELICOPTER self
|
||||||
-- @param Core.Point#COORDINATE Coordinate Place at which the cargo is deployed.
|
-- @param Core.Point#COORDINATE Coordinate Place at which the cargo is deployed.
|
||||||
-- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go.
|
-- @param #number Speed Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
|
||||||
|
|
||||||
--- Deploy Asynchronous Trigger for AI_CARGO_HELICOPTER
|
--- Deploy Asynchronous Trigger for AI_CARGO_HELICOPTER
|
||||||
-- @function [parent=#AI_CARGO_HELICOPTER] __Deploy
|
-- @function [parent=#AI_CARGO_HELICOPTER] __Deploy
|
||||||
-- @param #number Delay Delay in seconds.
|
-- @param #number Delay Delay in seconds.
|
||||||
-- @param #AI_CARGO_HELICOPTER self
|
-- @param #AI_CARGO_HELICOPTER self
|
||||||
-- @param Core.Point#COORDINATE Coordinate Place at which the cargo is deployed.
|
-- @param Core.Point#COORDINATE Coordinate Place at which the cargo is deployed.
|
||||||
-- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go.
|
-- @param #number Speed Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
|
||||||
|
|
||||||
|
--- Home Trigger for AI_CARGO_HELICOPTER
|
||||||
|
-- @function [parent=#AI_CARGO_HELICOPTER] Home
|
||||||
|
-- @param #AI_CARGO_HELICOPTER self
|
||||||
|
-- @param Core.Point#COORDINATE Coordinate Place to which the helicopter will go.
|
||||||
|
-- @param #number Speed (optional) Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
|
||||||
|
-- @param #number Height (optional) Height the Helicopter should be flying at.
|
||||||
|
|
||||||
|
--- Home Asynchronous Trigger for AI_CARGO_HELICOPTER
|
||||||
|
-- @function [parent=#AI_CARGO_HELICOPTER] __Home
|
||||||
|
-- @param #number Delay Delay in seconds.
|
||||||
|
-- @param #AI_CARGO_HELICOPTER self
|
||||||
|
-- @param Core.Point#COORDINATE Coordinate Place to which the helicopter will go.
|
||||||
|
-- @param #number Speed (optional) Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
|
||||||
|
-- @param #number Height (optional) Height the Helicopter should be flying at.
|
||||||
|
|
||||||
-- We need to capture the Crash events for the helicopters.
|
-- We need to capture the Crash events for the helicopters.
|
||||||
-- The helicopter reference is used in the semaphore AI_CARGO_QUEUE.
|
-- The helicopter reference is used in the semaphore AI_CARGO_QUEUE.
|
||||||
@@ -173,6 +211,9 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet )
|
|||||||
|
|
||||||
self:SetCarrier( Helicopter )
|
self:SetCarrier( Helicopter )
|
||||||
|
|
||||||
|
self.landingspeed = 15 -- kph
|
||||||
|
self.landingheight = 5.5 -- meter
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -221,6 +262,25 @@ function AI_CARGO_HELICOPTER:SetCarrier( Helicopter )
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Set landingspeed and -height for helicopter landings. Adjust after tracing if your helis get stuck after landing.
|
||||||
|
-- @param #AI_CARGO_HELICOPTER self
|
||||||
|
-- @param #number speed Landing speed in kph(!), e.g. 15
|
||||||
|
-- @param #number height Landing height in meters(!), e.g. 5.5
|
||||||
|
-- @return #AI_CARGO_HELICOPTER self
|
||||||
|
-- @usage If your choppers get stuck, add tracing to your script to determine if they hit the right parameters like so:
|
||||||
|
--
|
||||||
|
-- BASE:TraceOn()
|
||||||
|
-- BASE:TraceClass("AI_CARGO_HELICOPTER")
|
||||||
|
--
|
||||||
|
-- Watch the DCS.log for entries stating `Helicopter:<name>, Height = Helicopter:<number>, Velocity = Helicopter:<number>`
|
||||||
|
-- Adjust if necessary.
|
||||||
|
function AI_CARGO_HELICOPTER:SetLandingSpeedAndHeight(speed, height)
|
||||||
|
local _speed = speed or 15
|
||||||
|
local _height = height or 5.5
|
||||||
|
self.landingheight = _height
|
||||||
|
self.landingspeed = _speed
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- @param #AI_CARGO_HELICOPTER self
|
--- @param #AI_CARGO_HELICOPTER self
|
||||||
-- @param Wrapper.Group#GROUP Helicopter
|
-- @param Wrapper.Group#GROUP Helicopter
|
||||||
@@ -228,7 +288,7 @@ end
|
|||||||
-- @param Event
|
-- @param Event
|
||||||
-- @param To
|
-- @param To
|
||||||
function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To )
|
function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To )
|
||||||
|
self:F({From, Event, To})
|
||||||
Helicopter:F( { Name = Helicopter:GetName() } )
|
Helicopter:F( { Name = Helicopter:GetName() } )
|
||||||
|
|
||||||
if Helicopter and Helicopter:IsAlive() then
|
if Helicopter and Helicopter:IsAlive() then
|
||||||
@@ -237,13 +297,13 @@ function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To )
|
|||||||
-- 1 - When the helo lands normally on the ground.
|
-- 1 - When the helo lands normally on the ground.
|
||||||
-- 2 - when the helo is hit and goes RTB or even when it is destroyed.
|
-- 2 - when the helo is hit and goes RTB or even when it is destroyed.
|
||||||
-- For point 2, this is an issue, the infantry may not unload in this case!
|
-- For point 2, this is an issue, the infantry may not unload in this case!
|
||||||
-- So we check if the helo is on the ground, and velocity< 5.
|
-- So we check if the helo is on the ground, and velocity< 15.
|
||||||
-- Only then the infantry can unload (and load too, for consistency)!
|
-- Only then the infantry can unload (and load too, for consistency)!
|
||||||
|
|
||||||
self:F( { Helicopter:GetName(), Height = Helicopter:GetHeight( true ), Velocity = Helicopter:GetVelocityKMH() } )
|
self:T( { Helicopter:GetName(), Height = Helicopter:GetHeight( true ), Velocity = Helicopter:GetVelocityKMH() } )
|
||||||
|
|
||||||
if self.RoutePickup == true then
|
if self.RoutePickup == true then
|
||||||
if Helicopter:GetHeight( true ) <= 5 and Helicopter:GetVelocityKMH() < 10 then
|
if Helicopter:GetHeight( true ) <= self.landingheight then --and Helicopter:GetVelocityKMH() < self.landingspeed then
|
||||||
--self:Load( Helicopter:GetPointVec2() )
|
--self:Load( Helicopter:GetPointVec2() )
|
||||||
self:Load( self.PickupZone )
|
self:Load( self.PickupZone )
|
||||||
self.RoutePickup = false
|
self.RoutePickup = false
|
||||||
@@ -251,7 +311,7 @@ function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To )
|
|||||||
end
|
end
|
||||||
|
|
||||||
if self.RouteDeploy == true then
|
if self.RouteDeploy == true then
|
||||||
if Helicopter:GetHeight( true ) <= 5 and Helicopter:GetVelocityKMH() < 10 then
|
if Helicopter:GetHeight( true ) <= self.landingheight then --and Helicopter:GetVelocityKMH() < self.landingspeed then
|
||||||
self:Unload( self.DeployZone )
|
self:Unload( self.DeployZone )
|
||||||
self.RouteDeploy = false
|
self.RouteDeploy = false
|
||||||
end
|
end
|
||||||
@@ -269,7 +329,7 @@ end
|
|||||||
-- @param Core.Point#COORDINATE Coordinate
|
-- @param Core.Point#COORDINATE Coordinate
|
||||||
-- @param #number Speed
|
-- @param #number Speed
|
||||||
function AI_CARGO_HELICOPTER:onafterQueue( Helicopter, From, Event, To, Coordinate, Speed, DeployZone )
|
function AI_CARGO_HELICOPTER:onafterQueue( Helicopter, From, Event, To, Coordinate, Speed, DeployZone )
|
||||||
|
self:F({From, Event, To, Coordinate, Speed, DeployZone})
|
||||||
local HelicopterInZone = false
|
local HelicopterInZone = false
|
||||||
|
|
||||||
if Helicopter and Helicopter:IsAlive() == true then
|
if Helicopter and Helicopter:IsAlive() == true then
|
||||||
@@ -308,7 +368,11 @@ function AI_CARGO_HELICOPTER:onafterQueue( Helicopter, From, Event, To, Coordina
|
|||||||
-- true
|
-- true
|
||||||
-- )
|
-- )
|
||||||
-- Route[#Route+1] = WaypointFrom
|
-- Route[#Route+1] = WaypointFrom
|
||||||
local CoordinateTo = Coordinate
|
local CoordinateTo = Coordinate
|
||||||
|
|
||||||
|
local landheight = CoordinateTo:GetLandHeight() -- get target height
|
||||||
|
CoordinateTo.y = landheight + 50 -- flight height should be 50m above ground
|
||||||
|
|
||||||
local WaypointTo = CoordinateTo:WaypointAir(
|
local WaypointTo = CoordinateTo:WaypointAir(
|
||||||
"RADIO",
|
"RADIO",
|
||||||
POINT_VEC3.RoutePointType.TurningPoint,
|
POINT_VEC3.RoutePointType.TurningPoint,
|
||||||
@@ -348,28 +412,17 @@ end
|
|||||||
-- @param Core.Point#COORDINATE Coordinate
|
-- @param Core.Point#COORDINATE Coordinate
|
||||||
-- @param #number Speed
|
-- @param #number Speed
|
||||||
function AI_CARGO_HELICOPTER:onafterOrbit( Helicopter, From, Event, To, Coordinate )
|
function AI_CARGO_HELICOPTER:onafterOrbit( Helicopter, From, Event, To, Coordinate )
|
||||||
|
self:F({From, Event, To, Coordinate})
|
||||||
|
|
||||||
if Helicopter and Helicopter:IsAlive() then
|
if Helicopter and Helicopter:IsAlive() then
|
||||||
|
|
||||||
local Route = {}
|
local Route = {}
|
||||||
|
|
||||||
-- local CoordinateFrom = Helicopter:GetCoordinate()
|
local CoordinateTo = Coordinate
|
||||||
-- local WaypointFrom = CoordinateFrom:WaypointAir(
|
local landheight = CoordinateTo:GetLandHeight() -- get target height
|
||||||
-- "RADIO",
|
CoordinateTo.y = landheight + 50 -- flight height should be 50m above ground
|
||||||
-- POINT_VEC3.RoutePointType.TurningPoint,
|
|
||||||
-- POINT_VEC3.RoutePointAction.TurningPoint,
|
local WaypointTo = CoordinateTo:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, 50, true)
|
||||||
-- Speed,
|
|
||||||
-- true
|
|
||||||
-- )
|
|
||||||
-- Route[#Route+1] = WaypointFrom
|
|
||||||
local CoordinateTo = Coordinate
|
|
||||||
local WaypointTo = CoordinateTo:WaypointAir(
|
|
||||||
"RADIO",
|
|
||||||
POINT_VEC3.RoutePointType.TurningPoint,
|
|
||||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
|
||||||
50,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
Route[#Route+1] = WaypointTo
|
Route[#Route+1] = WaypointTo
|
||||||
|
|
||||||
local Tasks = {}
|
local Tasks = {}
|
||||||
@@ -379,7 +432,7 @@ function AI_CARGO_HELICOPTER:onafterOrbit( Helicopter, From, Event, To, Coordina
|
|||||||
Route[#Route+1] = WaypointTo
|
Route[#Route+1] = WaypointTo
|
||||||
|
|
||||||
-- Now route the helicopter
|
-- Now route the helicopter
|
||||||
Helicopter:Route( Route, 0 )
|
Helicopter:Route(Route, 0)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -395,7 +448,7 @@ end
|
|||||||
-- @param #boolean Deployed Cargo is deployed.
|
-- @param #boolean Deployed Cargo is deployed.
|
||||||
-- @return #boolean True if all cargo has been unloaded.
|
-- @return #boolean True if all cargo has been unloaded.
|
||||||
function AI_CARGO_HELICOPTER:onafterDeployed( Helicopter, From, Event, To, DeployZone )
|
function AI_CARGO_HELICOPTER:onafterDeployed( Helicopter, From, Event, To, DeployZone )
|
||||||
self:F( { Helicopter, From, Event, To, DeployZone = DeployZone } )
|
self:F( { From, Event, To, DeployZone = DeployZone } )
|
||||||
|
|
||||||
self:Orbit( Helicopter:GetCoordinate(), 50 )
|
self:Orbit( Helicopter:GetCoordinate(), 50 )
|
||||||
|
|
||||||
@@ -408,7 +461,6 @@ function AI_CARGO_HELICOPTER:onafterDeployed( Helicopter, From, Event, To, Deplo
|
|||||||
|
|
||||||
self:GetParent( self, AI_CARGO_HELICOPTER ).onafterDeployed( self, Helicopter, From, Event, To, DeployZone )
|
self:GetParent( self, AI_CARGO_HELICOPTER ).onafterDeployed( self, Helicopter, From, Event, To, DeployZone )
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- On after Pickup event.
|
--- On after Pickup event.
|
||||||
@@ -418,10 +470,11 @@ end
|
|||||||
-- @param Event
|
-- @param Event
|
||||||
-- @param To
|
-- @param To
|
||||||
-- @param Core.Point#COORDINATE Coordinate Pickup place.
|
-- @param Core.Point#COORDINATE Coordinate Pickup place.
|
||||||
-- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go.
|
-- @param #number Speed Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
|
||||||
-- @param #number Height Height in meters to move to the pickup coordinate. This parameter is ignored for APCs.
|
-- @param #number Height Height in meters to move to the pickup coordinate. This parameter is ignored for APCs.
|
||||||
-- @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.
|
-- @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_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordinate, Speed, Height, PickupZone )
|
function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordinate, Speed, Height, PickupZone )
|
||||||
|
self:F({Coordinate, Speed, Height, PickupZone })
|
||||||
|
|
||||||
if Helicopter and Helicopter:IsAlive() ~= nil then
|
if Helicopter and Helicopter:IsAlive() ~= nil then
|
||||||
|
|
||||||
@@ -436,25 +489,16 @@ function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordin
|
|||||||
|
|
||||||
--- Calculate the target route point.
|
--- Calculate the target route point.
|
||||||
local CoordinateFrom = Helicopter:GetCoordinate()
|
local CoordinateFrom = Helicopter:GetCoordinate()
|
||||||
local CoordinateTo = Coordinate
|
|
||||||
|
|
||||||
--- Create a route point of type air.
|
--- Create a route point of type air.
|
||||||
local WaypointFrom = CoordinateFrom:WaypointAir(
|
local WaypointFrom = CoordinateFrom:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, _speed, true)
|
||||||
"RADIO",
|
|
||||||
POINT_VEC3.RoutePointType.TurningPoint,
|
|
||||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
|
||||||
_speed,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
|
|
||||||
--- Create a route point of type air.
|
--- Create a route point of type air.
|
||||||
local WaypointTo = CoordinateTo:WaypointAir(
|
local CoordinateTo = Coordinate
|
||||||
"RADIO",
|
local landheight = CoordinateTo:GetLandHeight() -- get target height
|
||||||
POINT_VEC3.RoutePointType.TurningPoint,
|
CoordinateTo.y = landheight + 50 -- flight height should be 50m above ground
|
||||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
|
||||||
_speed,
|
local WaypointTo = CoordinateTo:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint,_speed, true)
|
||||||
true
|
|
||||||
)
|
|
||||||
|
|
||||||
Route[#Route+1] = WaypointFrom
|
Route[#Route+1] = WaypointFrom
|
||||||
Route[#Route+1] = WaypointTo
|
Route[#Route+1] = WaypointTo
|
||||||
@@ -495,10 +539,10 @@ end
|
|||||||
-- @param Event
|
-- @param Event
|
||||||
-- @param To
|
-- @param To
|
||||||
-- @param Core.Point#COORDINATE Coordinate Place at which the cargo is deployed.
|
-- @param Core.Point#COORDINATE Coordinate Place at which the cargo is deployed.
|
||||||
-- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go.
|
-- @param #number Speed Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
|
||||||
-- @param #number Height Height in meters to move to the deploy coordinate.
|
-- @param #number Height Height in meters to move to the deploy coordinate.
|
||||||
function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordinate, Speed, Height, DeployZone )
|
function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordinate, Speed, Height, DeployZone )
|
||||||
|
self:F({From, Event, To, Coordinate, Speed, Height, DeployZone})
|
||||||
if Helicopter and Helicopter:IsAlive() ~= nil then
|
if Helicopter and Helicopter:IsAlive() ~= nil then
|
||||||
|
|
||||||
self.RouteDeploy = true
|
self.RouteDeploy = true
|
||||||
@@ -514,25 +558,17 @@ function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordin
|
|||||||
|
|
||||||
--- Create a route point of type air.
|
--- Create a route point of type air.
|
||||||
local CoordinateFrom = Helicopter:GetCoordinate()
|
local CoordinateFrom = Helicopter:GetCoordinate()
|
||||||
local WaypointFrom = CoordinateFrom:WaypointAir(
|
local WaypointFrom = CoordinateFrom:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, _speed, true)
|
||||||
"RADIO",
|
|
||||||
POINT_VEC3.RoutePointType.TurningPoint,
|
|
||||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
|
||||||
_speed,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
Route[#Route+1] = WaypointFrom
|
Route[#Route+1] = WaypointFrom
|
||||||
Route[#Route+1] = WaypointFrom
|
Route[#Route+1] = WaypointFrom
|
||||||
|
|
||||||
--- Create a route point of type air.
|
--- Create a route point of type air.
|
||||||
local CoordinateTo = Coordinate
|
|
||||||
local WaypointTo = CoordinateTo:WaypointAir(
|
local CoordinateTo = Coordinate
|
||||||
"RADIO",
|
local landheight = CoordinateTo:GetLandHeight() -- get target height
|
||||||
POINT_VEC3.RoutePointType.TurningPoint,
|
CoordinateTo.y = landheight + 50 -- flight height should be 50m above ground
|
||||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
|
||||||
_speed,
|
local WaypointTo = CoordinateTo:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, _speed, true)
|
||||||
true
|
|
||||||
)
|
|
||||||
|
|
||||||
Route[#Route+1] = WaypointTo
|
Route[#Route+1] = WaypointTo
|
||||||
Route[#Route+1] = WaypointTo
|
Route[#Route+1] = WaypointTo
|
||||||
@@ -542,7 +578,9 @@ function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordin
|
|||||||
|
|
||||||
local Tasks = {}
|
local Tasks = {}
|
||||||
|
|
||||||
|
-- The _Deploy function does not exist.
|
||||||
Tasks[#Tasks+1] = Helicopter:TaskFunction( "AI_CARGO_HELICOPTER._Deploy", self, Coordinate, DeployZone )
|
Tasks[#Tasks+1] = Helicopter:TaskFunction( "AI_CARGO_HELICOPTER._Deploy", self, Coordinate, DeployZone )
|
||||||
|
|
||||||
Tasks[#Tasks+1] = Helicopter:TaskOrbitCircle( math.random( 30, 100 ), _speed, CoordinateTo:GetRandomCoordinateInRadius( 800, 500 ) )
|
Tasks[#Tasks+1] = Helicopter:TaskOrbitCircle( math.random( 30, 100 ), _speed, CoordinateTo:GetRandomCoordinateInRadius( 800, 500 ) )
|
||||||
|
|
||||||
--Tasks[#Tasks+1] = Helicopter:TaskLandAtVec2( CoordinateTo:GetVec2() )
|
--Tasks[#Tasks+1] = Helicopter:TaskLandAtVec2( CoordinateTo:GetVec2() )
|
||||||
@@ -566,10 +604,11 @@ end
|
|||||||
-- @param Event
|
-- @param Event
|
||||||
-- @param To
|
-- @param To
|
||||||
-- @param Core.Point#COORDINATE Coordinate Home place.
|
-- @param Core.Point#COORDINATE Coordinate Home place.
|
||||||
-- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go.
|
-- @param #number Speed Speed in km/h to fly to the pickup coordinate. Default is 50% of max possible speed the unit can go.
|
||||||
-- @param #number Height Height in meters to move to 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.
|
-- @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_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinate, Speed, Height, HomeZone )
|
function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinate, Speed, Height, HomeZone )
|
||||||
|
self:F({From, Event, To, Coordinate, Speed, Height})
|
||||||
|
|
||||||
if Helicopter and Helicopter:IsAlive() ~= nil then
|
if Helicopter and Helicopter:IsAlive() ~= nil then
|
||||||
|
|
||||||
@@ -579,30 +618,23 @@ function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinat
|
|||||||
|
|
||||||
--- Calculate the target route point.
|
--- Calculate the target route point.
|
||||||
|
|
||||||
Coordinate.y = Height
|
--Coordinate.y = Height
|
||||||
|
Height = Height or 50
|
||||||
|
|
||||||
Speed = Speed or Helicopter:GetSpeedMax()*0.5
|
Speed = Speed or Helicopter:GetSpeedMax()*0.5
|
||||||
|
|
||||||
--- Create a route point of type air.
|
--- Create a route point of type air.
|
||||||
local CoordinateFrom = Helicopter:GetCoordinate()
|
local CoordinateFrom = Helicopter:GetCoordinate()
|
||||||
local WaypointFrom = CoordinateFrom:WaypointAir(
|
|
||||||
"RADIO",
|
local WaypointFrom = CoordinateFrom:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, Speed, true)
|
||||||
POINT_VEC3.RoutePointType.TurningPoint,
|
|
||||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
|
||||||
Speed ,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
Route[#Route+1] = WaypointFrom
|
Route[#Route+1] = WaypointFrom
|
||||||
|
|
||||||
--- Create a route point of type air.
|
--- Create a route point of type air.
|
||||||
local CoordinateTo = Coordinate
|
local CoordinateTo = Coordinate
|
||||||
local WaypointTo = CoordinateTo:WaypointAir(
|
local landheight = CoordinateTo:GetLandHeight() -- get target height
|
||||||
"RADIO",
|
CoordinateTo.y = landheight + Height -- flight height should be 50m above ground
|
||||||
POINT_VEC3.RoutePointType.TurningPoint,
|
|
||||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
local WaypointTo = CoordinateTo:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, Speed, true)
|
||||||
Speed ,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
|
|
||||||
Route[#Route+1] = WaypointTo
|
Route[#Route+1] = WaypointTo
|
||||||
|
|
||||||
@@ -617,8 +649,7 @@ function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinat
|
|||||||
Route[#Route+1] = WaypointTo
|
Route[#Route+1] = WaypointTo
|
||||||
|
|
||||||
-- Now route the helicopter
|
-- Now route the helicopter
|
||||||
Helicopter:Route( Route, 0 )
|
Helicopter:Route(Route, 0)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
397
Moose Development/Moose/AI/AI_Cargo_Ship.lua
Normal file
397
Moose Development/Moose/AI/AI_Cargo_Ship.lua
Normal file
@@ -0,0 +1,397 @@
|
|||||||
|
--- **AI** -- (R2.5.1) - Models the intelligent transportation of infantry and other cargo.
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ### Author: **acrojason** (derived from AI_Cargo_APC by FlightControl)
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- @module AI.AI_Cargo_Ship
|
||||||
|
-- @image AI_Cargo_Dispatching_For_Ship.JPG
|
||||||
|
|
||||||
|
--- @type AI_CARGO_SHIP
|
||||||
|
-- @extends AI.AI_Cargo#AI_CARGO
|
||||||
|
|
||||||
|
--- Brings a dynamic cargo handling capability for an AI naval group.
|
||||||
|
--
|
||||||
|
-- Naval ships can be utilized to transport cargo around the map following naval shipping lanes.
|
||||||
|
-- The AI_CARGO_SHIP class uses the @{Cargo.Cargo} capabilities within the MOOSE framework.
|
||||||
|
-- @{Cargo.Cargo} must be declared within the mission or warehouse to make the AI_CARGO_SHIP recognize the cargo.
|
||||||
|
-- Please consult the @{Cargo.Cargo} module for more information.
|
||||||
|
--
|
||||||
|
-- ## Cargo loading.
|
||||||
|
--
|
||||||
|
-- The module will automatically load cargo when the Ship is within boarding or loading radius.
|
||||||
|
-- The boarding or loading radius is specified when the cargo is created in the simulation and depends on the type of
|
||||||
|
-- cargo and the specified boarding radius.
|
||||||
|
--
|
||||||
|
-- ## Defending the Ship when enemies are nearby
|
||||||
|
-- This is not supported for naval cargo because most tanks don't float. Protect your transports...
|
||||||
|
--
|
||||||
|
-- ## Infantry or cargo **health**.
|
||||||
|
-- When cargo is unboarded from the Ship, the cargo is actually respawned into the battlefield.
|
||||||
|
-- As a result, the unboarding cargo is very _healthy_ every time it unboards.
|
||||||
|
-- This is due to the limitation of the DCS simulator, which is not able to specify the health of newly spawned units as a parameter.
|
||||||
|
-- However, cargo that was destroyed when unboarded and following the Ship won't be respawned again (this is likely not a thing for
|
||||||
|
-- naval cargo due to the lack of support for defending the Ship mentioned above). Destroyed is destroyed.
|
||||||
|
-- As a result, there is some additional strength that is gained when an unboarding action happens, but in terms of simulation balance
|
||||||
|
-- this has marginal impact on the overall battlefield simulation. Given the relatively short duration of DCS missions and the somewhat
|
||||||
|
-- lengthy naval transport times, most units entering the Ship as cargo will be freshly en route to an amphibious landing or transporting
|
||||||
|
-- between warehouses.
|
||||||
|
--
|
||||||
|
-- ## Control the Ships on the map.
|
||||||
|
--
|
||||||
|
-- Currently, naval transports can only be controlled via scripts due to their reliance upon predefined Shipping Lanes created in the Mission
|
||||||
|
-- Editor. An interesting future enhancement could leverage new pathfinding functionality for ships in the Ops module.
|
||||||
|
--
|
||||||
|
-- ## Cargo deployment.
|
||||||
|
--
|
||||||
|
-- Using the @{AI_CARGO_SHIP.Deploy}() method, you are able to direct the Ship towards a Deploy zone to unboard/unload the cargo at the
|
||||||
|
-- specified coordinate. The Ship will follow the Shipping Lane to ensure consistent cargo transportation within the simulation environment.
|
||||||
|
--
|
||||||
|
-- ## Cargo pickup.
|
||||||
|
--
|
||||||
|
-- Using the @{AI_CARGO_SHIP.Pickup}() method, you are able to direct the Ship towards a Pickup zone to board/load the cargo at the specified
|
||||||
|
-- coordinate. The Ship will follow the Shipping Lane to ensure consistent cargo transportation within the simulation environment.
|
||||||
|
--
|
||||||
|
--
|
||||||
|
-- @field #AI_CARGO_SHIP
|
||||||
|
AI_CARGO_SHIP = {
|
||||||
|
ClassName = "AI_CARGO_SHIP",
|
||||||
|
Coordinate = nil -- Core.Point#COORDINATE
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Creates a new AI_CARGO_SHIP object.
|
||||||
|
-- @param #AI_CARGO_SHIP self
|
||||||
|
-- @param Wrapper.Group#GROUP Ship The carrier Ship group
|
||||||
|
-- @param Core.Set#SET_CARGO CargoSet The set of cargo to be transported
|
||||||
|
-- @param #number CombatRadius Provide the combat radius to defend the carrier by unboarding the cargo when enemies are nearby. When CombatRadius is 0, no defense will occur.
|
||||||
|
-- @param #table ShippingLane Table containing list of Shipping Lanes to be used
|
||||||
|
-- @return #AI_CARGO_SHIP
|
||||||
|
function AI_CARGO_SHIP:New( Ship, CargoSet, CombatRadius, ShippingLane )
|
||||||
|
|
||||||
|
local self = BASE:Inherit( self, AI_CARGO:New( Ship, CargoSet ) ) -- #AI_CARGO_SHIP
|
||||||
|
|
||||||
|
self:AddTransition( "*", "Monitor", "*" )
|
||||||
|
self:AddTransition( "*", "Destroyed", "Destroyed" )
|
||||||
|
self:AddTransition( "*", "Home", "*" )
|
||||||
|
|
||||||
|
self:SetCombatRadius( 0 ) -- Don't want to deploy cargo in middle of water to defend Ship, so set CombatRadius to 0
|
||||||
|
self:SetShippingLane ( ShippingLane )
|
||||||
|
|
||||||
|
self:SetCarrier( Ship )
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the Carrier
|
||||||
|
-- @param #AI_CARGO_SHIP self
|
||||||
|
-- @param Wrapper.Group#GROUP CargoCarrier
|
||||||
|
-- @return #AI_CARGO_SHIP
|
||||||
|
function AI_CARGO_SHIP:SetCarrier( CargoCarrier )
|
||||||
|
self.CargoCarrier = CargoCarrier -- Wrapper.Group#GROUIP
|
||||||
|
self.CargoCarrier:SetState( self.CargoCarrier, "AI_CARGO_SHIP", self )
|
||||||
|
|
||||||
|
CargoCarrier:HandleEvent( EVENTS.Dead )
|
||||||
|
|
||||||
|
function CargoCarrier:OnEventDead( EventData )
|
||||||
|
self:F({"dead"})
|
||||||
|
local AICargoTroops = self:GetState( self, "AI_CARGO_SHIP" )
|
||||||
|
self:F({AICargoTroops=AICargoTroops})
|
||||||
|
if AICargoTroops then
|
||||||
|
self:F({})
|
||||||
|
if not AICargoTroops:Is( "Loaded" ) then
|
||||||
|
-- Better hope they can swim!
|
||||||
|
AICargoTroops:Destroyed()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self.Zone = ZONE_UNIT:New( self.CargoCarrier:GetName() .. "-Zone", self.CargoCarrier, self.CombatRadius )
|
||||||
|
self.Coalition = self.CargoCarrier:GetCoalition()
|
||||||
|
|
||||||
|
self:SetControllable( CargoCarrier )
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- FInd a free Carrier within a radius
|
||||||
|
-- @param #AI_CARGO_SHIP self
|
||||||
|
-- @param Core.Point#COORDINATE Coordinate
|
||||||
|
-- @param #number Radius
|
||||||
|
-- @return Wrapper.Group#GROUP NewCarrier
|
||||||
|
function AI_CARGO_SHIP:FindCarrier( Coordinate, Radius )
|
||||||
|
|
||||||
|
local CoordinateZone = ZONE_RADIUS:New( "Zone", Coordinate:GetVec2(), Radius )
|
||||||
|
CoordinateZone:Scan( { Object.Category.UNIT } )
|
||||||
|
for _, DCSUnit in pairs( CoordinateZone:GetScannedUnits() ) do
|
||||||
|
local NearUnit = UNIT:Find( DCSUnit )
|
||||||
|
self:F({NearUnit=NearUnit})
|
||||||
|
if not NearUnit:GetState( NearUnit, "AI_CARGO_SHIP" ) then
|
||||||
|
local Attributes = NearUnit:GetDesc()
|
||||||
|
self:F({Desc=Attributes})
|
||||||
|
if NearUnit:HasAttributes( "Trucks" ) then
|
||||||
|
return NearUnit:GetGroup()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function AI_CARGO_SHIP:SetShippingLane( ShippingLane )
|
||||||
|
self.ShippingLane = ShippingLane
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function AI_CARGO_SHIP:SetCombatRadius( CombatRadius )
|
||||||
|
self.CombatRadius = CombatRadius or 0
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Follow Infantry to the Carrier
|
||||||
|
-- @param #AI_CARGO_SHIP self
|
||||||
|
-- @param #AI_CARGO_SHIP Me
|
||||||
|
-- @param Wrapper.Unit#UNIT ShipUnit
|
||||||
|
-- @param Cargo.CargoGroup#CARGO_GROUP Cargo
|
||||||
|
-- @return #AI_CARGO_SHIP
|
||||||
|
function AI_CARGO_SHIP:FollowToCarrier( Me, ShipUnit, CargoGroup )
|
||||||
|
|
||||||
|
local InfantryGroup = CargoGroup:GetGroup()
|
||||||
|
|
||||||
|
self:F( { self=self:GetClassNameAndID(), InfantryGroup = InfantryGroup:GetName() } )
|
||||||
|
|
||||||
|
if ShipUnit:IsAlive() then
|
||||||
|
-- Check if the Cargo is near the CargoCarrier
|
||||||
|
if InfantryGroup:IsPartlyInZone( ZONE_UNIT:New( "Radius", ShipUnit, 1000 ) ) then
|
||||||
|
|
||||||
|
-- Cargo does not need to navigate to Carrier
|
||||||
|
Me:Guard()
|
||||||
|
else
|
||||||
|
|
||||||
|
self:F( { InfantryGroup = InfantryGroup:GetName() } )
|
||||||
|
if InfantryGroup:IsAlive() then
|
||||||
|
|
||||||
|
self:F( { InfantryGroup = InfantryGroup:GetName() } )
|
||||||
|
local Waypoints = {}
|
||||||
|
|
||||||
|
-- Calculate new route
|
||||||
|
local FromCoord = InfantryGroup:GetCoordinate()
|
||||||
|
local FromGround = FromCoord:WaypointGround( 10, "Diamond" )
|
||||||
|
self:F({FromGround=FromGround})
|
||||||
|
table.insert( Waypoints, FromGround )
|
||||||
|
|
||||||
|
local ToCoord = ShipUnit:GetCoordinate():GetRandomCoordinateInRadius( 10, 5 )
|
||||||
|
local ToGround = ToCoord:WaypointGround( 10, "Diamond" )
|
||||||
|
self:F({ToGround=ToGround})
|
||||||
|
table.insert( Waypoints, ToGround )
|
||||||
|
|
||||||
|
local TaskRoute = InfantryGroup:TaskFunction( "AI_CARGO_SHIP.FollowToCarrier", Me, ShipUnit, CargoGroup )
|
||||||
|
|
||||||
|
self:F({Waypoints=Waypoints})
|
||||||
|
local Waypoint = Waypoints[#Waypoints]
|
||||||
|
InfantryGroup:SetTaskWaypoint( Waypoint, TaskRoute ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone
|
||||||
|
|
||||||
|
InfantryGroup:Route( Waypoints, 1 ) -- Move after a random number of seconds to the Route. See Route method for details
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function AI_CARGO_SHIP:onafterMonitor( Ship, From, Event, To )
|
||||||
|
self:F( { Ship, From, Event, To, IsTransporting = self:IsTransporting() } )
|
||||||
|
|
||||||
|
if self.CombatRadius > 0 then
|
||||||
|
-- We really shouldn't find ourselves in here for Ships since the CombatRadius should always be 0.
|
||||||
|
-- This is to avoid Unloading the Ship in the middle of the sea.
|
||||||
|
if Ship and Ship:IsAlive() then
|
||||||
|
if self.CarrierCoordinate then
|
||||||
|
if self:IsTransporting() == true then
|
||||||
|
local Coordinate = Ship:GetCoordinate()
|
||||||
|
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
|
||||||
|
-- There are no enemies within combat radius. Reload the CargoCarrier.
|
||||||
|
self:Reload()
|
||||||
|
end
|
||||||
|
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, ShipUnit in pairs( self.Carrier_Cargo ) do
|
||||||
|
local Cargo = Cargo -- Cargo.Cargo#CARGO
|
||||||
|
local ShipUnit = ShipUnit -- Wrapper.Unit#UNIT
|
||||||
|
if Cargo:IsAlive() then
|
||||||
|
if not Cargo:IsNear( ShipUnit, 40 ) then
|
||||||
|
ShipUnit:RouteStop()
|
||||||
|
self.CarrierStopped = true
|
||||||
|
else
|
||||||
|
if self.CarrierStopped then
|
||||||
|
if Cargo:IsNear( ShipUnit, 25 ) then
|
||||||
|
ShipUnit:RouteResume()
|
||||||
|
self.CarrierStopped = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self.CarrierCoordinate = Ship:GetCoordinate()
|
||||||
|
end
|
||||||
|
self:__Monitor( -5 )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check if cargo ship is alive and trigger Load event
|
||||||
|
-- @param Wrapper.Group#Group Ship
|
||||||
|
-- @param #AI_CARGO_SHIP self
|
||||||
|
function AI_CARGO_SHIP._Pickup( Ship, self, Coordinate, Speed, PickupZone )
|
||||||
|
|
||||||
|
Ship:F( { "AI_CARGO_Ship._Pickup:", Ship:GetName() } )
|
||||||
|
|
||||||
|
if Ship:IsAlive() then
|
||||||
|
self:Load( PickupZone )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check if cargo ship is alive and trigger Unload event. Good time to remind people that Lua is case sensitive and Unload != UnLoad
|
||||||
|
-- @param Wrapper.Group#GROUP Ship
|
||||||
|
-- @param #AI_CARGO_SHIP self
|
||||||
|
function AI_CARGO_SHIP._Deploy( Ship, self, Coordinate, DeployZone )
|
||||||
|
Ship:F( { "AI_CARGO_Ship._Deploy:", Ship } )
|
||||||
|
|
||||||
|
if Ship:IsAlive() then
|
||||||
|
self:Unload( DeployZone )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- on after Pickup event.
|
||||||
|
-- @param AI_CARGO_SHIP Ship
|
||||||
|
-- @param From
|
||||||
|
-- @param Event
|
||||||
|
-- @param To
|
||||||
|
-- @param Core.Point#COORDINATE Coordinate of the pickup point
|
||||||
|
-- @param #number Speed Speed in km/h to sail to the pickup coordinate. Default is 50% of max speed for the unit
|
||||||
|
-- @param #number Height Altitude in meters to move to the pickup coordinate. This parameter is ignored for Ships
|
||||||
|
-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil if there was no PickupZoneSet provided
|
||||||
|
function AI_CARGO_SHIP:onafterPickup( Ship, From, Event, To, Coordinate, Speed, Height, PickupZone )
|
||||||
|
|
||||||
|
if Ship and Ship:IsAlive() then
|
||||||
|
AI_CARGO_SHIP._Pickup( Ship, self, Coordinate, Speed, PickupZone )
|
||||||
|
self:GetParent( self, AI_CARGO_SHIP ).onafterPickup( self, Ship, From, Event, To, Coordinate, Speed, Height, PickupZone )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- On after Deploy event.
|
||||||
|
-- @param #AI_CARGO_SHIP self
|
||||||
|
-- @param Wrapper.Group#GROUP SHIP
|
||||||
|
-- @param From
|
||||||
|
-- @param Event
|
||||||
|
-- @param To
|
||||||
|
-- @param Core.Point#COORDINATE Coordinate Coordinate of the deploy point
|
||||||
|
-- @param #number Speed Speed in km/h to sail to the deploy coordinate. Default is 50% of max speed for the unit
|
||||||
|
-- @param #number Height Altitude in meters to move to the deploy coordinate. This parameter is ignored for Ships
|
||||||
|
-- @param Core.Zone#ZONE DeployZone The zone where the cargo will be deployed.
|
||||||
|
function AI_CARGO_SHIP:onafterDeploy( Ship, From, Event, To, Coordinate, Speed, Height, DeployZone )
|
||||||
|
|
||||||
|
if Ship and Ship:IsAlive() then
|
||||||
|
|
||||||
|
Speed = Speed or Ship:GetSpeedMax()*0.8
|
||||||
|
local lane = self.ShippingLane
|
||||||
|
|
||||||
|
if lane then
|
||||||
|
local Waypoints = {}
|
||||||
|
|
||||||
|
for i=1, #lane do
|
||||||
|
local coord = lane[i]
|
||||||
|
local Waypoint = coord:WaypointGround(_speed)
|
||||||
|
table.insert(Waypoints, Waypoint)
|
||||||
|
end
|
||||||
|
|
||||||
|
local TaskFunction = Ship:TaskFunction( "AI_CARGO_SHIP._Deploy", self, Coordinate, DeployZone )
|
||||||
|
local Waypoint = Waypoints[#Waypoints]
|
||||||
|
Ship:SetTaskWaypoint( Waypoint, TaskFunction )
|
||||||
|
Ship:Route(Waypoints, 1)
|
||||||
|
self:GetParent( self, AI_CARGO_SHIP ).onafterDeploy( self, Ship, From, Event, To, Coordinate, Speed, Height, DeployZone )
|
||||||
|
else
|
||||||
|
self:E(self.lid.."ERROR: No shipping lane defined for Naval Transport!")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- On after Unload event.
|
||||||
|
-- @param #AI_CARGO_SHIP self
|
||||||
|
-- @param Wrapper.Group#GROUP Ship
|
||||||
|
-- @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.
|
||||||
|
function AI_CARGO_SHIP:onafterUnload( Ship, From, Event, To, DeployZone, Defend )
|
||||||
|
self:F( { Ship, From, Event, To, DeployZone, Defend = Defend } )
|
||||||
|
|
||||||
|
local UnboardInterval = 5
|
||||||
|
local UnboardDelay = 5
|
||||||
|
|
||||||
|
if Ship and Ship:IsAlive() then
|
||||||
|
for _, ShipUnit in pairs( Ship:GetUnits() ) do
|
||||||
|
local ShipUnit = ShipUnit -- Wrapper.Unit#UNIT
|
||||||
|
Ship:RouteStop()
|
||||||
|
for _, Cargo in pairs( ShipUnit:GetCargo() ) do
|
||||||
|
self:F( { Cargo = Cargo:GetName(), Isloaded = Cargo:IsLoaded() } )
|
||||||
|
if Cargo:IsLoaded() then
|
||||||
|
local unboardCoord = DeployZone:GetRandomPointVec2()
|
||||||
|
Cargo:__UnBoard( UnboardDelay, unboardCoord, 1000)
|
||||||
|
UnboardDelay = UnboardDelay + Cargo:GetCount() * UnboardInterval
|
||||||
|
self:__Unboard( UnboardDelay, Cargo, ShipUnit, DeployZone, Defend )
|
||||||
|
if not Defend == true then
|
||||||
|
Cargo:SetDeployed( true )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function AI_CARGO_SHIP:onafterHome( Ship, From, Event, To, Coordinate, Speed, Height, HomeZone )
|
||||||
|
if Ship and Ship:IsAlive() then
|
||||||
|
|
||||||
|
self.RouteHome = true
|
||||||
|
Speed = Speed or Ship:GetSpeedMax()*0.8
|
||||||
|
local lane = self.ShippingLane
|
||||||
|
|
||||||
|
if lane then
|
||||||
|
local Waypoints = {}
|
||||||
|
|
||||||
|
-- Need to find a more generalized way to do this instead of reversing the shipping lane.
|
||||||
|
-- This only works if the Source/Dest route waypoints are numbered 1..n and not n..1
|
||||||
|
for i=#lane, 1, -1 do
|
||||||
|
local coord = lane[i]
|
||||||
|
local Waypoint = coord:WaypointGround(_speed)
|
||||||
|
table.insert(Waypoints, Waypoint)
|
||||||
|
end
|
||||||
|
|
||||||
|
local Waypoint = Waypoints[#Waypoints]
|
||||||
|
Ship:Route(Waypoints, 1)
|
||||||
|
|
||||||
|
else
|
||||||
|
self:E(self.lid.."ERROR: No shipping lane defined for Naval Transport!")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -184,11 +184,13 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
|||||||
|
|
||||||
self.FollowGroupSet:ForEachGroup(
|
self.FollowGroupSet:ForEachGroup(
|
||||||
function( FollowGroup )
|
function( FollowGroup )
|
||||||
self:E("Following")
|
--self:E("Following")
|
||||||
FollowGroup:SetState( self, "Mode", self.__Enum.Mode.Formation )
|
FollowGroup:SetState( self, "Mode", self.__Enum.Mode.Formation )
|
||||||
end
|
end
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self:SetFlightModeFormation()
|
||||||
|
|
||||||
self:SetFlightRandomization( 2 )
|
self:SetFlightRandomization( 2 )
|
||||||
|
|
||||||
self:SetStartState( "None" )
|
self:SetStartState( "None" )
|
||||||
@@ -997,7 +999,7 @@ function AI_FORMATION:SetFlightModeMission( FollowGroup )
|
|||||||
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
|
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
|
||||||
FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Mission )
|
FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Mission )
|
||||||
else
|
else
|
||||||
self.EscortGroupSet:ForSomeGroupAlive(
|
self.FollowGroupSet:ForSomeGroupAlive(
|
||||||
--- @param Core.Group#GROUP EscortGroup
|
--- @param Core.Group#GROUP EscortGroup
|
||||||
function( FollowGroup )
|
function( FollowGroup )
|
||||||
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
|
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
|
||||||
@@ -1021,7 +1023,7 @@ function AI_FORMATION:SetFlightModeAttack( FollowGroup )
|
|||||||
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
|
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
|
||||||
FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Attack )
|
FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Attack )
|
||||||
else
|
else
|
||||||
self.EscortGroupSet:ForSomeGroupAlive(
|
self.FollowGroupSet:ForSomeGroupAlive(
|
||||||
--- @param Core.Group#GROUP EscortGroup
|
--- @param Core.Group#GROUP EscortGroup
|
||||||
function( FollowGroup )
|
function( FollowGroup )
|
||||||
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
|
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
|
||||||
@@ -1045,7 +1047,7 @@ function AI_FORMATION:SetFlightModeFormation( FollowGroup )
|
|||||||
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
|
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
|
||||||
FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Formation )
|
FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Formation )
|
||||||
else
|
else
|
||||||
self.EscortGroupSet:ForSomeGroupAlive(
|
self.FollowGroupSet:ForSomeGroupAlive(
|
||||||
--- @param Core.Group#GROUP EscortGroup
|
--- @param Core.Group#GROUP EscortGroup
|
||||||
function( FollowGroup )
|
function( FollowGroup )
|
||||||
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
|
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ do -- ACT_ROUTE
|
|||||||
self:F( { ZoneName = ZoneName } )
|
self:F( { ZoneName = ZoneName } )
|
||||||
local Zone = Zone -- Core.Zone#ZONE
|
local Zone = Zone -- Core.Zone#ZONE
|
||||||
local ZoneCoord = Zone:GetCoordinate()
|
local ZoneCoord = Zone:GetCoordinate()
|
||||||
local ZoneDistance = ZoneCoord:Get2DDistance( self.Coordinate )
|
local ZoneDistance = ZoneCoord:Get2DDistance( Coordinate )
|
||||||
self:F( { ShortestDistance, ShortestReferenceName } )
|
self:F( { ShortestDistance, ShortestReferenceName } )
|
||||||
if ShortestDistance == 0 or ZoneDistance < ShortestDistance then
|
if ShortestDistance == 0 or ZoneDistance < ShortestDistance then
|
||||||
ShortestDistance = ZoneDistance
|
ShortestDistance = ZoneDistance
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
--- **Core** -- Management of CARGO logistics, that can be transported from and to transportation carriers.
|
--- **Cargo** - Management of CARGO logistics, that can be transported from and to transportation carriers.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
@@ -1066,18 +1066,26 @@ do -- CARGO_REPRESENTABLE
|
|||||||
|
|
||||||
--- CARGO_REPRESENTABLE Constructor.
|
--- CARGO_REPRESENTABLE Constructor.
|
||||||
-- @param #CARGO_REPRESENTABLE self
|
-- @param #CARGO_REPRESENTABLE self
|
||||||
-- @param #string Type
|
-- @param Wrapper.Positionable#POSITIONABLE CargoObject The cargo object.
|
||||||
-- @param #string Name
|
-- @param #string Type Type name
|
||||||
-- @param #number LoadRadius (optional)
|
-- @param #string Name Name.
|
||||||
-- @param #number NearRadius (optional)
|
-- @param #number LoadRadius (optional) Radius in meters.
|
||||||
|
-- @param #number NearRadius (optional) Radius in meters when the cargo is loaded into the carrier.
|
||||||
-- @return #CARGO_REPRESENTABLE
|
-- @return #CARGO_REPRESENTABLE
|
||||||
function CARGO_REPRESENTABLE:New( CargoObject, Type, Name, LoadRadius, NearRadius )
|
function CARGO_REPRESENTABLE:New( CargoObject, Type, Name, LoadRadius, NearRadius )
|
||||||
|
|
||||||
|
-- Inherit CARGO.
|
||||||
local self = BASE:Inherit( self, CARGO:New( Type, Name, 0, LoadRadius, NearRadius ) ) -- #CARGO_REPRESENTABLE
|
local self = BASE:Inherit( self, CARGO:New( Type, Name, 0, LoadRadius, NearRadius ) ) -- #CARGO_REPRESENTABLE
|
||||||
self:F( { Type, Name, LoadRadius, NearRadius } )
|
self:F( { Type, Name, LoadRadius, NearRadius } )
|
||||||
|
|
||||||
local Desc = CargoObject:GetDesc()
|
-- Descriptors.
|
||||||
self:I( { Desc = Desc } )
|
local Desc=CargoObject:GetDesc()
|
||||||
|
self:T({Desc=Desc})
|
||||||
|
|
||||||
|
-- Weight.
|
||||||
local Weight = math.random( 80, 120 )
|
local Weight = math.random( 80, 120 )
|
||||||
|
|
||||||
|
-- Adjust weight..
|
||||||
if Desc then
|
if Desc then
|
||||||
if Desc.typeName == "2B11 mortar" then
|
if Desc.typeName == "2B11 mortar" then
|
||||||
Weight = 210
|
Weight = 210
|
||||||
@@ -1086,14 +1094,9 @@ do -- CARGO_REPRESENTABLE
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Set weight.
|
||||||
self:SetWeight( Weight )
|
self:SetWeight( Weight )
|
||||||
|
|
||||||
-- local Box = CargoUnit:GetBoundingBox()
|
|
||||||
-- local VolumeUnit = ( Box.max.x - Box.min.x ) * ( Box.max.y - Box.min.y ) * ( Box.max.z - Box.min.z )
|
|
||||||
-- self:I( { VolumeUnit = VolumeUnit, WeightUnit = WeightUnit } )
|
|
||||||
--self:SetVolume( VolumeUnit )
|
|
||||||
|
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
--- **Cargo** -- Management of grouped cargo logistics, which are based on a @{Wrapper.Group} object.
|
--- **Cargo** - Management of grouped cargo logistics, which are based on a @{Wrapper.Group} object.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
@@ -56,6 +56,8 @@ do -- CARGO_GROUP
|
|||||||
-- @param #number NearRadius (optional) Once the units are within this radius of the carrier, they are actually loaded, i.e. disappear from the scene.
|
-- @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.
|
-- @return #CARGO_GROUP Cargo group object.
|
||||||
function CARGO_GROUP:New( CargoGroup, Type, Name, LoadRadius, NearRadius )
|
function CARGO_GROUP:New( CargoGroup, Type, Name, LoadRadius, NearRadius )
|
||||||
|
|
||||||
|
-- Inherit CAROG_REPORTABLE
|
||||||
local self = BASE:Inherit( self, CARGO_REPORTABLE:New( Type, Name, 0, LoadRadius, NearRadius ) ) -- #CARGO_GROUP
|
local self = BASE:Inherit( self, CARGO_REPORTABLE:New( Type, Name, 0, LoadRadius, NearRadius ) ) -- #CARGO_GROUP
|
||||||
self:F( { Type, Name, LoadRadius } )
|
self:F( { Type, Name, LoadRadius } )
|
||||||
|
|
||||||
@@ -77,6 +79,9 @@ do -- CARGO_GROUP
|
|||||||
self.CargoName = Name
|
self.CargoName = Name
|
||||||
self.CargoTemplate = UTILS.DeepCopy( _DATABASE:GetGroupTemplate( GroupName ) )
|
self.CargoTemplate = UTILS.DeepCopy( _DATABASE:GetGroupTemplate( GroupName ) )
|
||||||
|
|
||||||
|
-- Deactivate late activation.
|
||||||
|
self.CargoTemplate.lateActivation=false
|
||||||
|
|
||||||
self.GroupTemplate = UTILS.DeepCopy( self.CargoTemplate )
|
self.GroupTemplate = UTILS.DeepCopy( self.CargoTemplate )
|
||||||
self.GroupTemplate.name = self.CargoName .. "#CARGO"
|
self.GroupTemplate.name = self.CargoName .. "#CARGO"
|
||||||
self.GroupTemplate.groupId = nil
|
self.GroupTemplate.groupId = nil
|
||||||
@@ -481,7 +486,7 @@ do -- CARGO_GROUP
|
|||||||
-- @param #string Event
|
-- @param #string Event
|
||||||
-- @param #string From
|
-- @param #string From
|
||||||
-- @param #string To
|
-- @param #string To
|
||||||
-- @param Core.Point#POINT_VEC2
|
-- @param Core.Point#POINT_VEC2 ToPointVec2
|
||||||
function CARGO_GROUP:onafterUnLoad( From, Event, To, ToPointVec2, ... )
|
function CARGO_GROUP:onafterUnLoad( From, Event, To, ToPointVec2, ... )
|
||||||
--self:F( { From, Event, To, ToPointVec2 } )
|
--self:F( { From, Event, To, ToPointVec2 } )
|
||||||
|
|
||||||
@@ -491,7 +496,10 @@ do -- CARGO_GROUP
|
|||||||
self.CargoSet:ForEach(
|
self.CargoSet:ForEach(
|
||||||
function( Cargo )
|
function( Cargo )
|
||||||
--Cargo:UnLoad( ToPointVec2 )
|
--Cargo:UnLoad( ToPointVec2 )
|
||||||
local RandomVec2=ToPointVec2:GetRandomPointVec2InRadius(20, 10)
|
local RandomVec2=nil
|
||||||
|
if ToPointVec2 then
|
||||||
|
RandomVec2=ToPointVec2:GetRandomPointVec2InRadius(20, 10)
|
||||||
|
end
|
||||||
Cargo:UnBoard( RandomVec2 )
|
Cargo:UnBoard( RandomVec2 )
|
||||||
end
|
end
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
--- **Cargo** -- Management of single cargo logistics, which are based on a @{Wrapper.Unit} object.
|
--- **Cargo** - Management of single cargo logistics, which are based on a @{Wrapper.Unit} object.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
@@ -46,14 +46,17 @@ do -- CARGO_UNIT
|
|||||||
-- @param #number NearRadius (optional)
|
-- @param #number NearRadius (optional)
|
||||||
-- @return #CARGO_UNIT
|
-- @return #CARGO_UNIT
|
||||||
function CARGO_UNIT:New( CargoUnit, Type, Name, LoadRadius, NearRadius )
|
function CARGO_UNIT:New( CargoUnit, Type, Name, LoadRadius, NearRadius )
|
||||||
local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoUnit, Type, Name, LoadRadius, NearRadius ) ) -- #CARGO_UNIT
|
|
||||||
self:I( { Type, Name, LoadRadius, NearRadius } )
|
|
||||||
|
|
||||||
self:T( CargoUnit )
|
-- Inherit CARGO_REPRESENTABLE.
|
||||||
|
local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoUnit, Type, Name, LoadRadius, NearRadius ) ) -- #CARGO_UNIT
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
self:T({Type=Type, Name=Name, LoadRadius=LoadRadius, NearRadius=NearRadius})
|
||||||
|
|
||||||
|
-- Set cargo object.
|
||||||
self.CargoObject = CargoUnit
|
self.CargoObject = CargoUnit
|
||||||
|
|
||||||
self:T( self.ClassName )
|
-- Set event prio.
|
||||||
|
|
||||||
self:SetEventPriority( 5 )
|
self:SetEventPriority( 5 )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
@@ -100,7 +103,12 @@ do -- CARGO_UNIT
|
|||||||
|
|
||||||
-- Respawn the group...
|
-- Respawn the group...
|
||||||
if self.CargoObject then
|
if self.CargoObject then
|
||||||
self.CargoObject:ReSpawnAt( FromPointVec2, CargoDeployHeading )
|
if CargoCarrier:IsShip() then
|
||||||
|
-- If CargoCarrier is a ship, we don't want to spawn the units in the water next to the boat. Use destination coord instead.
|
||||||
|
self.CargoObject:ReSpawnAt( ToPointVec2, CargoDeployHeading )
|
||||||
|
else
|
||||||
|
self.CargoObject:ReSpawnAt( FromPointVec2, CargoDeployHeading )
|
||||||
|
end
|
||||||
self:F( { "CargoUnits:", self.CargoObject:GetGroup():GetName() } )
|
self:F( { "CargoUnits:", self.CargoObject:GetGroup():GetName() } )
|
||||||
self.CargoCarrier = nil
|
self.CargoCarrier = nil
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
-- * The construction and inheritance of MOOSE classes.
|
-- * The construction and inheritance of MOOSE classes.
|
||||||
-- * The class naming and numbering system.
|
-- * The class naming and numbering system.
|
||||||
-- * The class hierarchy search system.
|
-- * The class hierarchy search system.
|
||||||
-- * The tracing of information or objects during mission execution for debuggin purposes.
|
-- * The tracing of information or objects during mission execution for debugging purposes.
|
||||||
-- * The subscription to DCS events for event handling in MOOSE objects.
|
-- * The subscription to DCS events for event handling in MOOSE objects.
|
||||||
-- * Object inspection.
|
-- * Object inspection.
|
||||||
--
|
--
|
||||||
@@ -49,7 +49,8 @@ local _ClassID = 0
|
|||||||
-- # 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.
|
-- 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.
|
-- These trace methods are inherited by each MOOSE class inheriting BASE, thus all objects created from
|
||||||
|
-- a class derived from BASE can use the tracing methods to trace its execution.
|
||||||
--
|
--
|
||||||
-- Any type of information can be passed to these tracing methods. See the following examples:
|
-- Any type of information can be passed to these tracing methods. See the following examples:
|
||||||
--
|
--
|
||||||
@@ -111,7 +112,6 @@ local _ClassID = 0
|
|||||||
--
|
--
|
||||||
-- The method @{#BASE.IsTrace}() will validate if tracing is activated or not.
|
-- The method @{#BASE.IsTrace}() will validate if tracing is activated or not.
|
||||||
--
|
--
|
||||||
--
|
|
||||||
-- # 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,
|
-- The BASE class provides methods to catch DCS Events. These are events that are triggered from within the DCS simulator,
|
||||||
@@ -157,8 +157,6 @@ local _ClassID = 0
|
|||||||
-- self:SmokeBlue()
|
-- self:SmokeBlue()
|
||||||
-- end
|
-- end
|
||||||
--
|
--
|
||||||
--
|
|
||||||
--
|
|
||||||
-- See the @{Event} module for more information about event handling.
|
-- See the @{Event} module for more information about event handling.
|
||||||
--
|
--
|
||||||
-- # 4. Class identification methods.
|
-- # 4. Class identification methods.
|
||||||
@@ -203,13 +201,12 @@ BASE = {
|
|||||||
Scheduler = nil,
|
Scheduler = nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
--- @field #BASE.__
|
--- @field #BASE.__
|
||||||
BASE.__ = {}
|
BASE.__ = {}
|
||||||
|
|
||||||
--- @field #BASE._
|
--- @field #BASE._
|
||||||
BASE._ = {
|
BASE._ = {
|
||||||
Schedules = {} --- Contains the Schedulers Active
|
Schedules = {}, --- Contains the Schedulers Active
|
||||||
}
|
}
|
||||||
|
|
||||||
--- The Formation Class
|
--- The Formation Class
|
||||||
@@ -217,11 +214,9 @@ BASE._ = {
|
|||||||
-- @field Cone A cone formation.
|
-- @field Cone A cone formation.
|
||||||
FORMATION = {
|
FORMATION = {
|
||||||
Cone = "Cone",
|
Cone = "Cone",
|
||||||
Vee = "Vee"
|
Vee = "Vee",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- BASE constructor.
|
--- BASE constructor.
|
||||||
--
|
--
|
||||||
-- This is an example how to use the BASE:New() constructor in a new class definition when inheriting from BASE.
|
-- This is an example how to use the BASE:New() constructor in a new class definition when inheriting from BASE.
|
||||||
@@ -236,16 +231,16 @@ FORMATION = {
|
|||||||
function BASE:New()
|
function BASE:New()
|
||||||
local self = routines.utils.deepCopy( self ) -- Create a new self instance
|
local self = routines.utils.deepCopy( self ) -- Create a new self instance
|
||||||
|
|
||||||
_ClassID = _ClassID + 1
|
_ClassID = _ClassID + 1
|
||||||
self.ClassID = _ClassID
|
self.ClassID = _ClassID
|
||||||
|
|
||||||
-- This is for "private" methods...
|
-- This is for "private" methods...
|
||||||
-- When a __ is passed to a method as "self", the __index will search for the method on the public method list too!
|
-- When a __ is passed to a method as "self", the __index will search for the method on the public method list too!
|
||||||
-- if rawget( self, "__" ) then
|
-- if rawget( self, "__" ) then
|
||||||
--setmetatable( self, { __index = self.__ } )
|
-- setmetatable( self, { __index = self.__ } )
|
||||||
-- end
|
-- end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- This is the worker method to inherit from a parent class.
|
--- This is the worker method to inherit from a parent class.
|
||||||
@@ -256,26 +251,25 @@ end
|
|||||||
function BASE:Inherit( Child, Parent )
|
function BASE:Inherit( Child, Parent )
|
||||||
|
|
||||||
-- Create child.
|
-- Create child.
|
||||||
local Child = routines.utils.deepCopy( Child )
|
local Child = routines.utils.deepCopy( Child )
|
||||||
|
|
||||||
if Child ~= nil then
|
if Child ~= nil then
|
||||||
|
|
||||||
-- This is for "private" methods...
|
-- This is for "private" methods...
|
||||||
-- When a __ is passed to a method as "self", the __index will search for the method on the public method list of the same object too!
|
-- When a __ is passed to a method as "self", the __index will search for the method on the public method list of the same object too!
|
||||||
if rawget( Child, "__" ) then
|
if rawget( Child, "__" ) then
|
||||||
setmetatable( Child, { __index = Child.__ } )
|
setmetatable( Child, { __index = Child.__ } )
|
||||||
setmetatable( Child.__, { __index = Parent } )
|
setmetatable( Child.__, { __index = Parent } )
|
||||||
else
|
else
|
||||||
setmetatable( Child, { __index = Parent } )
|
setmetatable( Child, { __index = Parent } )
|
||||||
end
|
end
|
||||||
|
|
||||||
--Child:_SetDestructor()
|
-- Child:_SetDestructor()
|
||||||
end
|
end
|
||||||
|
|
||||||
return Child
|
return Child
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
local function getParent( Child )
|
local function getParent( Child )
|
||||||
local Parent = nil
|
local Parent = nil
|
||||||
|
|
||||||
@@ -291,7 +285,6 @@ local function getParent( Child )
|
|||||||
return Parent
|
return Parent
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- This is the worker method to retrieve the Parent class.
|
--- This is the worker method to retrieve the Parent class.
|
||||||
-- Note that the Parent class must be passed to call the parent class method.
|
-- Note that the Parent class must be passed to call the parent class method.
|
||||||
--
|
--
|
||||||
@@ -304,19 +297,18 @@ end
|
|||||||
-- @return #BASE
|
-- @return #BASE
|
||||||
function BASE:GetParent( Child, FromClass )
|
function BASE:GetParent( Child, FromClass )
|
||||||
|
|
||||||
|
|
||||||
local Parent
|
local Parent
|
||||||
-- BASE class has no parent
|
-- BASE class has no parent
|
||||||
if Child.ClassName == 'BASE' then
|
if Child.ClassName == 'BASE' then
|
||||||
Parent = nil
|
Parent = nil
|
||||||
else
|
else
|
||||||
|
|
||||||
--self:E({FromClass = FromClass})
|
-- self:E({FromClass = FromClass})
|
||||||
--self:E({Child = Child.ClassName})
|
-- self:E({Child = Child.ClassName})
|
||||||
if FromClass then
|
if FromClass then
|
||||||
while( Child.ClassName ~= "BASE" and Child.ClassName ~= FromClass.ClassName ) do
|
while (Child.ClassName ~= "BASE" and Child.ClassName ~= FromClass.ClassName) do
|
||||||
Child = getParent( Child )
|
Child = getParent( Child )
|
||||||
--self:E({Child.ClassName})
|
-- self:E({Child.ClassName})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if Child.ClassName == 'BASE' then
|
if Child.ClassName == 'BASE' then
|
||||||
@@ -325,7 +317,7 @@ function BASE:GetParent( Child, FromClass )
|
|||||||
Parent = getParent( Child )
|
Parent = getParent( Child )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
--self:E({Parent.ClassName})
|
-- self:E({Parent.ClassName})
|
||||||
return Parent
|
return Parent
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -354,11 +346,11 @@ function BASE:IsInstanceOf( ClassName )
|
|||||||
-- Get the name of the Moose class as a string
|
-- Get the name of the Moose class as a string
|
||||||
ClassName = ClassName.ClassName
|
ClassName = ClassName.ClassName
|
||||||
|
|
||||||
-- className is neither a string nor a Moose class, throw an error
|
-- className is neither a string nor a Moose class, throw an error
|
||||||
else
|
else
|
||||||
|
|
||||||
-- I'm not sure if this should take advantage of MOOSE logging function, or throw an error for pcall
|
-- I'm not sure if this should take advantage of MOOSE logging function, or throw an error for pcall
|
||||||
local err_str = 'className parameter should be a string; parameter received: '..type( ClassName )
|
local err_str = 'className parameter should be a string; parameter received: ' .. type( ClassName )
|
||||||
self:E( err_str )
|
self:E( err_str )
|
||||||
-- error( err_str )
|
-- error( err_str )
|
||||||
return false
|
return false
|
||||||
@@ -372,7 +364,7 @@ function BASE:IsInstanceOf( ClassName )
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
local Parent = getParent(self)
|
local Parent = getParent( self )
|
||||||
|
|
||||||
while Parent do
|
while Parent do
|
||||||
|
|
||||||
@@ -419,7 +411,6 @@ do -- Event Handling
|
|||||||
return _EVENTDISPATCHER
|
return _EVENTDISPATCHER
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Get the Class @{Event} processing Priority.
|
--- Get the Class @{Event} processing Priority.
|
||||||
-- The Event processing Priority is a number from 1 to 10,
|
-- The Event processing Priority is a number from 1 to 10,
|
||||||
-- reflecting the order of the classes subscribed to the Event to be processed.
|
-- reflecting the order of the classes subscribed to the Event to be processed.
|
||||||
@@ -451,58 +442,58 @@ do -- Event Handling
|
|||||||
|
|
||||||
--- Subscribe to a DCS Event.
|
--- Subscribe to a DCS Event.
|
||||||
-- @param #BASE self
|
-- @param #BASE self
|
||||||
-- @param Core.Event#EVENTS Event
|
-- @param Core.Event#EVENTS EventID Event ID.
|
||||||
-- @param #function EventFunction (optional) The function to be called when the event occurs for the unit.
|
-- @param #function EventFunction (optional) The function to be called when the event occurs for the unit.
|
||||||
-- @return #BASE
|
-- @return #BASE
|
||||||
function BASE:HandleEvent( Event, EventFunction )
|
function BASE:HandleEvent( EventID, EventFunction )
|
||||||
|
|
||||||
self:EventDispatcher():OnEventGeneric( EventFunction, self, Event )
|
self:EventDispatcher():OnEventGeneric( EventFunction, self, EventID )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- UnSubscribe to a DCS event.
|
--- UnSubscribe to a DCS event.
|
||||||
-- @param #BASE self
|
-- @param #BASE self
|
||||||
-- @param Core.Event#EVENTS Event
|
-- @param Core.Event#EVENTS EventID Event ID.
|
||||||
-- @return #BASE
|
-- @return #BASE
|
||||||
function BASE:UnHandleEvent( Event )
|
function BASE:UnHandleEvent( EventID )
|
||||||
|
|
||||||
self:EventDispatcher():RemoveEvent( self, Event )
|
self:EventDispatcher():RemoveEvent( self, EventID )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Event handling function prototypes
|
-- Event handling function prototypes
|
||||||
|
|
||||||
--- Occurs whenever any unit in a mission fires a weapon. But not any machine gun or autocannon based weapon, those are handled by EVENT.ShootingStart.
|
--- Occurs whenever any unit in a mission fires a weapon. But not any machine gun or auto cannon based weapon, those are handled by EVENT.ShootingStart.
|
||||||
-- @function [parent=#BASE] OnEventShot
|
-- @function [parent=#BASE] OnEventShot
|
||||||
-- @param #BASE self
|
-- @param #BASE self
|
||||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||||
|
|
||||||
--- Occurs whenever an object is hit by a weapon.
|
--- Occurs whenever an object is hit by a weapon.
|
||||||
-- initiator : The unit object the fired the weapon
|
-- initiator : The unit object the fired the weapon.
|
||||||
-- weapon: Weapon object that hit the target
|
-- weapon: Weapon object that hit the target.
|
||||||
-- target: The Object that was hit.
|
-- target: The Object that was hit.
|
||||||
-- @function [parent=#BASE] OnEventHit
|
-- @function [parent=#BASE] OnEventHit
|
||||||
-- @param #BASE self
|
-- @param #BASE self
|
||||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||||
|
|
||||||
--- Occurs when an aircraft takes off from an airbase, farp, or ship.
|
--- Occurs when an aircraft takes off from an airbase, farp, or ship.
|
||||||
-- initiator : The unit that tookoff
|
-- initiator : The unit that took off.
|
||||||
-- place: Object from where the AI took-off from. Can be an Airbase Object, FARP, or Ships
|
-- place: Object from where the AI took-off from. Can be an Airbase Object, FARP, or Ships.
|
||||||
-- @function [parent=#BASE] OnEventTakeoff
|
-- @function [parent=#BASE] OnEventTakeoff
|
||||||
-- @param #BASE self
|
-- @param #BASE self
|
||||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||||
|
|
||||||
--- Occurs when an aircraft lands at an airbase, farp or ship
|
--- Occurs when an aircraft lands at an airbase, farp or ship
|
||||||
-- initiator : The unit that has landed
|
-- initiator : The unit that has landed.
|
||||||
-- place: Object that the unit landed on. Can be an Airbase Object, FARP, or Ships
|
-- place: Object that the unit landed on. Can be an Airbase Object, FARP, or Ships.
|
||||||
-- @function [parent=#BASE] OnEventLand
|
-- @function [parent=#BASE] OnEventLand
|
||||||
-- @param #BASE self
|
-- @param #BASE self
|
||||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||||
|
|
||||||
--- Occurs when any aircraft crashes into the ground and is completely destroyed.
|
--- Occurs when any aircraft crashes into the ground and is completely destroyed.
|
||||||
-- initiator : The unit that has crashed
|
-- initiator : The unit that has crashed.
|
||||||
-- @function [parent=#BASE] OnEventCrash
|
-- @function [parent=#BASE] OnEventCrash
|
||||||
-- @param #BASE self
|
-- @param #BASE self
|
||||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||||
@@ -538,18 +529,18 @@ do -- Event Handling
|
|||||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||||
|
|
||||||
--- Occurs when a ground unit captures either an airbase or a farp.
|
--- Occurs when a ground unit captures either an airbase or a farp.
|
||||||
-- initiator : The unit that captured the base
|
-- initiator : The unit that captured the base.
|
||||||
-- place: The airbase that was captured, can be a FARP or Airbase. When calling place:getCoalition() the faction will already be the new owning faction.
|
-- place: The airbase that was captured, can be a FARP or Airbase. When calling place:getCoalition() the faction will already be the new owning faction.
|
||||||
-- @function [parent=#BASE] OnEventBaseCaptured
|
-- @function [parent=#BASE] OnEventBaseCaptured
|
||||||
-- @param #BASE self
|
-- @param #BASE self
|
||||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||||
|
|
||||||
--- Occurs when a mission starts
|
--- Occurs when a mission starts.
|
||||||
-- @function [parent=#BASE] OnEventMissionStart
|
-- @function [parent=#BASE] OnEventMissionStart
|
||||||
-- @param #BASE self
|
-- @param #BASE self
|
||||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||||
|
|
||||||
--- Occurs when a mission ends
|
--- Occurs when a mission ends.
|
||||||
-- @function [parent=#BASE] OnEventMissionEnd
|
-- @function [parent=#BASE] OnEventMissionEnd
|
||||||
-- @param #BASE self
|
-- @param #BASE self
|
||||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||||
@@ -561,25 +552,25 @@ do -- Event Handling
|
|||||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||||
|
|
||||||
--- Occurs when any object is spawned into the mission.
|
--- Occurs when any object is spawned into the mission.
|
||||||
-- initiator : The unit that was spawned
|
-- initiator : The unit that was spawned.
|
||||||
-- @function [parent=#BASE] OnEventBirth
|
-- @function [parent=#BASE] OnEventBirth
|
||||||
-- @param #BASE self
|
-- @param #BASE self
|
||||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||||
|
|
||||||
--- Occurs when any system fails on a human controlled aircraft.
|
--- Occurs when any system fails on a human controlled aircraft.
|
||||||
-- initiator : The unit that had the failure
|
-- initiator : The unit that had the failure.
|
||||||
-- @function [parent=#BASE] OnEventHumanFailure
|
-- @function [parent=#BASE] OnEventHumanFailure
|
||||||
-- @param #BASE self
|
-- @param #BASE self
|
||||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||||
|
|
||||||
--- Occurs when any aircraft starts its engines.
|
--- Occurs when any aircraft starts its engines.
|
||||||
-- initiator : The unit that is starting its engines.
|
-- initiator : The unit that is starting its engines..
|
||||||
-- @function [parent=#BASE] OnEventEngineStartup
|
-- @function [parent=#BASE] OnEventEngineStartup
|
||||||
-- @param #BASE self
|
-- @param #BASE self
|
||||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||||
|
|
||||||
--- Occurs when any aircraft shuts down its engines.
|
--- Occurs when any aircraft shuts down its engines.
|
||||||
-- initiator : The unit that is stopping its engines.
|
-- initiator : The unit that is stopping its engines..
|
||||||
-- @function [parent=#BASE] OnEventEngineShutdown
|
-- @function [parent=#BASE] OnEventEngineShutdown
|
||||||
-- @param #BASE self
|
-- @param #BASE self
|
||||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||||
@@ -596,7 +587,7 @@ do -- Event Handling
|
|||||||
-- @param #BASE self
|
-- @param #BASE self
|
||||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
-- @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.
|
--- Occurs when any unit begins firing a weapon that has a high rate of fire. Most common with aircraft cannons (GAU-8), auto cannons, and machine guns.
|
||||||
-- initiator : The unit that is doing the shooting.
|
-- initiator : The unit that is doing the shooting.
|
||||||
-- target: The unit that is being targeted.
|
-- target: The unit that is being targeted.
|
||||||
-- @function [parent=#BASE] OnEventShootingStart
|
-- @function [parent=#BASE] OnEventShootingStart
|
||||||
@@ -627,7 +618,6 @@ do -- Event Handling
|
|||||||
-- @param #BASE self
|
-- @param #BASE self
|
||||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
-- @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.
|
--- 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.
|
-- * initiator: The unit that had the failure.
|
||||||
@@ -644,7 +634,7 @@ do -- Event Handling
|
|||||||
|
|
||||||
--- 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.
|
--- 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
|
-- * initiator: The unit that killed the target.
|
||||||
-- * target: Target Object
|
-- * target: Target Object
|
||||||
-- * weapon: Weapon Object
|
-- * weapon: Weapon Object
|
||||||
--
|
--
|
||||||
@@ -676,8 +666,44 @@ do -- Event Handling
|
|||||||
-- @param #BASE self
|
-- @param #BASE self
|
||||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||||
|
|
||||||
end
|
--- Paratrooper landing.
|
||||||
|
-- @function [parent=#BASE] OnEventParatrooperLanding
|
||||||
|
-- @param #BASE self
|
||||||
|
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||||
|
|
||||||
|
--- Discard chair after ejection.
|
||||||
|
-- @function [parent=#BASE] OnEventDiscardChairAfterEjection
|
||||||
|
-- @param #BASE self
|
||||||
|
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||||
|
|
||||||
|
--- Weapon add. Fires when entering a mission per pylon with the name of the weapon (double pylons not counted, infinite wep reload not counted.
|
||||||
|
-- @function [parent=#BASE] OnEventParatrooperLanding
|
||||||
|
-- @param #BASE self
|
||||||
|
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||||
|
|
||||||
|
--- Trigger zone.
|
||||||
|
-- @function [parent=#BASE] OnEventTriggerZone
|
||||||
|
-- @param #BASE self
|
||||||
|
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||||
|
|
||||||
|
--- Landing quality mark.
|
||||||
|
-- @function [parent=#BASE] OnEventLandingQualityMark
|
||||||
|
-- @param #BASE self
|
||||||
|
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||||
|
|
||||||
|
--- BDA.
|
||||||
|
-- @function [parent=#BASE] OnEventBDA
|
||||||
|
-- @param #BASE self
|
||||||
|
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||||
|
|
||||||
|
--- Occurs when a player enters a slot and takes control of an aircraft.
|
||||||
|
-- **NOTE**: This is a workaround of a long standing DCS bug with the PLAYER_ENTER_UNIT event.
|
||||||
|
-- initiator : The unit that is being taken control of.
|
||||||
|
-- @function [parent=#BASE] OnEventPlayerEnterAircraft
|
||||||
|
-- @param #BASE self
|
||||||
|
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
--- Creation of a Birth Event.
|
--- Creation of a Birth Event.
|
||||||
-- @param #BASE self
|
-- @param #BASE self
|
||||||
@@ -687,18 +713,18 @@ end
|
|||||||
-- @param place
|
-- @param place
|
||||||
-- @param subplace
|
-- @param subplace
|
||||||
function BASE:CreateEventBirth( EventTime, Initiator, IniUnitName, place, subplace )
|
function BASE:CreateEventBirth( EventTime, Initiator, IniUnitName, place, subplace )
|
||||||
self:F( { EventTime, Initiator, IniUnitName, place, subplace } )
|
self:F( { EventTime, Initiator, IniUnitName, place, subplace } )
|
||||||
|
|
||||||
local Event = {
|
local Event = {
|
||||||
id = world.event.S_EVENT_BIRTH,
|
id = world.event.S_EVENT_BIRTH,
|
||||||
time = EventTime,
|
time = EventTime,
|
||||||
initiator = Initiator,
|
initiator = Initiator,
|
||||||
IniUnitName = IniUnitName,
|
IniUnitName = IniUnitName,
|
||||||
place = place,
|
place = place,
|
||||||
subplace = subplace
|
subplace = subplace,
|
||||||
}
|
}
|
||||||
|
|
||||||
world.onEvent( Event )
|
world.onEvent( Event )
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Creation of a Crash Event.
|
--- Creation of a Crash Event.
|
||||||
@@ -706,15 +732,15 @@ end
|
|||||||
-- @param DCS#Time EventTime The time stamp of the event.
|
-- @param DCS#Time EventTime The time stamp of the event.
|
||||||
-- @param DCS#Object Initiator The initiating object of the event.
|
-- @param DCS#Object Initiator The initiating object of the event.
|
||||||
function BASE:CreateEventCrash( EventTime, Initiator )
|
function BASE:CreateEventCrash( EventTime, Initiator )
|
||||||
self:F( { EventTime, Initiator } )
|
self:F( { EventTime, Initiator } )
|
||||||
|
|
||||||
local Event = {
|
local Event = {
|
||||||
id = world.event.S_EVENT_CRASH,
|
id = world.event.S_EVENT_CRASH,
|
||||||
time = EventTime,
|
time = EventTime,
|
||||||
initiator = Initiator,
|
initiator = Initiator,
|
||||||
}
|
}
|
||||||
|
|
||||||
world.onEvent( Event )
|
world.onEvent( Event )
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Creation of a Dead Event.
|
--- Creation of a Dead Event.
|
||||||
@@ -728,7 +754,7 @@ function BASE:CreateEventDead( EventTime, Initiator )
|
|||||||
id = world.event.S_EVENT_DEAD,
|
id = world.event.S_EVENT_DEAD,
|
||||||
time = EventTime,
|
time = EventTime,
|
||||||
initiator = Initiator,
|
initiator = Initiator,
|
||||||
}
|
}
|
||||||
|
|
||||||
world.onEvent( Event )
|
world.onEvent( Event )
|
||||||
end
|
end
|
||||||
@@ -744,7 +770,7 @@ function BASE:CreateEventRemoveUnit( EventTime, Initiator )
|
|||||||
id = EVENTS.RemoveUnit,
|
id = EVENTS.RemoveUnit,
|
||||||
time = EventTime,
|
time = EventTime,
|
||||||
initiator = Initiator,
|
initiator = Initiator,
|
||||||
}
|
}
|
||||||
|
|
||||||
world.onEvent( Event )
|
world.onEvent( Event )
|
||||||
end
|
end
|
||||||
@@ -760,7 +786,22 @@ function BASE:CreateEventTakeoff( EventTime, Initiator )
|
|||||||
id = world.event.S_EVENT_TAKEOFF,
|
id = world.event.S_EVENT_TAKEOFF,
|
||||||
time = EventTime,
|
time = EventTime,
|
||||||
initiator = Initiator,
|
initiator = Initiator,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
world.onEvent( Event )
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Creation of a `S_EVENT_PLAYER_ENTER_AIRCRAFT` event.
|
||||||
|
-- @param #BASE self
|
||||||
|
-- @param Wrapper.Unit#UNIT PlayerUnit The aircraft unit the player entered.
|
||||||
|
function BASE:CreateEventPlayerEnterAircraft( PlayerUnit )
|
||||||
|
self:F( { PlayerUnit } )
|
||||||
|
|
||||||
|
local Event = {
|
||||||
|
id = EVENTS.PlayerEnterAircraft,
|
||||||
|
time = timer.getTime(),
|
||||||
|
initiator = PlayerUnit:GetDCSObject(),
|
||||||
|
}
|
||||||
|
|
||||||
world.onEvent( Event )
|
world.onEvent( Event )
|
||||||
end
|
end
|
||||||
@@ -769,30 +810,32 @@ end
|
|||||||
--- The main event handling function... This function captures all events generated for the class.
|
--- The main event handling function... This function captures all events generated for the class.
|
||||||
-- @param #BASE self
|
-- @param #BASE self
|
||||||
-- @param DCS#Event event
|
-- @param DCS#Event event
|
||||||
function BASE:onEvent(event)
|
function BASE:onEvent( event )
|
||||||
--self:F( { BaseEventCodes[event.id], event } )
|
|
||||||
|
|
||||||
if self then
|
if self then
|
||||||
for EventID, EventObject in pairs( self.Events ) do
|
|
||||||
if EventObject.EventEnabled then
|
for EventID, EventObject in pairs( self.Events ) do
|
||||||
--env.info( 'onEvent Table EventObject.Self = ' .. tostring(EventObject.Self) )
|
if EventObject.EventEnabled then
|
||||||
--env.info( 'onEvent event.id = ' .. tostring(event.id) )
|
|
||||||
--env.info( 'onEvent EventObject.Event = ' .. tostring(EventObject.Event) )
|
if event.id == EventObject.Event then
|
||||||
if event.id == EventObject.Event then
|
|
||||||
if self == EventObject.Self then
|
if self == EventObject.Self then
|
||||||
if event.initiator and event.initiator:isExist() then
|
|
||||||
event.IniUnitName = event.initiator:getName()
|
if event.initiator and event.initiator:isExist() then
|
||||||
end
|
event.IniUnitName = event.initiator:getName()
|
||||||
if event.target and event.target:isExist() then
|
end
|
||||||
event.TgtUnitName = event.target:getName()
|
|
||||||
end
|
if event.target and event.target:isExist() then
|
||||||
--self:T( { BaseEventCodes[event.id], event } )
|
event.TgtUnitName = event.target:getName()
|
||||||
--EventObject.EventFunction( self, event )
|
end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
do -- Scheduling
|
do -- Scheduling
|
||||||
@@ -810,23 +853,23 @@ do -- Scheduling
|
|||||||
local ObjectName = "-"
|
local ObjectName = "-"
|
||||||
ObjectName = self.ClassName .. self.ClassID
|
ObjectName = self.ClassName .. self.ClassID
|
||||||
|
|
||||||
self:F3( { "ScheduleOnce: ", ObjectName, Start } )
|
self:F3( { "ScheduleOnce: ", ObjectName, Start } )
|
||||||
|
|
||||||
if not self.Scheduler then
|
if not self.Scheduler then
|
||||||
self.Scheduler = SCHEDULER:New( self )
|
self.Scheduler = SCHEDULER:New( self )
|
||||||
end
|
end
|
||||||
|
|
||||||
local ScheduleID = _SCHEDULEDISPATCHER:AddSchedule(
|
local ScheduleID = _SCHEDULEDISPATCHER:AddSchedule(
|
||||||
self,
|
self,
|
||||||
SchedulerFunction,
|
SchedulerFunction,
|
||||||
{ ... },
|
{ ... },
|
||||||
Start,
|
Start,
|
||||||
nil,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
nil
|
nil
|
||||||
)
|
)
|
||||||
|
|
||||||
self._.Schedules[#self._.Schedules+1] = ScheduleID
|
self._.Schedules[#self._.Schedules + 1] = ScheduleID
|
||||||
|
|
||||||
return self._.Schedules[#self._.Schedules]
|
return self._.Schedules[#self._.Schedules]
|
||||||
end
|
end
|
||||||
@@ -854,17 +897,17 @@ do -- Scheduling
|
|||||||
end
|
end
|
||||||
|
|
||||||
local ScheduleID = self.Scheduler:Schedule(
|
local ScheduleID = self.Scheduler:Schedule(
|
||||||
self,
|
self,
|
||||||
SchedulerFunction,
|
SchedulerFunction,
|
||||||
{ ... },
|
{ ... },
|
||||||
Start,
|
Start,
|
||||||
Repeat,
|
Repeat,
|
||||||
RandomizeFactor,
|
RandomizeFactor,
|
||||||
Stop,
|
Stop,
|
||||||
4
|
4
|
||||||
)
|
)
|
||||||
|
|
||||||
self._.Schedules[#self._.Schedules+1] = ScheduleID
|
self._.Schedules[#self._.Schedules + 1] = ScheduleID
|
||||||
|
|
||||||
return self._.Schedules[#self._.Schedules]
|
return self._.Schedules[#self._.Schedules]
|
||||||
end
|
end
|
||||||
@@ -883,9 +926,8 @@ do -- Scheduling
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Set a state or property of the Object given a Key and a Value.
|
--- Set a state or property of the Object given a Key and a Value.
|
||||||
-- Note that if the Object is destroyed, nillified or garbage collected, then the Values and Keys will also be gone.
|
-- Note that if the Object is destroyed, set to nil, or garbage collected, then the Values and Keys will also be gone.
|
||||||
-- @param #BASE self
|
-- @param #BASE self
|
||||||
-- @param Object The object that will hold the Value set by the Key.
|
-- @param Object The object that will hold the Value set by the Key.
|
||||||
-- @param Key The key that is used as a reference of the value. Note that the key can be a #string, but it can also be any other type!
|
-- @param Key The key that is used as a reference of the value. Note that the key can be a #string, but it can also be any other type!
|
||||||
@@ -901,9 +943,8 @@ function BASE:SetState( Object, Key, Value )
|
|||||||
return self.States[ClassNameAndID][Key]
|
return self.States[ClassNameAndID][Key]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Get a Value given a Key from the Object.
|
--- Get a Value given a Key from the Object.
|
||||||
-- Note that if the Object is destroyed, nillified or garbage collected, then the Values and Keys will also be gone.
|
-- Note that if the Object is destroyed, set to nil, or garbage collected, then the Values and Keys will also be gone.
|
||||||
-- @param #BASE self
|
-- @param #BASE self
|
||||||
-- @param Object The object that holds the Value set by the Key.
|
-- @param Object The object that holds the Value set by the Key.
|
||||||
-- @param Key The key that is used to retrieve the value. Note that the key can be a #string, but it can also be any other type!
|
-- @param Key The key that is used to retrieve the value. Note that the key can be a #string, but it can also be any other type!
|
||||||
@@ -955,8 +996,6 @@ function BASE:TraceOff()
|
|||||||
self:TraceOnOff( false )
|
self:TraceOnOff( false )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- Set trace on or off
|
--- Set trace on or off
|
||||||
-- Note that when trace is off, no BASE.Debug statement is performed, increasing performance!
|
-- Note that when trace is off, no BASE.Debug statement is performed, increasing performance!
|
||||||
-- When Moose is loaded statically, (as one file), tracing is switched off by default.
|
-- When Moose is loaded statically, (as one file), tracing is switched off by default.
|
||||||
@@ -965,28 +1004,29 @@ end
|
|||||||
-- @param #BASE self
|
-- @param #BASE self
|
||||||
-- @param #boolean TraceOnOff Switch the tracing on or off.
|
-- @param #boolean TraceOnOff Switch the tracing on or off.
|
||||||
-- @usage
|
-- @usage
|
||||||
-- -- Switch the tracing On
|
|
||||||
-- BASE:TraceOnOff( true )
|
|
||||||
--
|
--
|
||||||
-- -- Switch the tracing Off
|
-- -- Switch the tracing On
|
||||||
-- BASE:TraceOnOff( false )
|
-- BASE:TraceOnOff( true )
|
||||||
|
--
|
||||||
|
-- -- Switch the tracing Off
|
||||||
|
-- BASE:TraceOnOff( false )
|
||||||
|
--
|
||||||
function BASE:TraceOnOff( TraceOnOff )
|
function BASE:TraceOnOff( TraceOnOff )
|
||||||
if TraceOnOff==false then
|
if TraceOnOff == false then
|
||||||
self:I( "Tracing in MOOSE is OFF" )
|
self:I( "Tracing in MOOSE is OFF" )
|
||||||
_TraceOnOff = false
|
_TraceOnOff = false
|
||||||
else
|
else
|
||||||
self:I( "Tracing in MOOSE is ON" )
|
self:I( "Tracing in MOOSE is ON" )
|
||||||
_TraceOnOff = true
|
_TraceOnOff = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Enquires if tracing is on (for the class).
|
--- Enquires if tracing is on (for the class).
|
||||||
-- @param #BASE self
|
-- @param #BASE self
|
||||||
-- @return #boolean
|
-- @return #boolean
|
||||||
function BASE:IsTrace()
|
function BASE:IsTrace()
|
||||||
|
|
||||||
if BASE.Debug and ( _TraceAll == true ) or ( _TraceClass[self.ClassName] or _TraceClassMethod[self.ClassName] ) then
|
if BASE.Debug and (_TraceAll == true) or (_TraceClass[self.ClassName] or _TraceClassMethod[self.ClassName]) then
|
||||||
return true
|
return true
|
||||||
else
|
else
|
||||||
return false
|
return false
|
||||||
@@ -1006,8 +1046,8 @@ end
|
|||||||
-- @param #boolean TraceAll true = trace all methods in MOOSE.
|
-- @param #boolean TraceAll true = trace all methods in MOOSE.
|
||||||
function BASE:TraceAll( TraceAll )
|
function BASE:TraceAll( TraceAll )
|
||||||
|
|
||||||
if TraceAll==false then
|
if TraceAll == false then
|
||||||
_TraceAll=false
|
_TraceAll = false
|
||||||
else
|
else
|
||||||
_TraceAll = true
|
_TraceAll = true
|
||||||
end
|
end
|
||||||
@@ -1046,7 +1086,7 @@ end
|
|||||||
-- @param Arguments A #table or any field.
|
-- @param Arguments A #table or any field.
|
||||||
function BASE:_F( Arguments, DebugInfoCurrentParam, DebugInfoFromParam )
|
function BASE:_F( Arguments, DebugInfoCurrentParam, DebugInfoFromParam )
|
||||||
|
|
||||||
if BASE.Debug and ( _TraceAll == true ) or ( _TraceClass[self.ClassName] or _TraceClassMethod[self.ClassName] ) then
|
if BASE.Debug and (_TraceAll == true) or (_TraceClass[self.ClassName] or _TraceClassMethod[self.ClassName]) then
|
||||||
|
|
||||||
local DebugInfoCurrent = DebugInfoCurrentParam and DebugInfoCurrentParam or BASE.Debug.getinfo( 2, "nl" )
|
local DebugInfoCurrent = DebugInfoCurrentParam and DebugInfoCurrentParam or BASE.Debug.getinfo( 2, "nl" )
|
||||||
local DebugInfoFrom = DebugInfoFromParam and DebugInfoFromParam or BASE.Debug.getinfo( 3, "l" )
|
local DebugInfoFrom = DebugInfoFromParam and DebugInfoFromParam or BASE.Debug.getinfo( 3, "l" )
|
||||||
@@ -1065,7 +1105,7 @@ function BASE:_F( Arguments, DebugInfoCurrentParam, DebugInfoFromParam )
|
|||||||
if DebugInfoFrom then
|
if DebugInfoFrom then
|
||||||
LineFrom = DebugInfoFrom.currentline
|
LineFrom = DebugInfoFrom.currentline
|
||||||
end
|
end
|
||||||
env.info( string.format( "%6d(%6d)/%1s:%30s%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
|
end
|
||||||
end
|
end
|
||||||
@@ -1085,7 +1125,6 @@ function BASE:F( Arguments )
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Trace a function call level 2. Must be at the beginning of the function logic.
|
--- Trace a function call level 2. Must be at the beginning of the function logic.
|
||||||
-- @param #BASE self
|
-- @param #BASE self
|
||||||
-- @param Arguments A #table or any field.
|
-- @param Arguments A #table or any field.
|
||||||
@@ -1121,28 +1160,28 @@ end
|
|||||||
-- @param Arguments A #table or any field.
|
-- @param Arguments A #table or any field.
|
||||||
function BASE:_T( Arguments, DebugInfoCurrentParam, DebugInfoFromParam )
|
function BASE:_T( Arguments, DebugInfoCurrentParam, DebugInfoFromParam )
|
||||||
|
|
||||||
if BASE.Debug and ( _TraceAll == true ) or ( _TraceClass[self.ClassName] or _TraceClassMethod[self.ClassName] ) then
|
if BASE.Debug and (_TraceAll == true) or (_TraceClass[self.ClassName] or _TraceClassMethod[self.ClassName]) then
|
||||||
|
|
||||||
local DebugInfoCurrent = DebugInfoCurrentParam and DebugInfoCurrentParam or BASE.Debug.getinfo( 2, "nl" )
|
local DebugInfoCurrent = DebugInfoCurrentParam and DebugInfoCurrentParam or BASE.Debug.getinfo( 2, "nl" )
|
||||||
local DebugInfoFrom = DebugInfoFromParam and DebugInfoFromParam or BASE.Debug.getinfo( 3, "l" )
|
local DebugInfoFrom = DebugInfoFromParam and DebugInfoFromParam or BASE.Debug.getinfo( 3, "l" )
|
||||||
|
|
||||||
local Function = "function"
|
local Function = "function"
|
||||||
if DebugInfoCurrent.name then
|
if DebugInfoCurrent.name then
|
||||||
Function = DebugInfoCurrent.name
|
Function = DebugInfoCurrent.name
|
||||||
end
|
end
|
||||||
|
|
||||||
if _TraceAll == true or _TraceClass[self.ClassName] or _TraceClassMethod[self.ClassName].Method[Function] then
|
if _TraceAll == true or _TraceClass[self.ClassName] or _TraceClassMethod[self.ClassName].Method[Function] then
|
||||||
local LineCurrent = 0
|
local LineCurrent = 0
|
||||||
if DebugInfoCurrent.currentline then
|
if DebugInfoCurrent.currentline then
|
||||||
LineCurrent = DebugInfoCurrent.currentline
|
LineCurrent = DebugInfoCurrent.currentline
|
||||||
end
|
end
|
||||||
local LineFrom = 0
|
local LineFrom = 0
|
||||||
if DebugInfoFrom then
|
if DebugInfoFrom then
|
||||||
LineFrom = DebugInfoFrom.currentline
|
LineFrom = DebugInfoFrom.currentline
|
||||||
end
|
end
|
||||||
env.info( string.format( "%6d(%6d)/%1s:%30s%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
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Trace a function logic level 1. Can be anywhere within the function logic.
|
--- Trace a function logic level 1. Can be anywhere within the function logic.
|
||||||
@@ -1160,7 +1199,6 @@ function BASE:T( Arguments )
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Trace a function logic level 2. Can be anywhere within the function logic.
|
--- Trace a function logic level 2. Can be anywhere within the function logic.
|
||||||
-- @param #BASE self
|
-- @param #BASE self
|
||||||
-- @param Arguments A #table or any field.
|
-- @param Arguments A #table or any field.
|
||||||
@@ -1197,28 +1235,27 @@ end
|
|||||||
function BASE:E( Arguments )
|
function BASE:E( Arguments )
|
||||||
|
|
||||||
if BASE.Debug then
|
if BASE.Debug then
|
||||||
local DebugInfoCurrent = BASE.Debug.getinfo( 2, "nl" )
|
local DebugInfoCurrent = BASE.Debug.getinfo( 2, "nl" )
|
||||||
local DebugInfoFrom = BASE.Debug.getinfo( 3, "l" )
|
local DebugInfoFrom = BASE.Debug.getinfo( 3, "l" )
|
||||||
|
|
||||||
local Function = "function"
|
local Function = "function"
|
||||||
if DebugInfoCurrent.name then
|
if DebugInfoCurrent.name then
|
||||||
Function = DebugInfoCurrent.name
|
Function = DebugInfoCurrent.name
|
||||||
end
|
end
|
||||||
|
|
||||||
local LineCurrent = DebugInfoCurrent.currentline
|
local LineCurrent = DebugInfoCurrent.currentline
|
||||||
local LineFrom = -1
|
local LineFrom = -1
|
||||||
if DebugInfoFrom then
|
if DebugInfoFrom then
|
||||||
LineFrom = DebugInfoFrom.currentline
|
LineFrom = DebugInfoFrom.currentline
|
||||||
end
|
end
|
||||||
|
|
||||||
env.info( string.format( "%6d(%6d)/%1s:%30s%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
|
else
|
||||||
env.info( string.format( "%1s:%30s%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
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Log an information which will be traced always. Can be anywhere within the function logic.
|
--- Log an information which will be traced always. Can be anywhere within the function logic.
|
||||||
-- @param #BASE self
|
-- @param #BASE self
|
||||||
-- @param Arguments A #table or any field.
|
-- @param Arguments A #table or any field.
|
||||||
@@ -1239,26 +1276,23 @@ function BASE:I( Arguments )
|
|||||||
LineFrom = DebugInfoFrom.currentline
|
LineFrom = DebugInfoFrom.currentline
|
||||||
end
|
end
|
||||||
|
|
||||||
env.info( string.format( "%6d(%6d)/%1s:%30s%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
|
else
|
||||||
env.info( string.format( "%1s:%30s%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
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- old stuff
|
--- old stuff
|
||||||
|
|
||||||
--function BASE:_Destructor()
|
-- function BASE:_Destructor()
|
||||||
-- --self:E("_Destructor")
|
-- --self:E("_Destructor")
|
||||||
--
|
--
|
||||||
-- --self:EventRemoveAll()
|
-- --self:EventRemoveAll()
|
||||||
--end
|
-- end
|
||||||
|
|
||||||
|
|
||||||
-- THIS IS WHY WE NEED LUA 5.2 ...
|
-- THIS IS WHY WE NEED LUA 5.2 ...
|
||||||
--function BASE:_SetDestructor()
|
-- function BASE:_SetDestructor()
|
||||||
--
|
--
|
||||||
-- -- TODO: Okay, this is really technical...
|
-- -- TODO: Okay, this is really technical...
|
||||||
-- -- When you set a proxy to a table to catch __gc, weak tables don't behave like weak...
|
-- -- When you set a proxy to a table to catch __gc, weak tables don't behave like weak...
|
||||||
@@ -1279,4 +1313,4 @@ end
|
|||||||
-- -- will be invoked and the destructor called
|
-- -- will be invoked and the destructor called
|
||||||
-- rawset( self, '__proxy', proxy )
|
-- rawset( self, '__proxy', proxy )
|
||||||
--
|
--
|
||||||
--end
|
-- end
|
||||||
|
|||||||
441
Moose Development/Moose/Core/Beacon.lua
Normal file
441
Moose Development/Moose/Core/Beacon.lua
Normal file
@@ -0,0 +1,441 @@
|
|||||||
|
--- **Core** - TACAN and other beacons.
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ## Features:
|
||||||
|
--
|
||||||
|
-- * Provide beacon functionality to assist pilots.
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ### Authors: Hugues "Grey_Echo" Bousquet, funkyfranky
|
||||||
|
--
|
||||||
|
-- @module Core.Beacon
|
||||||
|
-- @image Core_Radio.JPG
|
||||||
|
|
||||||
|
--- *In order for the light to shine so brightly, the darkness must be present.* -- Francis Bacon
|
||||||
|
--
|
||||||
|
-- After attaching a @{#BEACON} to your @{Wrapper.Positionable#POSITIONABLE}, you need to select the right function to activate the kind of beacon you want.
|
||||||
|
-- There are two types of BEACONs available : the AA TACAN Beacon and the general purpose Radio Beacon.
|
||||||
|
-- Note that in both case, you can set an optional parameter : the `BeaconDuration`. This can be very usefull to simulate the battery time if your BEACON is
|
||||||
|
-- attach to a cargo crate, for exemple.
|
||||||
|
--
|
||||||
|
-- ## AA TACAN Beacon usage
|
||||||
|
--
|
||||||
|
-- This beacon only works with airborne @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}. Use @{#BEACON:AATACAN}() to set the beacon parameters and start the beacon.
|
||||||
|
-- Use @#BEACON:StopAATACAN}() to stop it.
|
||||||
|
--
|
||||||
|
-- ## General Purpose Radio Beacon usage
|
||||||
|
--
|
||||||
|
-- This beacon will work with any @{Wrapper.Positionable#POSITIONABLE}, but **it won't follow the @{Wrapper.Positionable#POSITIONABLE}** ! This means that you should only use it with
|
||||||
|
-- @{Wrapper.Positionable#POSITIONABLE} that don't move, or move very slowly. Use @{#BEACON:RadioBeacon}() to set the beacon parameters and start the beacon.
|
||||||
|
-- Use @{#BEACON:StopRadioBeacon}() to stop it.
|
||||||
|
--
|
||||||
|
-- @type BEACON
|
||||||
|
-- @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,
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Beacon types supported by DCS.
|
||||||
|
-- @type BEACON.Type
|
||||||
|
-- @field #number NULL
|
||||||
|
-- @field #number VOR
|
||||||
|
-- @field #number DME
|
||||||
|
-- @field #number VOR_DME
|
||||||
|
-- @field #number TACAN TACtical Air Navigation system.
|
||||||
|
-- @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 PRMG_LOCALIZER
|
||||||
|
-- @field #number PRMG_GLIDESLOPE
|
||||||
|
-- @field #number ICLS Same as ICLS glideslope.
|
||||||
|
-- @field #number ICLS_LOCALIZER
|
||||||
|
-- @field #number ICLS_GLIDESLOPE
|
||||||
|
-- @field #number NAUTICAL_HOMER
|
||||||
|
BEACON.Type={
|
||||||
|
NULL = 0,
|
||||||
|
VOR = 1,
|
||||||
|
DME = 2,
|
||||||
|
VOR_DME = 3,
|
||||||
|
TACAN = 4,
|
||||||
|
VORTAC = 5,
|
||||||
|
RSBN = 128,
|
||||||
|
BROADCAST_STATION = 1024,
|
||||||
|
HOMER = 8,
|
||||||
|
AIRPORT_HOMER = 4104,
|
||||||
|
AIRPORT_HOMER_WITH_MARKER = 4136,
|
||||||
|
ILS_FAR_HOMER = 16408,
|
||||||
|
ILS_NEAR_HOMER = 16424,
|
||||||
|
ILS_LOCALIZER = 16640,
|
||||||
|
ILS_GLIDESLOPE = 16896,
|
||||||
|
PRMG_LOCALIZER = 33024,
|
||||||
|
PRMG_GLIDESLOPE = 33280,
|
||||||
|
ICLS = 131584, --leaving this in here but it is the same as ICLS_GLIDESLOPE
|
||||||
|
ICLS_LOCALIZER = 131328,
|
||||||
|
ICLS_GLIDESLOPE = 131584,
|
||||||
|
NAUTICAL_HOMER = 65536,
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Beacon systems supported by DCS. https://wiki.hoggitworld.com/view/DCS_command_activateBeacon
|
||||||
|
-- @type BEACON.System
|
||||||
|
-- @field #number PAR_10 ?
|
||||||
|
-- @field #number RSBN_5 Russian VOR/DME system.
|
||||||
|
-- @field #number TACAN TACtical Air Navigation system on ground.
|
||||||
|
-- @field #number TACAN_TANKER_X TACtical Air Navigation system for tankers on X band.
|
||||||
|
-- @field #number TACAN_TANKER_Y TACtical Air Navigation system for tankers on Y band.
|
||||||
|
-- @field #number VOR Very High Frequency Omnidirectional Range
|
||||||
|
-- @field #number ILS_LOCALIZER ILS localizer
|
||||||
|
-- @field #number ILS_GLIDESLOPE ILS glide slope.
|
||||||
|
-- @field #number PRGM_LOCALIZER PRGM localizer.
|
||||||
|
-- @field #number PRGM_GLIDESLOPE PRGM glide slope.
|
||||||
|
-- @field #number BROADCAST_STATION Broadcast station.
|
||||||
|
-- @field #number VORTAC Radio-based navigational aid for aircraft pilots consisting of a co-located VHF omnidirectional range (VOR) beacon and a tactical air navigation system (TACAN) beacon.
|
||||||
|
-- @field #number TACAN_AA_MODE_X TACtical Air Navigation for aircraft on X band.
|
||||||
|
-- @field #number TACAN_AA_MODE_Y TACtical Air Navigation for aircraft on Y band.
|
||||||
|
-- @field #number VORDME Radio beacon that combines a VHF omnidirectional range (VOR) with a distance measuring equipment (DME).
|
||||||
|
-- @field #number ICLS_LOCALIZER Carrier landing system.
|
||||||
|
-- @field #number ICLS_GLIDESLOPE Carrier landing system.
|
||||||
|
BEACON.System={
|
||||||
|
PAR_10 = 1,
|
||||||
|
RSBN_5 = 2,
|
||||||
|
TACAN = 3,
|
||||||
|
TACAN_TANKER_X = 4,
|
||||||
|
TACAN_TANKER_Y = 5,
|
||||||
|
VOR = 6,
|
||||||
|
ILS_LOCALIZER = 7,
|
||||||
|
ILS_GLIDESLOPE = 8,
|
||||||
|
PRMG_LOCALIZER = 9,
|
||||||
|
PRMG_GLIDESLOPE = 10,
|
||||||
|
BROADCAST_STATION = 11,
|
||||||
|
VORTAC = 12,
|
||||||
|
TACAN_AA_MODE_X = 13,
|
||||||
|
TACAN_AA_MODE_Y = 14,
|
||||||
|
VORDME = 15,
|
||||||
|
ICLS_LOCALIZER = 16,
|
||||||
|
ICLS_GLIDESLOPE = 17,
|
||||||
|
}
|
||||||
|
|
||||||
|
--- 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 object or #nil if the POSITIONABLE is invalid.
|
||||||
|
function BEACON:New( Positionable )
|
||||||
|
|
||||||
|
-- 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
|
||||||
|
|
||||||
|
self:E( { "The passed POSITIONABLE is invalid, no BEACON created", Positionable } )
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Activates a TACAN BEACON.
|
||||||
|
-- @param #BEACON self
|
||||||
|
-- @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
|
||||||
|
|
||||||
|
-- Beacon type.
|
||||||
|
local Type = BEACON.Type.TACAN
|
||||||
|
|
||||||
|
-- Beacon system.
|
||||||
|
local System = BEACON.System.TACAN
|
||||||
|
|
||||||
|
-- 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
|
||||||
|
|
||||||
|
-- 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 scheduler.
|
||||||
|
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 scheduler
|
||||||
|
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
|
||||||
|
-- @param #number TACANChannel (the "10" part in "10Y"). Note that AA TACAN are only available on Y Channels
|
||||||
|
-- @param #string Message The Message that is going to be coded in Morse and broadcasted by the beacon
|
||||||
|
-- @param #boolean Bearing Can the BEACON be homed on ?
|
||||||
|
-- @param #number BeaconDuration How long will the beacon last in seconds. Omit for forever.
|
||||||
|
-- @return #BEACON self
|
||||||
|
-- @usage
|
||||||
|
--
|
||||||
|
-- -- Let's create a TACAN Beacon for a tanker
|
||||||
|
-- local myUnit = UNIT:FindByName("MyUnit")
|
||||||
|
-- local myBeacon = myUnit:GetBeacon() -- Creates the beacon
|
||||||
|
--
|
||||||
|
-- myBeacon:AATACAN(20, "TEXACO", true) -- Activate the beacon
|
||||||
|
--
|
||||||
|
function BEACON:AATACAN( TACANChannel, Message, Bearing, BeaconDuration )
|
||||||
|
self:F( { TACANChannel, Message, Bearing, BeaconDuration } )
|
||||||
|
|
||||||
|
local IsValid = true
|
||||||
|
|
||||||
|
if not self.Positionable:IsAir() then
|
||||||
|
self:E( { "The POSITIONABLE you want to attach the AA TACAN Beacon is not an aircraft! The BEACON is not emitting", self.Positionable } )
|
||||||
|
IsValid = false
|
||||||
|
end
|
||||||
|
|
||||||
|
local Frequency = self:_TACANToFrequency( TACANChannel, "Y" )
|
||||||
|
if not Frequency then
|
||||||
|
self:E( { "The passed TACAN channel is invalid, the BEACON is not emitting" } )
|
||||||
|
IsValid = false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- I'm using the beacon type 4 (BEACON_TYPE_TACAN). For System, I'm using 5 (TACAN_TANKER_MODE_Y) if the bearing shows its bearing
|
||||||
|
-- or 14 (TACAN_AA_MODE_Y) if it does not
|
||||||
|
local System
|
||||||
|
if Bearing then
|
||||||
|
System = 5
|
||||||
|
else
|
||||||
|
System = 14
|
||||||
|
end
|
||||||
|
|
||||||
|
if IsValid then -- Starts the BEACON
|
||||||
|
self:T2( { "AA TACAN BEACON started !" } )
|
||||||
|
self.Positionable:SetCommand( {
|
||||||
|
id = "ActivateBeacon",
|
||||||
|
params = {
|
||||||
|
type = 4,
|
||||||
|
system = System,
|
||||||
|
callsign = Message,
|
||||||
|
frequency = Frequency,
|
||||||
|
},
|
||||||
|
} )
|
||||||
|
|
||||||
|
if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD
|
||||||
|
SCHEDULER:New( nil, function()
|
||||||
|
self:StopAATACAN()
|
||||||
|
end, {}, BeaconDuration )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Stops the AA TACAN BEACON
|
||||||
|
-- @param #BEACON self
|
||||||
|
-- @return #BEACON self
|
||||||
|
function BEACON:StopAATACAN()
|
||||||
|
self:F()
|
||||||
|
if not self.Positionable then
|
||||||
|
self:E( { "Start the beacon first before stopping it!" } )
|
||||||
|
else
|
||||||
|
self.Positionable:SetCommand( {
|
||||||
|
id = 'DeactivateBeacon',
|
||||||
|
params = {},
|
||||||
|
} )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Activates a general purpose Radio Beacon
|
||||||
|
-- This uses the very generic singleton function "trigger.action.radioTransmission()" provided by DCS to broadcast a sound file on a specific frequency.
|
||||||
|
-- Although any frequency could be used, only 2 DCS Modules can home on radio beacons at the time of writing : the Huey and the Mi-8.
|
||||||
|
-- They can home in on these specific frequencies :
|
||||||
|
-- * **Mi8**
|
||||||
|
-- * R-828 -> 20-60MHz
|
||||||
|
-- * ARKUD -> 100-150MHz (canal 1 : 114166, canal 2 : 114333, canal 3 : 114583, canal 4 : 121500, canal 5 : 123100, canal 6 : 124100) AM
|
||||||
|
-- * ARK9 -> 150-1300KHz
|
||||||
|
-- * **Huey**
|
||||||
|
-- * AN/ARC-131 -> 30-76 Mhz FM
|
||||||
|
-- @param #BEACON self
|
||||||
|
-- @param #string FileName The name of the audio file
|
||||||
|
-- @param #number Frequency in MHz
|
||||||
|
-- @param #number Modulation either radio.modulation.AM or radio.modulation.FM
|
||||||
|
-- @param #number Power in W
|
||||||
|
-- @param #number BeaconDuration How long will the beacon last in seconds. Omit for forever.
|
||||||
|
-- @return #BEACON self
|
||||||
|
-- @usage
|
||||||
|
-- -- Let's create a beacon for a unit in distress.
|
||||||
|
-- -- Frequency will be 40MHz FM (home-able by a Huey's AN/ARC-131)
|
||||||
|
-- -- The beacon they use is battery-powered, and only lasts for 5 min
|
||||||
|
-- local UnitInDistress = UNIT:FindByName("Unit1")
|
||||||
|
-- local UnitBeacon = UnitInDistress:GetBeacon()
|
||||||
|
--
|
||||||
|
-- -- Set the beacon and start it
|
||||||
|
-- UnitBeacon:RadioBeacon("MySoundFileSOS.ogg", 40, radio.modulation.FM, 20, 5*60)
|
||||||
|
function BEACON:RadioBeacon( FileName, Frequency, Modulation, Power, BeaconDuration )
|
||||||
|
self:F( { FileName, Frequency, Modulation, Power, BeaconDuration } )
|
||||||
|
local IsValid = false
|
||||||
|
|
||||||
|
-- Check the filename
|
||||||
|
if type( FileName ) == "string" then
|
||||||
|
if FileName:find( ".ogg" ) or FileName:find( ".wav" ) then
|
||||||
|
if not FileName:find( "l10n/DEFAULT/" ) then
|
||||||
|
FileName = "l10n/DEFAULT/" .. FileName
|
||||||
|
end
|
||||||
|
IsValid = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not IsValid then
|
||||||
|
self:E( { "File name invalid. Maybe something wrong with the extension? ", FileName } )
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check the Frequency
|
||||||
|
if type( Frequency ) ~= "number" and IsValid then
|
||||||
|
self:E( { "Frequency invalid. ", Frequency } )
|
||||||
|
IsValid = false
|
||||||
|
end
|
||||||
|
Frequency = Frequency * 1000000 -- Conversion to Hz
|
||||||
|
|
||||||
|
-- Check the modulation
|
||||||
|
if Modulation ~= radio.modulation.AM and Modulation ~= radio.modulation.FM and IsValid then -- TODO: Maybe make this future proof if ED decides to add an other modulation ?
|
||||||
|
self:E( { "Modulation is invalid. Use DCS's enum radio.modulation.", Modulation } )
|
||||||
|
IsValid = false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check the Power
|
||||||
|
if type( Power ) ~= "number" and IsValid then
|
||||||
|
self:E( { "Power is invalid. ", Power } )
|
||||||
|
IsValid = false
|
||||||
|
end
|
||||||
|
Power = math.floor( math.abs( Power ) ) -- TODO: Find what is the maximum power allowed by DCS and limit power to that
|
||||||
|
|
||||||
|
if IsValid then
|
||||||
|
self:T2( { "Activating Beacon on ", Frequency, Modulation } )
|
||||||
|
-- Note that this is looped. I have to give this transmission a unique name, I use the class ID
|
||||||
|
trigger.action.radioTransmission( FileName, self.Positionable:GetPositionVec3(), Modulation, true, Frequency, Power, tostring( self.ID ) )
|
||||||
|
|
||||||
|
if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD
|
||||||
|
SCHEDULER:New( nil, function()
|
||||||
|
self:StopRadioBeacon()
|
||||||
|
end, {}, BeaconDuration )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Stops the AA TACAN BEACON
|
||||||
|
-- @param #BEACON self
|
||||||
|
-- @return #BEACON self
|
||||||
|
function BEACON:StopRadioBeacon()
|
||||||
|
self:F()
|
||||||
|
-- The unique name of the transmission is the class ID
|
||||||
|
trigger.action.stopRadioTransmission( tostring( self.ID ) )
|
||||||
|
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 Frequency
|
||||||
|
-- @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
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -14,7 +14,7 @@
|
|||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
-- Within a running mission, various DCS events occur. Units are dynamically created, crash, die, shoot stuff, get hit etc.
|
-- Within a running mission, various DCS events occur. Units are dynamically created, crash, die, shoot stuff, get hit etc.
|
||||||
-- This module provides a mechanism to dispatch those events occuring within your running mission, to the different objects orchestrating your mission.
|
-- This module provides a mechanism to dispatch those events occurring within your running mission, to the different objects orchestrating your mission.
|
||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
--
|
--
|
||||||
@@ -141,7 +141,6 @@
|
|||||||
-- EventData.IniUnit:SmokeGreen()
|
-- EventData.IniUnit:SmokeGreen()
|
||||||
-- end
|
-- end
|
||||||
--
|
--
|
||||||
--
|
|
||||||
-- Find below an overview which events populate which information categories:
|
-- Find below an overview which events populate which information categories:
|
||||||
--
|
--
|
||||||
-- 
|
-- 
|
||||||
@@ -172,7 +171,6 @@
|
|||||||
-- @module Core.Event
|
-- @module Core.Event
|
||||||
-- @image Core_Event.JPG
|
-- @image Core_Event.JPG
|
||||||
|
|
||||||
|
|
||||||
--- @type EVENT
|
--- @type EVENT
|
||||||
-- @field #EVENT.Events Events
|
-- @field #EVENT.Events Events
|
||||||
-- @extends Core.Base#BASE
|
-- @extends Core.Base#BASE
|
||||||
@@ -192,7 +190,7 @@ world.event.S_EVENT_DELETE_ZONE = world.event.S_EVENT_MAX + 1003
|
|||||||
world.event.S_EVENT_NEW_ZONE_GOAL = 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_DELETE_ZONE_GOAL = world.event.S_EVENT_MAX + 1005
|
||||||
world.event.S_EVENT_REMOVE_UNIT = world.event.S_EVENT_MAX + 1006
|
world.event.S_EVENT_REMOVE_UNIT = world.event.S_EVENT_MAX + 1006
|
||||||
|
world.event.S_EVENT_PLAYER_ENTER_AIRCRAFT = world.event.S_EVENT_MAX + 1007
|
||||||
|
|
||||||
--- The different types of events supported by MOOSE.
|
--- The different types of events supported by MOOSE.
|
||||||
-- Use this structure to subscribe to events using the @{Core.Base#BASE.HandleEvent}() method.
|
-- Use this structure to subscribe to events using the @{Core.Base#BASE.HandleEvent}() method.
|
||||||
@@ -226,26 +224,34 @@ EVENTS = {
|
|||||||
MarkChange = world.event.S_EVENT_MARK_CHANGE,
|
MarkChange = world.event.S_EVENT_MARK_CHANGE,
|
||||||
MarkRemoved = world.event.S_EVENT_MARK_REMOVED,
|
MarkRemoved = world.event.S_EVENT_MARK_REMOVED,
|
||||||
-- Moose Events
|
-- Moose Events
|
||||||
NewCargo = world.event.S_EVENT_NEW_CARGO,
|
NewCargo = world.event.S_EVENT_NEW_CARGO,
|
||||||
DeleteCargo = world.event.S_EVENT_DELETE_CARGO,
|
DeleteCargo = world.event.S_EVENT_DELETE_CARGO,
|
||||||
NewZone = world.event.S_EVENT_NEW_ZONE,
|
NewZone = world.event.S_EVENT_NEW_ZONE,
|
||||||
DeleteZone = world.event.S_EVENT_DELETE_ZONE,
|
DeleteZone = world.event.S_EVENT_DELETE_ZONE,
|
||||||
NewZoneGoal = world.event.S_EVENT_NEW_ZONE_GOAL,
|
NewZoneGoal = world.event.S_EVENT_NEW_ZONE_GOAL,
|
||||||
DeleteZoneGoal = world.event.S_EVENT_DELETE_ZONE_GOAL,
|
DeleteZoneGoal = world.event.S_EVENT_DELETE_ZONE_GOAL,
|
||||||
RemoveUnit = world.event.S_EVENT_REMOVE_UNIT,
|
RemoveUnit = world.event.S_EVENT_REMOVE_UNIT,
|
||||||
|
PlayerEnterAircraft = world.event.S_EVENT_PLAYER_ENTER_AIRCRAFT,
|
||||||
-- Added with DCS 2.5.6
|
-- 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
|
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,
|
Kill = world.event.S_EVENT_KILL or -1,
|
||||||
Score = world.event.S_EVENT_SCORE or -1,
|
Score = world.event.S_EVENT_SCORE or -1,
|
||||||
UnitLost = world.event.S_EVENT_UNIT_LOST or -1,
|
UnitLost = world.event.S_EVENT_UNIT_LOST or -1,
|
||||||
LandingAfterEjection = world.event.S_EVENT_LANDING_AFTER_EJECTION or -1,
|
LandingAfterEjection = world.event.S_EVENT_LANDING_AFTER_EJECTION or -1,
|
||||||
|
-- Added with DCS 2.7.0
|
||||||
|
ParatrooperLanding = world.event.S_EVENT_PARATROOPER_LENDING or -1,
|
||||||
|
DiscardChairAfterEjection = world.event.S_EVENT_DISCARD_CHAIR_AFTER_EJECTION or -1,
|
||||||
|
WeaponAdd = world.event.S_EVENT_WEAPON_ADD or -1,
|
||||||
|
TriggerZone = world.event.S_EVENT_TRIGGER_ZONE or -1,
|
||||||
|
LandingQualityMark = world.event.S_EVENT_LANDING_QUALITY_MARK or -1,
|
||||||
|
BDA = world.event.S_EVENT_BDA or -1,
|
||||||
}
|
}
|
||||||
|
|
||||||
--- The Event structure
|
--- The Event structure
|
||||||
-- Note that at the beginning of each field description, there is an indication which field will be populated depending on the object type involved in the Event:
|
-- Note that at the beginning of each field description, there is an indication which field will be populated depending on the object type involved in the Event:
|
||||||
--
|
--
|
||||||
-- * A (Object.Category.)UNIT : A UNIT object type is involved in the Event.
|
-- * A (Object.Category.)UNIT : A UNIT object type is involved in the Event.
|
||||||
-- * A (Object.Category.)STATIC : A STATIC object type is involved in the Event.µ
|
-- * A (Object.Category.)STATIC : A STATIC object type is involved in the Event.
|
||||||
--
|
--
|
||||||
-- @type EVENTDATA
|
-- @type EVENTDATA
|
||||||
-- @field #number id The identifier of the event.
|
-- @field #number id The identifier of the event.
|
||||||
@@ -295,8 +301,6 @@ EVENTS = {
|
|||||||
-- @field Core.ZONE#ZONE Zone The zone object.
|
-- @field Core.ZONE#ZONE Zone The zone object.
|
||||||
-- @field #string ZoneName The name of the zone.
|
-- @field #string ZoneName The name of the zone.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
local _EVENTMETA = {
|
local _EVENTMETA = {
|
||||||
[world.event.S_EVENT_SHOT] = {
|
[world.event.S_EVENT_SHOT] = {
|
||||||
Order = 1,
|
Order = 1,
|
||||||
@@ -489,6 +493,11 @@ local _EVENTMETA = {
|
|||||||
Event = "OnEventRemoveUnit",
|
Event = "OnEventRemoveUnit",
|
||||||
Text = "S_EVENT_REMOVE_UNIT"
|
Text = "S_EVENT_REMOVE_UNIT"
|
||||||
},
|
},
|
||||||
|
[EVENTS.PlayerEnterAircraft] = {
|
||||||
|
Order = 1,
|
||||||
|
Event = "OnEventPlayerEnterAircraft",
|
||||||
|
Text = "S_EVENT_PLAYER_ENTER_AIRCRAFT"
|
||||||
|
},
|
||||||
-- Added with DCS 2.5.6
|
-- Added with DCS 2.5.6
|
||||||
[EVENTS.DetailedFailure] = {
|
[EVENTS.DetailedFailure] = {
|
||||||
Order = 1,
|
Order = 1,
|
||||||
@@ -515,9 +524,39 @@ local _EVENTMETA = {
|
|||||||
Event = "OnEventLandingAfterEjection",
|
Event = "OnEventLandingAfterEjection",
|
||||||
Text = "S_EVENT_LANDING_AFTER_EJECTION"
|
Text = "S_EVENT_LANDING_AFTER_EJECTION"
|
||||||
},
|
},
|
||||||
|
-- Added with DCS 2.7.0
|
||||||
|
[EVENTS.ParatrooperLanding] = {
|
||||||
|
Order = 1,
|
||||||
|
Event = "OnEventParatrooperLanding",
|
||||||
|
Text = "S_EVENT_PARATROOPER_LENDING"
|
||||||
|
},
|
||||||
|
[EVENTS.DiscardChairAfterEjection] = {
|
||||||
|
Order = 1,
|
||||||
|
Event = "OnEventDiscardChairAfterEjection",
|
||||||
|
Text = "S_EVENT_DISCARD_CHAIR_AFTER_EJECTION"
|
||||||
|
},
|
||||||
|
[EVENTS.WeaponAdd] = {
|
||||||
|
Order = 1,
|
||||||
|
Event = "OnEventWeaponAdd",
|
||||||
|
Text = "S_EVENT_WEAPON_ADD"
|
||||||
|
},
|
||||||
|
[EVENTS.TriggerZone] = {
|
||||||
|
Order = 1,
|
||||||
|
Event = "OnEventTriggerZone",
|
||||||
|
Text = "S_EVENT_TRIGGER_ZONE"
|
||||||
|
},
|
||||||
|
[EVENTS.LandingQualityMark] = {
|
||||||
|
Order = 1,
|
||||||
|
Event = "OnEventLandingQualityMark",
|
||||||
|
Text = "S_EVENT_LANDING_QUALITYMARK"
|
||||||
|
},
|
||||||
|
[EVENTS.BDA] = {
|
||||||
|
Order = 1,
|
||||||
|
Event = "OnEventBDA",
|
||||||
|
Text = "S_EVENT_BDA"
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
--- The Events structure
|
--- The Events structure
|
||||||
-- @type EVENT.Events
|
-- @type EVENT.Events
|
||||||
-- @field #number IniUnit
|
-- @field #number IniUnit
|
||||||
@@ -526,17 +565,20 @@ local _EVENTMETA = {
|
|||||||
-- @param #EVENT self
|
-- @param #EVENT self
|
||||||
-- @return #EVENT self
|
-- @return #EVENT self
|
||||||
function EVENT:New()
|
function EVENT:New()
|
||||||
|
|
||||||
|
-- Inherit base.
|
||||||
local self = BASE:Inherit( self, BASE:New() )
|
local self = BASE:Inherit( self, BASE:New() )
|
||||||
self:F2()
|
|
||||||
|
-- Add world event handler.
|
||||||
self.EventHandler = world.addEventHandler( self )
|
self.EventHandler = world.addEventHandler( self )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Initializes the Events structure for the event.
|
||||||
--- Initializes the Events structure for the event
|
|
||||||
-- @param #EVENT self
|
-- @param #EVENT self
|
||||||
-- @param DCS#world.event EventID
|
-- @param DCS#world.event EventID Event ID.
|
||||||
-- @param Core.Base#BASE EventClass
|
-- @param Core.Base#BASE EventClass The class object for which events are handled.
|
||||||
-- @return #EVENT.Events
|
-- @return #EVENT.Events
|
||||||
function EVENT:Init( EventID, EventClass )
|
function EVENT:Init( EventID, EventClass )
|
||||||
self:F3( { _EVENTMETA[EventID].Text, EventClass } )
|
self:F3( { _EVENTMETA[EventID].Text, EventClass } )
|
||||||
@@ -546,49 +588,55 @@ function EVENT:Init( EventID, EventClass )
|
|||||||
self.Events[EventID] = {}
|
self.Events[EventID] = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Each event has a subtable of EventClasses, ordered by EventPriority.
|
-- Each event has a sub-table of EventClasses, ordered by EventPriority.
|
||||||
local EventPriority = EventClass:GetEventPriority()
|
local EventPriority = EventClass:GetEventPriority()
|
||||||
|
|
||||||
if not self.Events[EventID][EventPriority] then
|
if not self.Events[EventID][EventPriority] then
|
||||||
self.Events[EventID][EventPriority] = setmetatable( {}, { __mode = "k" } )
|
self.Events[EventID][EventPriority] = setmetatable( {}, { __mode = "k" } )
|
||||||
end
|
end
|
||||||
|
|
||||||
if not self.Events[EventID][EventPriority][EventClass] then
|
if not self.Events[EventID][EventPriority][EventClass] then
|
||||||
self.Events[EventID][EventPriority][EventClass] = {}
|
self.Events[EventID][EventPriority][EventClass] = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
return self.Events[EventID][EventPriority][EventClass]
|
return self.Events[EventID][EventPriority][EventClass]
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Removes a subscription
|
--- Removes a subscription
|
||||||
-- @param #EVENT self
|
-- @param #EVENT self
|
||||||
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is.
|
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is.
|
||||||
-- @param DCS#world.event EventID
|
-- @param DCS#world.event EventID Event ID.
|
||||||
-- @return #EVENT.Events
|
-- @return #EVENT self
|
||||||
function EVENT:RemoveEvent( EventClass, EventID )
|
function EVENT:RemoveEvent( EventClass, EventID )
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
self:F2( { "Removing subscription for class: ", EventClass:GetClassNameAndID() } )
|
self:F2( { "Removing subscription for class: ", EventClass:GetClassNameAndID() } )
|
||||||
|
|
||||||
|
-- Get event prio.
|
||||||
local EventPriority = EventClass:GetEventPriority()
|
local EventPriority = EventClass:GetEventPriority()
|
||||||
|
|
||||||
|
-- Events.
|
||||||
self.Events = self.Events or {}
|
self.Events = self.Events or {}
|
||||||
self.Events[EventID] = self.Events[EventID] or {}
|
self.Events[EventID] = self.Events[EventID] or {}
|
||||||
self.Events[EventID][EventPriority] = self.Events[EventID][EventPriority] or {}
|
self.Events[EventID][EventPriority] = self.Events[EventID][EventPriority] or {}
|
||||||
|
|
||||||
|
-- Remove
|
||||||
self.Events[EventID][EventPriority][EventClass] = nil
|
self.Events[EventID][EventPriority][EventClass] = nil
|
||||||
|
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Resets subscriptions
|
--- Resets subscriptions.
|
||||||
-- @param #EVENT self
|
-- @param #EVENT self
|
||||||
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is.
|
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is.
|
||||||
-- @param DCS#world.event EventID
|
-- @param DCS#world.event EventID Event ID.
|
||||||
-- @return #EVENT.Events
|
-- @return #EVENT.Events
|
||||||
function EVENT:Reset( EventObject ) --R2.1
|
function EVENT:Reset( EventObject ) -- R2.1
|
||||||
|
|
||||||
self:F( { "Resetting subscriptions for class: ", EventObject:GetClassNameAndID() } )
|
self:F( { "Resetting subscriptions for class: ", EventObject:GetClassNameAndID() } )
|
||||||
|
|
||||||
local EventPriority = EventObject:GetEventPriority()
|
local EventPriority = EventObject:GetEventPriority()
|
||||||
|
|
||||||
for EventID, EventData in pairs( self.Events ) do
|
for EventID, EventData in pairs( self.Events ) do
|
||||||
if self.EventsDead then
|
if self.EventsDead then
|
||||||
if self.EventsDead[EventID] then
|
if self.EventsDead[EventID] then
|
||||||
@@ -602,31 +650,31 @@ function EVENT:Reset( EventObject ) --R2.1
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- Clears all event subscriptions for a @{Core.Base#BASE} derived object.
|
--- Clears all event subscriptions for a @{Core.Base#BASE} derived object.
|
||||||
-- @param #EVENT self
|
-- @param #EVENT self
|
||||||
-- @param Core.Base#BASE EventObject
|
-- @param Core.Base#BASE EventClass The self class object for which the events are removed.
|
||||||
function EVENT:RemoveAll( EventObject )
|
-- @return #EVENT self
|
||||||
self:F3( { EventObject:GetClassNameAndID() } )
|
function EVENT:RemoveAll( EventClass )
|
||||||
|
|
||||||
local EventClass = EventObject:GetClassNameAndID()
|
local EventClassName = EventClass:GetClassNameAndID()
|
||||||
|
|
||||||
|
-- Get Event prio.
|
||||||
local EventPriority = EventClass:GetEventPriority()
|
local EventPriority = EventClass:GetEventPriority()
|
||||||
|
|
||||||
for EventID, EventData in pairs( self.Events ) do
|
for EventID, EventData in pairs( self.Events ) do
|
||||||
self.Events[EventID][EventPriority][EventClass] = nil
|
self.Events[EventID][EventPriority][EventClass] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- Create an OnDead event handler for a group
|
--- Create an OnDead event handler for a group
|
||||||
-- @param #EVENT self
|
-- @param #EVENT self
|
||||||
-- @param #table EventTemplate
|
-- @param #table EventTemplate
|
||||||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||||||
-- @param EventClass The instance of the class for which the event is.
|
-- @param EventClass The instance of the class for which the event is.
|
||||||
-- @param #function OnEventFunction
|
-- @param #function OnEventFunction
|
||||||
-- @return #EVENT
|
-- @return #EVENT self
|
||||||
function EVENT:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EventID )
|
function EVENT:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EventID )
|
||||||
self:F2( EventTemplate.name )
|
self:F2( EventTemplate.name )
|
||||||
|
|
||||||
@@ -636,7 +684,7 @@ function EVENT:OnEventForTemplate( EventTemplate, EventFunction, EventClass, Eve
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set a new listener for an S_EVENT_X event independent from a unit or a weapon.
|
--- Set a new listener for an `S_EVENT_X` event independent from a unit or a weapon.
|
||||||
-- @param #EVENT self
|
-- @param #EVENT self
|
||||||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||||||
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is captured. When the event happens, the event process will be called in this class provided.
|
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is captured. When the event happens, the event process will be called in this class provided.
|
||||||
@@ -651,14 +699,13 @@ function EVENT:OnEventGeneric( EventFunction, EventClass, EventID )
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Set a new listener for an `S_EVENT_X` event for a UNIT.
|
||||||
--- Set a new listener for an S_EVENT_X event for a UNIT.
|
|
||||||
-- @param #EVENT self
|
-- @param #EVENT self
|
||||||
-- @param #string UnitName The name of the UNIT.
|
-- @param #string UnitName The name of the UNIT.
|
||||||
-- @param #function EventFunction The function to be called when the event occurs for the GROUP.
|
-- @param #function EventFunction The function to be called when the event occurs for the GROUP.
|
||||||
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is.
|
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is.
|
||||||
-- @param EventID
|
-- @param EventID
|
||||||
-- @return #EVENT
|
-- @return #EVENT self
|
||||||
function EVENT:OnEventForUnit( UnitName, EventFunction, EventClass, EventID )
|
function EVENT:OnEventForUnit( UnitName, EventFunction, EventClass, EventID )
|
||||||
self:F2( UnitName )
|
self:F2( UnitName )
|
||||||
|
|
||||||
@@ -673,8 +720,9 @@ end
|
|||||||
-- @param #string GroupName The name of the GROUP.
|
-- @param #string GroupName The name of the GROUP.
|
||||||
-- @param #function EventFunction The function to be called when the event occurs for the GROUP.
|
-- @param #function EventFunction The function to be called when the event occurs for the GROUP.
|
||||||
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is.
|
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is.
|
||||||
-- @param EventID
|
-- @param #number EventID Event ID.
|
||||||
-- @return #EVENT
|
-- @param ... Optional arguments passed to the event function.
|
||||||
|
-- @return #EVENT self
|
||||||
function EVENT:OnEventForGroup( GroupName, EventFunction, EventClass, EventID, ... )
|
function EVENT:OnEventForGroup( GroupName, EventFunction, EventClass, EventID, ... )
|
||||||
|
|
||||||
local Event = self:Init( EventID, EventClass )
|
local Event = self:Init( EventID, EventClass )
|
||||||
@@ -691,7 +739,7 @@ do -- OnBirth
|
|||||||
-- @param Wrapper.Group#GROUP EventGroup
|
-- @param Wrapper.Group#GROUP EventGroup
|
||||||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||||||
-- @param EventClass The self instance of the class for which the event is.
|
-- @param EventClass The self instance of the class for which the event is.
|
||||||
-- @return #EVENT
|
-- @return #EVENT self
|
||||||
function EVENT:OnBirthForTemplate( EventTemplate, EventFunction, EventClass )
|
function EVENT:OnBirthForTemplate( EventTemplate, EventFunction, EventClass )
|
||||||
self:F2( EventTemplate.name )
|
self:F2( EventTemplate.name )
|
||||||
|
|
||||||
@@ -724,10 +772,10 @@ do -- OnDead
|
|||||||
|
|
||||||
--- Create an OnDead event handler for a group
|
--- Create an OnDead event handler for a group
|
||||||
-- @param #EVENT self
|
-- @param #EVENT self
|
||||||
-- @param Wrapper.Group#GROUP EventGroup
|
-- @param Wrapper.Group#GROUP EventGroup The GROUP object.
|
||||||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||||||
-- @param EventClass The self instance of the class for which the event is.
|
-- @param #table EventClass The self instance of the class for which the event is.
|
||||||
-- @return #EVENT
|
-- @return #EVENT self
|
||||||
function EVENT:OnDeadForTemplate( EventTemplate, EventFunction, EventClass )
|
function EVENT:OnDeadForTemplate( EventTemplate, EventFunction, EventClass )
|
||||||
self:F2( EventTemplate.name )
|
self:F2( EventTemplate.name )
|
||||||
|
|
||||||
@@ -738,14 +786,14 @@ do -- OnDead
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
do -- OnLand
|
do -- OnLand
|
||||||
|
|
||||||
--- Create an OnLand event handler for a group
|
--- Create an OnLand event handler for a group
|
||||||
-- @param #EVENT self
|
-- @param #EVENT self
|
||||||
-- @param #table EventTemplate
|
-- @param #table EventTemplate
|
||||||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||||||
-- @param EventClass The self instance of the class for which the event is.
|
-- @param #table EventClass The self instance of the class for which the event is.
|
||||||
-- @return #EVENT
|
-- @return #EVENT self
|
||||||
function EVENT:OnLandForTemplate( EventTemplate, EventFunction, EventClass )
|
function EVENT:OnLandForTemplate( EventTemplate, EventFunction, EventClass )
|
||||||
self:F2( EventTemplate.name )
|
self:F2( EventTemplate.name )
|
||||||
|
|
||||||
@@ -757,12 +805,13 @@ do -- OnLand
|
|||||||
end
|
end
|
||||||
|
|
||||||
do -- OnTakeOff
|
do -- OnTakeOff
|
||||||
|
|
||||||
--- Create an OnTakeOff event handler for a group
|
--- Create an OnTakeOff event handler for a group
|
||||||
-- @param #EVENT self
|
-- @param #EVENT self
|
||||||
-- @param #table EventTemplate
|
-- @param #table EventTemplate Template table.
|
||||||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||||||
-- @param EventClass The self instance of the class for which the event is.
|
-- @param #table EventClass The self instance of the class for which the event is.
|
||||||
-- @return #EVENT
|
-- @return #EVENT self
|
||||||
function EVENT:OnTakeOffForTemplate( EventTemplate, EventFunction, EventClass )
|
function EVENT:OnTakeOffForTemplate( EventTemplate, EventFunction, EventClass )
|
||||||
self:F2( EventTemplate.name )
|
self:F2( EventTemplate.name )
|
||||||
|
|
||||||
@@ -797,13 +846,13 @@ do -- Event Creation
|
|||||||
-- @param #EVENT self
|
-- @param #EVENT self
|
||||||
-- @param AI.AI_Cargo#AI_CARGO Cargo The Cargo created.
|
-- @param AI.AI_Cargo#AI_CARGO Cargo The Cargo created.
|
||||||
function EVENT:CreateEventNewCargo( Cargo )
|
function EVENT:CreateEventNewCargo( Cargo )
|
||||||
self:I( { Cargo } )
|
self:F( { Cargo } )
|
||||||
|
|
||||||
local Event = {
|
local Event = {
|
||||||
id = EVENTS.NewCargo,
|
id = EVENTS.NewCargo,
|
||||||
time = timer.getTime(),
|
time = timer.getTime(),
|
||||||
cargo = Cargo,
|
cargo = Cargo,
|
||||||
}
|
}
|
||||||
|
|
||||||
world.onEvent( Event )
|
world.onEvent( Event )
|
||||||
end
|
end
|
||||||
@@ -818,7 +867,7 @@ do -- Event Creation
|
|||||||
id = EVENTS.DeleteCargo,
|
id = EVENTS.DeleteCargo,
|
||||||
time = timer.getTime(),
|
time = timer.getTime(),
|
||||||
cargo = Cargo,
|
cargo = Cargo,
|
||||||
}
|
}
|
||||||
|
|
||||||
world.onEvent( Event )
|
world.onEvent( Event )
|
||||||
end
|
end
|
||||||
@@ -833,7 +882,7 @@ do -- Event Creation
|
|||||||
id = EVENTS.NewZone,
|
id = EVENTS.NewZone,
|
||||||
time = timer.getTime(),
|
time = timer.getTime(),
|
||||||
zone = Zone,
|
zone = Zone,
|
||||||
}
|
}
|
||||||
|
|
||||||
world.onEvent( Event )
|
world.onEvent( Event )
|
||||||
end
|
end
|
||||||
@@ -848,7 +897,7 @@ do -- Event Creation
|
|||||||
id = EVENTS.DeleteZone,
|
id = EVENTS.DeleteZone,
|
||||||
time = timer.getTime(),
|
time = timer.getTime(),
|
||||||
zone = Zone,
|
zone = Zone,
|
||||||
}
|
}
|
||||||
|
|
||||||
world.onEvent( Event )
|
world.onEvent( Event )
|
||||||
end
|
end
|
||||||
@@ -863,12 +912,11 @@ do -- Event Creation
|
|||||||
id = EVENTS.NewZoneGoal,
|
id = EVENTS.NewZoneGoal,
|
||||||
time = timer.getTime(),
|
time = timer.getTime(),
|
||||||
ZoneGoal = ZoneGoal,
|
ZoneGoal = ZoneGoal,
|
||||||
}
|
}
|
||||||
|
|
||||||
world.onEvent( Event )
|
world.onEvent( Event )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Creation of a ZoneGoal Deletion Event.
|
--- Creation of a ZoneGoal Deletion Event.
|
||||||
-- @param #EVENT self
|
-- @param #EVENT self
|
||||||
-- @param Core.ZoneGoal#ZONE_GOAL ZoneGoal The ZoneGoal created.
|
-- @param Core.ZoneGoal#ZONE_GOAL ZoneGoal The ZoneGoal created.
|
||||||
@@ -879,12 +927,11 @@ do -- Event Creation
|
|||||||
id = EVENTS.DeleteZoneGoal,
|
id = EVENTS.DeleteZoneGoal,
|
||||||
time = timer.getTime(),
|
time = timer.getTime(),
|
||||||
ZoneGoal = ZoneGoal,
|
ZoneGoal = ZoneGoal,
|
||||||
}
|
}
|
||||||
|
|
||||||
world.onEvent( Event )
|
world.onEvent( Event )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Creation of a S_EVENT_PLAYER_ENTER_UNIT Event.
|
--- Creation of a S_EVENT_PLAYER_ENTER_UNIT Event.
|
||||||
-- @param #EVENT self
|
-- @param #EVENT self
|
||||||
-- @param Wrapper.Unit#UNIT PlayerUnit.
|
-- @param Wrapper.Unit#UNIT PlayerUnit.
|
||||||
@@ -894,16 +941,32 @@ do -- Event Creation
|
|||||||
local Event = {
|
local Event = {
|
||||||
id = EVENTS.PlayerEnterUnit,
|
id = EVENTS.PlayerEnterUnit,
|
||||||
time = timer.getTime(),
|
time = timer.getTime(),
|
||||||
initiator = PlayerUnit:GetDCSObject()
|
initiator = PlayerUnit:GetDCSObject(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
world.onEvent( Event )
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Creation of a S_EVENT_PLAYER_ENTER_AIRCRAFT event.
|
||||||
|
-- @param #EVENT self
|
||||||
|
-- @param Wrapper.Unit#UNIT PlayerUnit The aircraft unit the player entered.
|
||||||
|
function EVENT:CreateEventPlayerEnterAircraft( PlayerUnit )
|
||||||
|
self:F( { PlayerUnit } )
|
||||||
|
|
||||||
|
local Event = {
|
||||||
|
id = EVENTS.PlayerEnterAircraft,
|
||||||
|
time = timer.getTime(),
|
||||||
|
initiator = PlayerUnit:GetDCSObject(),
|
||||||
|
}
|
||||||
|
|
||||||
world.onEvent( Event )
|
world.onEvent( Event )
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param #EVENT self
|
--- Main event function.
|
||||||
-- @param #EVENTDATA Event
|
-- @param #EVENT self
|
||||||
|
-- @param #EVENTDATA Event Event data table.
|
||||||
function EVENT:onEvent( Event )
|
function EVENT:onEvent( Event )
|
||||||
|
|
||||||
local ErrorHandler = function( errmsg )
|
local ErrorHandler = function( errmsg )
|
||||||
@@ -916,18 +979,13 @@ function EVENT:onEvent( Event )
|
|||||||
return errmsg
|
return errmsg
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- Get event meta data.
|
-- Get event meta data.
|
||||||
local EventMeta = _EVENTMETA[Event.id]
|
local EventMeta = _EVENTMETA[Event.id]
|
||||||
|
|
||||||
-- Check if this is a known event?
|
-- Check if this is a known event?
|
||||||
if EventMeta then
|
if EventMeta then
|
||||||
|
|
||||||
if self and
|
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
|
||||||
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
|
if Event.id and Event.id == EVENTS.MissionEnd then
|
||||||
self.MissionEnd = true
|
self.MissionEnd = true
|
||||||
@@ -951,9 +1009,9 @@ function EVENT:onEvent( Event )
|
|||||||
if Event.IniDCSGroup and Event.IniDCSGroup:isExist() then
|
if Event.IniDCSGroup and Event.IniDCSGroup:isExist() then
|
||||||
Event.IniDCSGroupName = Event.IniDCSGroup:getName()
|
Event.IniDCSGroupName = Event.IniDCSGroup:getName()
|
||||||
Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName )
|
Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName )
|
||||||
--if Event.IniGroup then
|
-- if Event.IniGroup then
|
||||||
Event.IniGroupName = Event.IniDCSGroupName
|
Event.IniGroupName = Event.IniDCSGroupName
|
||||||
--end
|
-- end
|
||||||
end
|
end
|
||||||
Event.IniPlayerName = Event.IniDCSUnit:getPlayerName()
|
Event.IniPlayerName = Event.IniDCSUnit:getPlayerName()
|
||||||
Event.IniCoalition = Event.IniDCSUnit:getCoalition()
|
Event.IniCoalition = Event.IniDCSUnit:getCoalition()
|
||||||
@@ -962,16 +1020,25 @@ function EVENT:onEvent( Event )
|
|||||||
end
|
end
|
||||||
|
|
||||||
if Event.IniObjectCategory == Object.Category.STATIC then
|
if Event.IniObjectCategory == Object.Category.STATIC then
|
||||||
if Event.id==31 then
|
|
||||||
--env.info("FF event 31")
|
if Event.id == 31 then
|
||||||
-- Event.initiator is a Static object representing the pilot. But getName() error due to DCS bug.
|
|
||||||
|
-- Event.initiator is a Static object representing the pilot. But getName() errors due to DCS bug.
|
||||||
Event.IniDCSUnit = Event.initiator
|
Event.IniDCSUnit = Event.initiator
|
||||||
local ID=Event.initiator.id_
|
local ID = Event.initiator.id_
|
||||||
Event.IniDCSUnitName = string.format("Ejected Pilot ID %s", tostring(ID))
|
Event.IniDCSUnitName = string.format( "Ejected Pilot ID %s", tostring( ID ) )
|
||||||
Event.IniUnitName = Event.IniDCSUnitName
|
Event.IniUnitName = Event.IniDCSUnitName
|
||||||
Event.IniCoalition = 0
|
Event.IniCoalition = 0
|
||||||
Event.IniCategory = 0
|
Event.IniCategory = 0
|
||||||
Event.IniTypeName = "Ejected Pilot"
|
Event.IniTypeName = "Ejected Pilot"
|
||||||
|
elseif Event.id == 33 then -- ejection seat discarded
|
||||||
|
Event.IniDCSUnit = Event.initiator
|
||||||
|
local ID = Event.initiator.id_
|
||||||
|
Event.IniDCSUnitName = string.format( "Ejection Seat ID %s", tostring( ID ) )
|
||||||
|
Event.IniUnitName = Event.IniDCSUnitName
|
||||||
|
Event.IniCoalition = 0
|
||||||
|
Event.IniCategory = 0
|
||||||
|
Event.IniTypeName = "Ejection Seat"
|
||||||
else
|
else
|
||||||
Event.IniDCSUnit = Event.initiator
|
Event.IniDCSUnit = Event.initiator
|
||||||
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
||||||
@@ -1006,7 +1073,7 @@ function EVENT:onEvent( Event )
|
|||||||
Event.IniDCSUnit = Event.initiator
|
Event.IniDCSUnit = Event.initiator
|
||||||
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
||||||
Event.IniUnitName = Event.IniDCSUnitName
|
Event.IniUnitName = Event.IniDCSUnitName
|
||||||
Event.IniUnit = AIRBASE:FindByName(Event.IniDCSUnitName)
|
Event.IniUnit = AIRBASE:FindByName( Event.IniDCSUnitName )
|
||||||
Event.IniCoalition = Event.IniDCSUnit:getCoalition()
|
Event.IniCoalition = Event.IniDCSUnit:getCoalition()
|
||||||
Event.IniCategory = Event.IniDCSUnit:getDesc().category
|
Event.IniCategory = Event.IniDCSUnit:getDesc().category
|
||||||
Event.IniTypeName = Event.IniDCSUnit:getTypeName()
|
Event.IniTypeName = Event.IniDCSUnit:getTypeName()
|
||||||
@@ -1027,9 +1094,9 @@ function EVENT:onEvent( Event )
|
|||||||
if Event.TgtDCSGroup and Event.TgtDCSGroup:isExist() then
|
if Event.TgtDCSGroup and Event.TgtDCSGroup:isExist() then
|
||||||
Event.TgtDCSGroupName = Event.TgtDCSGroup:getName()
|
Event.TgtDCSGroupName = Event.TgtDCSGroup:getName()
|
||||||
Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName )
|
Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName )
|
||||||
--if Event.TgtGroup then
|
-- if Event.TgtGroup then
|
||||||
Event.TgtGroupName = Event.TgtDCSGroupName
|
Event.TgtGroupName = Event.TgtDCSGroupName
|
||||||
--end
|
-- end
|
||||||
end
|
end
|
||||||
Event.TgtPlayerName = Event.TgtDCSUnit:getPlayerName()
|
Event.TgtPlayerName = Event.TgtDCSUnit:getPlayerName()
|
||||||
Event.TgtCoalition = Event.TgtDCSUnit:getCoalition()
|
Event.TgtCoalition = Event.TgtDCSUnit:getCoalition()
|
||||||
@@ -1038,13 +1105,33 @@ function EVENT:onEvent( Event )
|
|||||||
end
|
end
|
||||||
|
|
||||||
if Event.TgtObjectCategory == Object.Category.STATIC then
|
if Event.TgtObjectCategory == Object.Category.STATIC then
|
||||||
|
-- get base data
|
||||||
Event.TgtDCSUnit = Event.target
|
Event.TgtDCSUnit = Event.target
|
||||||
Event.TgtDCSUnitName = Event.TgtDCSUnit:getName()
|
if Event.target:isExist() and Event.id ~= 33 then -- leave out ejected seat object
|
||||||
Event.TgtUnitName = Event.TgtDCSUnitName
|
Event.TgtDCSUnitName = Event.TgtDCSUnit:getName()
|
||||||
Event.TgtUnit = STATIC:FindByName( Event.TgtDCSUnitName, false )
|
Event.TgtUnitName = Event.TgtDCSUnitName
|
||||||
Event.TgtCoalition = Event.TgtDCSUnit:getCoalition()
|
Event.TgtUnit = STATIC:FindByName( Event.TgtDCSUnitName, false )
|
||||||
Event.TgtCategory = Event.TgtDCSUnit:getDesc().category
|
Event.TgtCoalition = Event.TgtDCSUnit:getCoalition()
|
||||||
Event.TgtTypeName = Event.TgtDCSUnit:getTypeName()
|
Event.TgtCategory = Event.TgtDCSUnit:getDesc().category
|
||||||
|
Event.TgtTypeName = Event.TgtDCSUnit:getTypeName()
|
||||||
|
else
|
||||||
|
Event.TgtDCSUnitName = string.format( "No target object for Event ID %s", tostring( Event.id ) )
|
||||||
|
Event.TgtUnitName = Event.TgtDCSUnitName
|
||||||
|
Event.TgtUnit = nil
|
||||||
|
Event.TgtCoalition = 0
|
||||||
|
Event.TgtCategory = 0
|
||||||
|
if Event.id == 6 then
|
||||||
|
Event.TgtTypeName = "Ejected Pilot"
|
||||||
|
Event.TgtDCSUnitName = string.format( "Ejected Pilot ID %s", tostring( Event.IniDCSUnitName ) )
|
||||||
|
Event.TgtUnitName = Event.TgtDCSUnitName
|
||||||
|
elseif Event.id == 33 then
|
||||||
|
Event.TgtTypeName = "Ejection Seat"
|
||||||
|
Event.TgtDCSUnitName = string.format( "Ejection Seat ID %s", tostring( Event.IniDCSUnitName ) )
|
||||||
|
Event.TgtUnitName = Event.TgtDCSUnitName
|
||||||
|
else
|
||||||
|
Event.TgtTypeName = "Static"
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if Event.TgtObjectCategory == Object.Category.SCENERY then
|
if Event.TgtObjectCategory == Object.Category.SCENERY then
|
||||||
@@ -1065,29 +1152,29 @@ function EVENT:onEvent( Event )
|
|||||||
Event.WeaponCoalition = Event.WeaponUNIT and Event.Weapon:getCoalition()
|
Event.WeaponCoalition = Event.WeaponUNIT and Event.Weapon:getCoalition()
|
||||||
Event.WeaponCategory = Event.WeaponUNIT and Event.Weapon:getDesc().category
|
Event.WeaponCategory = Event.WeaponUNIT and Event.Weapon:getDesc().category
|
||||||
Event.WeaponTypeName = Event.WeaponUNIT and Event.Weapon:getTypeName()
|
Event.WeaponTypeName = Event.WeaponUNIT and Event.Weapon:getTypeName()
|
||||||
--Event.WeaponTgtDCSUnit = Event.Weapon:getTarget()
|
-- Event.WeaponTgtDCSUnit = Event.Weapon:getTarget()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Place should be given for takeoff and landing events as well as base captured. It should be a DCS airbase.
|
-- 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.place then
|
||||||
if Event.id==EVENTS.LandingAfterEjection then
|
if Event.id == EVENTS.LandingAfterEjection then
|
||||||
-- Place is here the UNIT of which the pilot ejected.
|
-- Place is here the UNIT of which the pilot ejected.
|
||||||
--local name=Event.place:getName() -- This returns a DCS error "Airbase doesn't exit" :(
|
-- 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.
|
-- 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)
|
-- Event.Place=UNIT:Find(Event.place)
|
||||||
else
|
else
|
||||||
Event.Place=AIRBASE:Find(Event.place)
|
Event.Place = AIRBASE:Find( Event.place )
|
||||||
Event.PlaceName=Event.Place:GetName()
|
Event.PlaceName = Event.Place:GetName()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Mark points.
|
-- Mark points.
|
||||||
if Event.idx then
|
if Event.idx then
|
||||||
Event.MarkID=Event.idx
|
Event.MarkID = Event.idx
|
||||||
Event.MarkVec3=Event.pos
|
Event.MarkVec3 = Event.pos
|
||||||
Event.MarkCoordinate=COORDINATE:NewFromVec3(Event.pos)
|
Event.MarkCoordinate = COORDINATE:NewFromVec3( Event.pos )
|
||||||
Event.MarkText=Event.text
|
Event.MarkText = Event.text
|
||||||
Event.MarkCoalition=Event.coalition
|
Event.MarkCoalition = Event.coalition
|
||||||
Event.MarkGroupID = Event.groupID
|
Event.MarkGroupID = Event.groupID
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1116,9 +1203,9 @@ function EVENT:onEvent( Event )
|
|||||||
-- Okay, we got the event from DCS. Now loop the SORTED self.EventSorted[] table for the received Event.id, and for each EventData registered, check if a function needs to be called.
|
-- 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
|
for EventClass, EventData in pairs( self.Events[Event.id][EventPriority] ) do
|
||||||
|
|
||||||
--if Event.IniObjectCategory ~= Object.Category.STATIC then
|
-- if Event.IniObjectCategory ~= Object.Category.STATIC then
|
||||||
-- self:E( { "Evaluating: ", EventClass:GetClassNameAndID() } )
|
-- self:E( { "Evaluating: ", EventClass:GetClassNameAndID() } )
|
||||||
--end
|
-- end
|
||||||
|
|
||||||
Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName )
|
Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName )
|
||||||
Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName )
|
Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName )
|
||||||
@@ -1135,8 +1222,8 @@ function EVENT:onEvent( Event )
|
|||||||
|
|
||||||
local UnitName = EventClass:GetName()
|
local UnitName = EventClass:GetName()
|
||||||
|
|
||||||
if ( EventMeta.Side == "I" and UnitName == Event.IniDCSUnitName ) or
|
if (EventMeta.Side == "I" and UnitName == Event.IniDCSUnitName) or
|
||||||
( EventMeta.Side == "T" and UnitName == Event.TgtDCSUnitName ) then
|
(EventMeta.Side == "T" and UnitName == Event.TgtDCSUnitName) then
|
||||||
|
|
||||||
-- First test if a EventFunction is Set, otherwise search for the default function
|
-- First test if a EventFunction is Set, otherwise search for the default function
|
||||||
if EventData.EventFunction then
|
if EventData.EventFunction then
|
||||||
@@ -1145,15 +1232,14 @@ function EVENT:onEvent( Event )
|
|||||||
self:F( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } )
|
self:F( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } )
|
||||||
end
|
end
|
||||||
|
|
||||||
local Result, Value = xpcall(
|
local Result, Value = xpcall( function()
|
||||||
function()
|
return EventData.EventFunction( EventClass, Event )
|
||||||
return EventData.EventFunction( EventClass, Event )
|
end, ErrorHandler )
|
||||||
end, ErrorHandler )
|
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
-- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object.
|
-- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object.
|
||||||
local EventFunction = EventClass[ EventMeta.Event ]
|
local EventFunction = EventClass[EventMeta.Event]
|
||||||
if EventFunction and type( EventFunction ) == "function" then
|
if EventFunction and type( EventFunction ) == "function" then
|
||||||
|
|
||||||
-- Now call the default event function.
|
-- Now call the default event function.
|
||||||
@@ -1161,10 +1247,9 @@ function EVENT:onEvent( Event )
|
|||||||
self:F( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
self:F( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||||
end
|
end
|
||||||
|
|
||||||
local Result, Value = xpcall(
|
local Result, Value = xpcall( function()
|
||||||
function()
|
return EventFunction( EventClass, Event )
|
||||||
return EventFunction( EventClass, Event )
|
end, ErrorHandler )
|
||||||
end, ErrorHandler )
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -1188,8 +1273,8 @@ function EVENT:onEvent( Event )
|
|||||||
-- We can get the name of the EventClass, which is now always a GROUP object.
|
-- We can get the name of the EventClass, which is now always a GROUP object.
|
||||||
local GroupName = EventClass:GetName()
|
local GroupName = EventClass:GetName()
|
||||||
|
|
||||||
if ( EventMeta.Side == "I" and GroupName == Event.IniDCSGroupName ) or
|
if (EventMeta.Side == "I" and GroupName == Event.IniDCSGroupName) or
|
||||||
( EventMeta.Side == "T" and GroupName == Event.TgtDCSGroupName ) then
|
(EventMeta.Side == "T" and GroupName == Event.TgtDCSGroupName) then
|
||||||
|
|
||||||
-- First test if a EventFunction is Set, otherwise search for the default function
|
-- First test if a EventFunction is Set, otherwise search for the default function
|
||||||
if EventData.EventFunction then
|
if EventData.EventFunction then
|
||||||
@@ -1198,15 +1283,14 @@ function EVENT:onEvent( Event )
|
|||||||
self:F( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } )
|
self:F( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } )
|
||||||
end
|
end
|
||||||
|
|
||||||
local Result, Value = xpcall(
|
local Result, Value = xpcall( function()
|
||||||
function()
|
return EventData.EventFunction( EventClass, Event, unpack( EventData.Params ) )
|
||||||
return EventData.EventFunction( EventClass, Event, unpack( EventData.Params ) )
|
end, ErrorHandler )
|
||||||
end, ErrorHandler )
|
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
-- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object.
|
-- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object.
|
||||||
local EventFunction = EventClass[ EventMeta.Event ]
|
local EventFunction = EventClass[EventMeta.Event]
|
||||||
if EventFunction and type( EventFunction ) == "function" then
|
if EventFunction and type( EventFunction ) == "function" then
|
||||||
|
|
||||||
-- Now call the default event function.
|
-- Now call the default event function.
|
||||||
@@ -1214,16 +1298,15 @@ function EVENT:onEvent( Event )
|
|||||||
self:F( { "Calling " .. EventMeta.Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } )
|
self:F( { "Calling " .. EventMeta.Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||||
end
|
end
|
||||||
|
|
||||||
local Result, Value = xpcall(
|
local Result, Value = xpcall( function()
|
||||||
function()
|
return EventFunction( EventClass, Event, unpack( EventData.Params ) )
|
||||||
return EventFunction( EventClass, Event, unpack( EventData.Params ) )
|
end, ErrorHandler )
|
||||||
end, ErrorHandler )
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
-- The EventClass is not alive anymore, we remove it from the EventHandlers...
|
-- The EventClass is not alive anymore, we remove it from the EventHandlers...
|
||||||
--self:RemoveEvent( EventClass, Event.id )
|
-- self:RemoveEvent( EventClass, Event.id )
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
|
||||||
@@ -1238,14 +1321,13 @@ function EVENT:onEvent( Event )
|
|||||||
if Event.IniObjectCategory ~= 3 then
|
if Event.IniObjectCategory ~= 3 then
|
||||||
self:F2( { "Calling EventFunction for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
self:F2( { "Calling EventFunction for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||||
end
|
end
|
||||||
local Result, Value = xpcall(
|
local Result, Value = xpcall( function()
|
||||||
function()
|
return EventData.EventFunction( EventClass, Event )
|
||||||
return EventData.EventFunction( EventClass, Event )
|
end, ErrorHandler )
|
||||||
end, ErrorHandler )
|
|
||||||
else
|
else
|
||||||
|
|
||||||
-- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object.
|
-- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object.
|
||||||
local EventFunction = EventClass[ EventMeta.Event ]
|
local EventFunction = EventClass[EventMeta.Event]
|
||||||
if EventFunction and type( EventFunction ) == "function" then
|
if EventFunction and type( EventFunction ) == "function" then
|
||||||
|
|
||||||
-- Now call the default event function.
|
-- Now call the default event function.
|
||||||
@@ -1253,11 +1335,10 @@ function EVENT:onEvent( Event )
|
|||||||
self:F2( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
self:F2( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
||||||
end
|
end
|
||||||
|
|
||||||
local Result, Value = xpcall(
|
local Result, Value = xpcall( function()
|
||||||
function()
|
local Result, Value = EventFunction( EventClass, Event )
|
||||||
local Result, Value = EventFunction( EventClass, Event )
|
return Result, Value
|
||||||
return Result, Value
|
end, ErrorHandler )
|
||||||
end, ErrorHandler )
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1281,13 +1362,13 @@ function EVENT:onEvent( Event )
|
|||||||
self:T( { EventMeta.Text, Event } )
|
self:T( { EventMeta.Text, Event } )
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
self:E(string.format("WARNING: Could not get EVENTMETA data for event ID=%d! Is this an unknown/new DCS event?", tostring(Event.id)))
|
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
|
end
|
||||||
|
|
||||||
Event = nil
|
Event = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
--- The EVENTHANDLER structure
|
--- The EVENTHANDLER structure.
|
||||||
-- @type EVENTHANDLER
|
-- @type EVENTHANDLER
|
||||||
-- @extends Core.Base#BASE
|
-- @extends Core.Base#BASE
|
||||||
EVENTHANDLER = {
|
EVENTHANDLER = {
|
||||||
@@ -1295,7 +1376,7 @@ EVENTHANDLER = {
|
|||||||
ClassID = 0,
|
ClassID = 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
--- The EVENTHANDLER constructor
|
--- The EVENTHANDLER constructor.
|
||||||
-- @param #EVENTHANDLER self
|
-- @param #EVENTHANDLER self
|
||||||
-- @return #EVENTHANDLER self
|
-- @return #EVENTHANDLER self
|
||||||
function EVENTHANDLER:New()
|
function EVENTHANDLER:New()
|
||||||
|
|||||||
@@ -53,7 +53,7 @@
|
|||||||
--
|
--
|
||||||
-- Detailed explanations and API specifics are further below clarified and FSM derived class specifics are described in those class documentation sections.
|
-- Detailed explanations and API specifics are further below clarified and FSM derived class specifics are described in those class documentation sections.
|
||||||
--
|
--
|
||||||
-- ##__Dislaimer:__
|
-- ##__Disclaimer:__
|
||||||
-- The FSM class development is based on a finite state machine implementation made by Conroy Kyle.
|
-- The FSM class development is based on a finite state machine implementation made by Conroy Kyle.
|
||||||
-- The state machine can be found on [github](https://github.com/kyleconroy/lua-state-machine)
|
-- The state machine can be found on [github](https://github.com/kyleconroy/lua-state-machine)
|
||||||
-- I've reworked this development (taken the concept), and created a **hierarchical state machine** out of it, embedded within the DCS simulator.
|
-- I've reworked this development (taken the concept), and created a **hierarchical state machine** out of it, embedded within the DCS simulator.
|
||||||
@@ -69,7 +69,6 @@
|
|||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
--
|
|
||||||
-- ### Author: **FlightControl**
|
-- ### Author: **FlightControl**
|
||||||
-- ### Contributions: **funkyfranky**
|
-- ### Contributions: **funkyfranky**
|
||||||
--
|
--
|
||||||
@@ -89,7 +88,6 @@ do -- FSM
|
|||||||
-- @field #string current Current state name.
|
-- @field #string current Current state name.
|
||||||
-- @extends Core.Base#BASE
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
|
|
||||||
--- A Finite State Machine (FSM) models a process flow that transitions between various **States** through triggered **Events**.
|
--- A Finite State Machine (FSM) models a process flow that transitions between various **States** through triggered **Events**.
|
||||||
--
|
--
|
||||||
-- A FSM can only be in one of a finite number of states.
|
-- A FSM can only be in one of a finite number of states.
|
||||||
@@ -145,12 +143,12 @@ do -- FSM
|
|||||||
-- Most of the time, these Event Triggers are used within the Transition Handler methods, so that a workflow is created running through the state machine.
|
-- Most of the time, these Event Triggers are used within the Transition Handler methods, so that a workflow is created running through the state machine.
|
||||||
--
|
--
|
||||||
-- As explained above, a FSM supports **Linear State Transitions** and **Hierarchical State Transitions**, and both can be mixed to make a comprehensive FSM implementation.
|
-- As explained above, a FSM supports **Linear State Transitions** and **Hierarchical State Transitions**, and both can be mixed to make a comprehensive FSM implementation.
|
||||||
-- The below documentation has a seperate chapter explaining both transition modes, taking into account the **Transition Rules**, **Transition Handlers** and **Event Triggers**.
|
-- The below documentation has a separate chapter explaining both transition modes, taking into account the **Transition Rules**, **Transition Handlers** and **Event Triggers**.
|
||||||
--
|
--
|
||||||
-- ## FSM Linear Transitions
|
-- ## FSM Linear Transitions
|
||||||
--
|
--
|
||||||
-- Linear Transitions are Transition Rules allowing an FSM to transition from one or multiple possible **From** state(s) towards a **To** state upon a Triggered **Event**.
|
-- Linear Transitions are Transition Rules allowing an FSM to transition from one or multiple possible **From** state(s) towards a **To** state upon a Triggered **Event**.
|
||||||
-- The Lineair transition rule evaluation will always be done from the **current state** of the FSM.
|
-- The Linear transition rule evaluation will always be done from the **current state** of the FSM.
|
||||||
-- If no valid Transition Rule can be found in the FSM, the FSM will log an error and stop.
|
-- If no valid Transition Rule can be found in the FSM, the FSM will log an error and stop.
|
||||||
--
|
--
|
||||||
-- ### FSM Transition Rules
|
-- ### FSM Transition Rules
|
||||||
@@ -185,7 +183,7 @@ do -- FSM
|
|||||||
-- * The From states can be **a table of strings**, indicating that the transition rule will be valid **if the current state** of the FSM will be **one of the given From states**.
|
-- * The From states can be **a table of strings**, indicating that the transition rule will be valid **if the current state** of the FSM will be **one of the given From states**.
|
||||||
-- * The From state can be a **"*"**, indicating that **the transition rule will always be valid**, regardless of the current state of the FSM.
|
-- * The From state can be a **"*"**, indicating that **the transition rule will always be valid**, regardless of the current state of the FSM.
|
||||||
--
|
--
|
||||||
-- The below code snippet shows how the two last lines can be rewritten and consensed.
|
-- The below code snippet shows how the two last lines can be rewritten and condensed.
|
||||||
--
|
--
|
||||||
-- FsmSwitch:AddTransition( { "On", "Middle" }, "SwitchOff", "Off" )
|
-- FsmSwitch:AddTransition( { "On", "Middle" }, "SwitchOff", "Off" )
|
||||||
--
|
--
|
||||||
@@ -221,7 +219,7 @@ do -- FSM
|
|||||||
-- * The method **FSM:Event()** triggers an Event that will be processed **synchronously** or **immediately**.
|
-- * The method **FSM:Event()** triggers an Event that will be processed **synchronously** or **immediately**.
|
||||||
-- * The method **FSM:__Event( __seconds__ )** triggers an Event that will be processed **asynchronously** over time, waiting __x seconds__.
|
-- * The method **FSM:__Event( __seconds__ )** triggers an Event that will be processed **asynchronously** over time, waiting __x seconds__.
|
||||||
--
|
--
|
||||||
-- The destinction between these 2 Event Trigger methods are important to understand. An asynchronous call will "log" the Event Trigger to be executed at a later time.
|
-- The distinction between these 2 Event Trigger methods are important to understand. An asynchronous call will "log" the Event Trigger to be executed at a later time.
|
||||||
-- Processing will just continue. Synchronous Event Trigger methods are useful to change states of the FSM immediately, but may have a larger processing impact.
|
-- Processing will just continue. Synchronous Event Trigger methods are useful to change states of the FSM immediately, but may have a larger processing impact.
|
||||||
--
|
--
|
||||||
-- The following example provides a little demonstration on the difference between synchronous and asynchronous Event Triggering.
|
-- The following example provides a little demonstration on the difference between synchronous and asynchronous Event Triggering.
|
||||||
@@ -345,7 +343,6 @@ do -- FSM
|
|||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- @field #FSM
|
-- @field #FSM
|
||||||
--
|
|
||||||
FSM = {
|
FSM = {
|
||||||
ClassName = "FSM",
|
ClassName = "FSM",
|
||||||
}
|
}
|
||||||
@@ -379,7 +376,6 @@ do -- FSM
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Sets the start state of the FSM.
|
--- Sets the start state of the FSM.
|
||||||
-- @param #FSM self
|
-- @param #FSM self
|
||||||
-- @param #string State A string defining the start state.
|
-- @param #string State A string defining the start state.
|
||||||
@@ -388,7 +384,6 @@ do -- FSM
|
|||||||
self.current = State
|
self.current = State
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Returns the start state of the FSM.
|
--- Returns the start state of the FSM.
|
||||||
-- @param #FSM self
|
-- @param #FSM self
|
||||||
-- @return #string A string containing the start state.
|
-- @return #string A string containing the start state.
|
||||||
@@ -416,7 +411,6 @@ do -- FSM
|
|||||||
self:_eventmap( self.Events, Transition )
|
self:_eventmap( self.Events, Transition )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Returns a table of the transition rules defined within the FSM.
|
--- Returns a table of the transition rules defined within the FSM.
|
||||||
-- @param #FSM self
|
-- @param #FSM self
|
||||||
-- @return #table Transitions.
|
-- @return #table Transitions.
|
||||||
@@ -450,7 +444,6 @@ do -- FSM
|
|||||||
return Process
|
return Process
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Returns a table of the SubFSM rules defined within the FSM.
|
--- Returns a table of the SubFSM rules defined within the FSM.
|
||||||
-- @param #FSM self
|
-- @param #FSM self
|
||||||
-- @return #table Sub processes.
|
-- @return #table Sub processes.
|
||||||
@@ -499,7 +492,6 @@ do -- FSM
|
|||||||
return self._EndStates or {}
|
return self._EndStates or {}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Adds a score for the FSM to be achieved.
|
--- Adds a score for the FSM to be achieved.
|
||||||
-- @param #FSM self
|
-- @param #FSM self
|
||||||
-- @param #string State is the state of the process when the score needs to be given. (See the relevant state descriptions of the process).
|
-- @param #string State is the state of the process when the score needs to be given. (See the relevant state descriptions of the process).
|
||||||
@@ -540,7 +532,7 @@ do -- FSM
|
|||||||
|
|
||||||
--- Returns a table with the scores defined.
|
--- Returns a table with the scores defined.
|
||||||
-- @param #FSM self
|
-- @param #FSM self
|
||||||
-- @param #table Scores.
|
-- @return #table Scores.
|
||||||
function FSM:GetScores()
|
function FSM:GetScores()
|
||||||
return self._Scores or {}
|
return self._Scores or {}
|
||||||
end
|
end
|
||||||
@@ -563,27 +555,27 @@ do -- FSM
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Event map.
|
--- Event map.
|
||||||
-- @param #FSM self
|
-- @param #FSM self
|
||||||
-- @param #table Events Events.
|
-- @param #table Events Events.
|
||||||
-- @param #table EventStructure Event structure.
|
-- @param #table EventStructure Event structure.
|
||||||
function FSM:_eventmap( Events, EventStructure )
|
function FSM:_eventmap( Events, EventStructure )
|
||||||
|
|
||||||
local Event = EventStructure.Event
|
local Event = EventStructure.Event
|
||||||
local __Event = "__" .. EventStructure.Event
|
local __Event = "__" .. EventStructure.Event
|
||||||
|
|
||||||
self[Event] = self[Event] or self:_create_transition(Event)
|
self[Event] = self[Event] or self:_create_transition( Event )
|
||||||
self[__Event] = self[__Event] or self:_delayed_transition(Event)
|
self[__Event] = self[__Event] or self:_delayed_transition( Event )
|
||||||
|
|
||||||
-- Debug message.
|
-- Debug message.
|
||||||
self:T2( "Added methods: " .. Event .. ", " .. __Event )
|
self:T2( "Added methods: " .. Event .. ", " .. __Event )
|
||||||
|
|
||||||
Events[Event] = self.Events[Event] or { map = {} }
|
Events[Event] = self.Events[Event] or { map = {} }
|
||||||
self:_add_to_map( Events[Event].map, EventStructure )
|
self:_add_to_map( Events[Event].map, EventStructure )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Sub maps.
|
--- Sub maps.
|
||||||
-- @param #FSM self
|
-- @param #FSM self
|
||||||
-- @param #table subs Subs.
|
-- @param #table subs Subs.
|
||||||
-- @param #table sub Sub.
|
-- @param #table sub Sub.
|
||||||
@@ -613,7 +605,7 @@ do -- FSM
|
|||||||
-- @param #string EventName Event name.
|
-- @param #string EventName Event name.
|
||||||
-- @return Value.
|
-- @return Value.
|
||||||
function FSM:_call_handler( step, trigger, params, EventName )
|
function FSM:_call_handler( step, trigger, params, EventName )
|
||||||
--env.info(string.format("FF T=%.3f _call_handler step=%s, trigger=%s, event=%s", timer.getTime(), step, trigger, EventName))
|
-- env.info(string.format("FF T=%.3f _call_handler step=%s, trigger=%s, event=%s", timer.getTime(), step, trigger, EventName))
|
||||||
|
|
||||||
local handler = step .. trigger
|
local handler = step .. trigger
|
||||||
|
|
||||||
@@ -644,10 +636,12 @@ do -- FSM
|
|||||||
return errmsg
|
return errmsg
|
||||||
end
|
end
|
||||||
|
|
||||||
--return self[handler](self, unpack( params ))
|
-- return self[handler](self, unpack( params ))
|
||||||
|
|
||||||
-- Protected call.
|
-- Protected call.
|
||||||
local Result, Value = xpcall( function() return self[handler]( self, unpack( params ) ) end, ErrorHandler )
|
local Result, Value = xpcall( function()
|
||||||
|
return self[handler]( self, unpack( params ) )
|
||||||
|
end, ErrorHandler )
|
||||||
return Value
|
return Value
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -671,17 +665,16 @@ do -- FSM
|
|||||||
local From = self.current
|
local From = self.current
|
||||||
|
|
||||||
-- Parameters.
|
-- Parameters.
|
||||||
local Params = { From, EventName, To, ... }
|
local Params = { From, EventName, To, ... }
|
||||||
|
|
||||||
|
if self["onleave" .. From] or
|
||||||
if self["onleave".. From] or
|
self["OnLeave" .. From] or
|
||||||
self["OnLeave".. From] or
|
self["onbefore" .. EventName] or
|
||||||
self["onbefore".. EventName] or
|
self["OnBefore" .. EventName] or
|
||||||
self["OnBefore".. EventName] or
|
self["onafter" .. EventName] or
|
||||||
self["onafter".. EventName] or
|
self["OnAfter" .. EventName] or
|
||||||
self["OnAfter".. EventName] or
|
self["onenter" .. To] or
|
||||||
self["onenter".. To] or
|
self["OnEnter" .. To] then
|
||||||
self["OnEnter".. To] then
|
|
||||||
|
|
||||||
if self:_call_handler( "onbefore", EventName, Params, EventName ) == false then
|
if self:_call_handler( "onbefore", EventName, Params, EventName ) == false then
|
||||||
self:T( "*** FSM *** Cancel" .. " *** " .. self.current .. " --> " .. EventName .. " --> " .. To .. " *** onbefore" .. EventName )
|
self:T( "*** FSM *** Cancel" .. " *** " .. self.current .. " --> " .. EventName .. " --> " .. To .. " *** onbefore" .. EventName )
|
||||||
@@ -716,11 +709,11 @@ do -- FSM
|
|||||||
end
|
end
|
||||||
|
|
||||||
if ClassName == "FSM_CONTROLLABLE" then
|
if ClassName == "FSM_CONTROLLABLE" then
|
||||||
self:T( "*** FSM *** Transit *** " .. self.current .. " --> " .. EventName .. " --> " .. To .. " *** TaskUnit: " .. self.Controllable.ControllableName .. " *** " )
|
self:T( "*** FSM *** Transit *** " .. self.current .. " --> " .. EventName .. " --> " .. To .. " *** TaskUnit: " .. self.Controllable.ControllableName .. " *** " )
|
||||||
end
|
end
|
||||||
|
|
||||||
if ClassName == "FSM_PROCESS" then
|
if ClassName == "FSM_PROCESS" then
|
||||||
self:T( "*** FSM *** Transit *** " .. self.current .. " --> " .. EventName .. " --> " .. To .. " *** Task: " .. self.Task:GetName() .. ", TaskUnit: " .. self.Controllable.ControllableName .. " *** " )
|
self:T( "*** FSM *** Transit *** " .. self.current .. " --> " .. EventName .. " --> " .. To .. " *** Task: " .. self.Task:GetName() .. ", TaskUnit: " .. self.Controllable.ControllableName .. " *** " )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -733,10 +726,10 @@ do -- FSM
|
|||||||
|
|
||||||
for _, sub in pairs( subtable ) do
|
for _, sub in pairs( subtable ) do
|
||||||
|
|
||||||
--if sub.nextevent then
|
-- if sub.nextevent then
|
||||||
-- self:F2( "nextevent = " .. sub.nextevent )
|
-- self:F2( "nextevent = " .. sub.nextevent )
|
||||||
-- self[sub.nextevent]( self )
|
-- self[sub.nextevent]( self )
|
||||||
--end
|
-- end
|
||||||
|
|
||||||
self:T( "*** FSM *** Sub *** " .. sub.StartEvent )
|
self:T( "*** FSM *** Sub *** " .. sub.StartEvent )
|
||||||
|
|
||||||
@@ -753,11 +746,11 @@ do -- FSM
|
|||||||
|
|
||||||
self:T( "*** FSM *** End *** " .. Event )
|
self:T( "*** FSM *** End *** " .. Event )
|
||||||
|
|
||||||
self:_call_handler("onenter", To, Params, EventName )
|
self:_call_handler( "onenter", To, Params, EventName )
|
||||||
self:_call_handler("OnEnter", To, Params, EventName )
|
self:_call_handler( "OnEnter", To, Params, EventName )
|
||||||
self:_call_handler("onafter", EventName, Params, EventName )
|
self:_call_handler( "onafter", EventName, Params, EventName )
|
||||||
self:_call_handler("OnAfter", EventName, Params, EventName )
|
self:_call_handler( "OnAfter", EventName, Params, EventName )
|
||||||
self:_call_handler("onstate", "change", Params, EventName )
|
self:_call_handler( "onstate", "change", Params, EventName )
|
||||||
|
|
||||||
fsmparent[Event]( fsmparent )
|
fsmparent[Event]( fsmparent )
|
||||||
|
|
||||||
@@ -766,13 +759,13 @@ do -- FSM
|
|||||||
|
|
||||||
if execute then
|
if execute then
|
||||||
|
|
||||||
self:_call_handler("onafter", EventName, Params, EventName )
|
self:_call_handler( "onafter", EventName, Params, EventName )
|
||||||
self:_call_handler("OnAfter", EventName, Params, EventName )
|
self:_call_handler( "OnAfter", EventName, Params, EventName )
|
||||||
|
|
||||||
self:_call_handler("onenter", To, Params, EventName )
|
self:_call_handler( "onenter", To, Params, EventName )
|
||||||
self:_call_handler("OnEnter", To, Params, EventName )
|
self:_call_handler( "OnEnter", To, Params, EventName )
|
||||||
|
|
||||||
self:_call_handler("onstate", "change", Params, EventName )
|
self:_call_handler( "onstate", "change", Params, EventName )
|
||||||
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@@ -809,16 +802,16 @@ do -- FSM
|
|||||||
self._EventSchedules[EventName] = CallID
|
self._EventSchedules[EventName] = CallID
|
||||||
|
|
||||||
-- Debug output.
|
-- Debug output.
|
||||||
self:T2(string.format("NEGATIVE Event %s delayed by %.1f sec SCHEDULED with CallID=%s", EventName, DelaySeconds, tostring(CallID)))
|
self:T2( string.format( "NEGATIVE Event %s delayed by %.1f sec SCHEDULED with CallID=%s", EventName, DelaySeconds, tostring( CallID ) ) )
|
||||||
else
|
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))
|
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
|
-- reschedule
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
|
||||||
CallID = self.CallScheduler:Schedule( self, self._handler, { EventName, ... }, DelaySeconds or 1, nil, nil, nil, 4, true )
|
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)))
|
self:T2( string.format( "Event %s delayed by %.1f sec SCHEDULED with CallID=%s", EventName, DelaySeconds, tostring( CallID ) ) )
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
error( "FSM: An asynchronous event trigger requires a DelaySeconds parameter!!! This can be positive or negative! Sorry, but will not process this." )
|
error( "FSM: An asynchronous event trigger requires a DelaySeconds parameter!!! This can be positive or negative! Sorry, but will not process this." )
|
||||||
@@ -835,7 +828,9 @@ do -- FSM
|
|||||||
-- @param #string EventName Event name.
|
-- @param #string EventName Event name.
|
||||||
-- @return #function Function.
|
-- @return #function Function.
|
||||||
function FSM:_create_transition( EventName )
|
function FSM:_create_transition( EventName )
|
||||||
return function( self, ... ) return self._handler( self, EventName , ... ) end
|
return function( self, ... )
|
||||||
|
return self._handler( self, EventName, ... )
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Go sub.
|
--- Go sub.
|
||||||
@@ -862,16 +857,16 @@ do -- FSM
|
|||||||
local FSMParent = self.fsmparent
|
local FSMParent = self.fsmparent
|
||||||
|
|
||||||
if FSMParent and self.endstates[Current] then
|
if FSMParent and self.endstates[Current] then
|
||||||
--self:T( { state = Current, endstates = self.endstates, endstate = self.endstates[Current] } )
|
-- self:T( { state = Current, endstates = self.endstates, endstate = self.endstates[Current] } )
|
||||||
FSMParent.current = Current
|
FSMParent.current = Current
|
||||||
local ParentFrom = FSMParent.current
|
local ParentFrom = FSMParent.current
|
||||||
--self:T( { ParentFrom, self.ReturnEvents } )
|
-- self:T( { ParentFrom, self.ReturnEvents } )
|
||||||
local Event = self.ReturnEvents[Current]
|
local Event = self.ReturnEvents[Current]
|
||||||
--self:T( { Event } )
|
-- self:T( { Event } )
|
||||||
if Event then
|
if Event then
|
||||||
return FSMParent, Event
|
return FSMParent, Event
|
||||||
else
|
else
|
||||||
--self:T( { "Could not find parent event name for state ", ParentFrom } )
|
-- self:T( { "Could not find parent event name for state ", ParentFrom } )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -883,17 +878,17 @@ do -- FSM
|
|||||||
-- @param #table Map Map.
|
-- @param #table Map Map.
|
||||||
-- @param #table Event Event table.
|
-- @param #table Event Event table.
|
||||||
function FSM:_add_to_map( Map, Event )
|
function FSM:_add_to_map( Map, Event )
|
||||||
self:F3( { Map, Event } )
|
self:F3( { Map, Event } )
|
||||||
|
|
||||||
if type(Event.From) == 'string' then
|
if type( Event.From ) == 'string' then
|
||||||
Map[Event.From] = Event.To
|
Map[Event.From] = Event.To
|
||||||
else
|
else
|
||||||
for _, From in ipairs(Event.From) do
|
for _, From in ipairs( Event.From ) do
|
||||||
Map[From] = Event.To
|
Map[From] = Event.To
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
self:T3( { Map, Event } )
|
self:T3( { Map, Event } )
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get current state.
|
--- Get current state.
|
||||||
@@ -922,7 +917,7 @@ do -- FSM
|
|||||||
-- @param #FSM self
|
-- @param #FSM self
|
||||||
-- @param #string State State name.
|
-- @param #string State State name.
|
||||||
-- @param #boolean If true, FSM is in this state.
|
-- @param #boolean If true, FSM is in this state.
|
||||||
function FSM:is(state)
|
function FSM:is( state )
|
||||||
return self.current == state
|
return self.current == state
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -931,11 +926,11 @@ do -- FSM
|
|||||||
-- @param #string e Event name.
|
-- @param #string e Event name.
|
||||||
-- @return #boolean If true, FSM can do the event.
|
-- @return #boolean If true, FSM can do the event.
|
||||||
-- @return #string To state.
|
-- @return #string To state.
|
||||||
function FSM:can(e)
|
function FSM:can( e )
|
||||||
|
|
||||||
local Event = self.Events[e]
|
local Event = self.Events[e]
|
||||||
|
|
||||||
--self:F3( { self.current, Event } )
|
-- self:F3( { self.current, Event } )
|
||||||
|
|
||||||
local To = Event and Event.map[self.current] or Event.map['*']
|
local To = Event and Event.map[self.current] or Event.map['*']
|
||||||
|
|
||||||
@@ -946,8 +941,8 @@ do -- FSM
|
|||||||
-- @param #FSM self
|
-- @param #FSM self
|
||||||
-- @param #string e Event name.
|
-- @param #string e Event name.
|
||||||
-- @return #boolean If true, FSM cannot do the event.
|
-- @return #boolean If true, FSM cannot do the event.
|
||||||
function FSM:cannot(e)
|
function FSM:cannot( e )
|
||||||
return not self:can(e)
|
return not self:can( e )
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -1036,7 +1031,7 @@ do -- FSM_CONTROLLABLE
|
|||||||
-- @param #string From The From State string.
|
-- @param #string From The From State string.
|
||||||
-- @param #string Event The Event string.
|
-- @param #string Event The Event string.
|
||||||
-- @param #string To The To State string.
|
-- @param #string To The To State string.
|
||||||
function FSM_CONTROLLABLE:OnAfterStop(Controllable,From,Event,To)
|
function FSM_CONTROLLABLE:OnAfterStop( Controllable, From, Event, To )
|
||||||
|
|
||||||
-- Clear all pending schedules
|
-- Clear all pending schedules
|
||||||
self.CallScheduler:Clear()
|
self.CallScheduler:Clear()
|
||||||
@@ -1047,7 +1042,7 @@ do -- FSM_CONTROLLABLE
|
|||||||
-- @param Wrapper.Controllable#CONTROLLABLE FSMControllable
|
-- @param Wrapper.Controllable#CONTROLLABLE FSMControllable
|
||||||
-- @return #FSM_CONTROLLABLE
|
-- @return #FSM_CONTROLLABLE
|
||||||
function FSM_CONTROLLABLE:SetControllable( FSMControllable )
|
function FSM_CONTROLLABLE:SetControllable( FSMControllable )
|
||||||
--self:F( FSMControllable:GetName() )
|
-- self:F( FSMControllable:GetName() )
|
||||||
self.Controllable = FSMControllable
|
self.Controllable = FSMControllable
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1075,9 +1070,11 @@ do -- FSM_CONTROLLABLE
|
|||||||
if self[handler] then
|
if self[handler] then
|
||||||
self:T( "*** FSM *** " .. step .. " *** " .. params[1] .. " --> " .. params[2] .. " --> " .. params[3] .. " *** TaskUnit: " .. self.Controllable:GetName() )
|
self:T( "*** FSM *** " .. step .. " *** " .. params[1] .. " --> " .. params[2] .. " --> " .. params[3] .. " *** TaskUnit: " .. self.Controllable:GetName() )
|
||||||
self._EventSchedules[EventName] = nil
|
self._EventSchedules[EventName] = nil
|
||||||
local Result, Value = xpcall( function() return self[handler]( self, self.Controllable, unpack( params ) ) end, ErrorHandler )
|
local Result, Value = xpcall( function()
|
||||||
|
return self[handler]( self, self.Controllable, unpack( params ) )
|
||||||
|
end, ErrorHandler )
|
||||||
return Value
|
return Value
|
||||||
--return self[handler]( self, self.Controllable, unpack( params ) )
|
-- return self[handler]( self, self.Controllable, unpack( params ) )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1089,16 +1086,13 @@ do -- FSM_PROCESS
|
|||||||
-- @field Tasking.Task#TASK Task
|
-- @field Tasking.Task#TASK Task
|
||||||
-- @extends Core.Fsm#FSM_CONTROLLABLE
|
-- @extends Core.Fsm#FSM_CONTROLLABLE
|
||||||
|
|
||||||
|
|
||||||
--- FSM_PROCESS class models Finite State Machines for @{Task} actions, which control @{Client}s.
|
--- FSM_PROCESS class models Finite State Machines for @{Task} actions, which control @{Client}s.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- @field #FSM_PROCESS FSM_PROCESS
|
-- @field #FSM_PROCESS FSM_PROCESS
|
||||||
--
|
--
|
||||||
FSM_PROCESS = {
|
FSM_PROCESS = { ClassName = "FSM_PROCESS" }
|
||||||
ClassName = "FSM_PROCESS",
|
|
||||||
}
|
|
||||||
|
|
||||||
--- Creates a new FSM_PROCESS object.
|
--- Creates a new FSM_PROCESS object.
|
||||||
-- @param #FSM_PROCESS self
|
-- @param #FSM_PROCESS self
|
||||||
@@ -1107,7 +1101,7 @@ do -- FSM_PROCESS
|
|||||||
|
|
||||||
local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- Core.Fsm#FSM_PROCESS
|
local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- Core.Fsm#FSM_PROCESS
|
||||||
|
|
||||||
--self:F( Controllable )
|
-- self:F( Controllable )
|
||||||
|
|
||||||
self:Assign( Controllable, Task )
|
self:Assign( Controllable, Task )
|
||||||
|
|
||||||
@@ -1139,10 +1133,12 @@ do -- FSM_PROCESS
|
|||||||
self._EventSchedules[EventName] = nil
|
self._EventSchedules[EventName] = nil
|
||||||
local Result, Value
|
local Result, Value
|
||||||
if self.Controllable and self.Controllable:IsAlive() == true then
|
if self.Controllable and self.Controllable:IsAlive() == true then
|
||||||
Result, Value = xpcall( function() return self[handler]( self, self.Controllable, self.Task, unpack( params ) ) end, ErrorHandler )
|
Result, Value = xpcall( function()
|
||||||
|
return self[handler]( self, self.Controllable, self.Task, unpack( params ) )
|
||||||
|
end, ErrorHandler )
|
||||||
end
|
end
|
||||||
return Value
|
return Value
|
||||||
--return self[handler]( self, self.Controllable, unpack( params ) )
|
-- return self[handler]( self, self.Controllable, unpack( params ) )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1152,7 +1148,6 @@ do -- FSM_PROCESS
|
|||||||
function FSM_PROCESS:Copy( Controllable, Task )
|
function FSM_PROCESS:Copy( Controllable, Task )
|
||||||
self:T( { self:GetClassNameAndID() } )
|
self:T( { self:GetClassNameAndID() } )
|
||||||
|
|
||||||
|
|
||||||
local NewFsm = self:New( Controllable, Task ) -- Core.Fsm#FSM_PROCESS
|
local NewFsm = self:New( Controllable, Task ) -- Core.Fsm#FSM_PROCESS
|
||||||
|
|
||||||
NewFsm:Assign( Controllable, Task )
|
NewFsm:Assign( Controllable, Task )
|
||||||
@@ -1170,7 +1165,7 @@ do -- FSM_PROCESS
|
|||||||
|
|
||||||
-- Copy Processes
|
-- Copy Processes
|
||||||
for ProcessID, Process in pairs( self:GetProcesses() ) do
|
for ProcessID, Process in pairs( self:GetProcesses() ) do
|
||||||
--self:E( { Process:GetName() } )
|
-- self:E( { Process:GetName() } )
|
||||||
local FsmProcess = NewFsm:AddProcess( Process.From, Process.Event, Process.fsm:Copy( Controllable, Task ), Process.ReturnEvents )
|
local FsmProcess = NewFsm:AddProcess( Process.From, Process.Event, Process.fsm:Copy( Controllable, Task ), Process.ReturnEvents )
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1244,7 +1239,7 @@ do -- FSM_PROCESS
|
|||||||
return self:GetTask():GetMission():GetCommandCenter()
|
return self:GetTask():GetMission():GetCommandCenter()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- TODO: Need to check and fix that an FSM_PROCESS is only for a UNIT. Not for a GROUP.
|
-- TODO: Need to check and fix that an FSM_PROCESS is only for a UNIT. Not for a GROUP.
|
||||||
|
|
||||||
--- Send a message of the @{Task} to the Group of the Unit.
|
--- Send a message of the @{Task} to the Group of the Unit.
|
||||||
-- @param #FSM_PROCESS self
|
-- @param #FSM_PROCESS self
|
||||||
@@ -1263,32 +1258,29 @@ do -- FSM_PROCESS
|
|||||||
CC:MessageToGroup( Message, TaskGroup )
|
CC:MessageToGroup( Message, TaskGroup )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- Assign the process to a @{Wrapper.Unit} and activate the process.
|
--- Assign the process to a @{Wrapper.Unit} and activate the process.
|
||||||
-- @param #FSM_PROCESS self
|
-- @param #FSM_PROCESS self
|
||||||
-- @param Task.Tasking#TASK Task
|
-- @param Task.Tasking#TASK Task
|
||||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||||
-- @return #FSM_PROCESS self
|
-- @return #FSM_PROCESS self
|
||||||
function FSM_PROCESS:Assign( ProcessUnit, Task )
|
function FSM_PROCESS:Assign( ProcessUnit, Task )
|
||||||
--self:T( { Task:GetName(), ProcessUnit:GetName() } )
|
-- self:T( { Task:GetName(), ProcessUnit:GetName() } )
|
||||||
|
|
||||||
self:SetControllable( ProcessUnit )
|
self:SetControllable( ProcessUnit )
|
||||||
self:SetTask( Task )
|
self:SetTask( Task )
|
||||||
|
|
||||||
--self.ProcessGroup = ProcessUnit:GetGroup()
|
-- self.ProcessGroup = ProcessUnit:GetGroup()
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
-- function FSM_PROCESS:onenterAssigned( ProcessUnit, Task, From, Event, To )
|
-- function FSM_PROCESS:onenterAssigned( ProcessUnit, Task, From, Event, To )
|
||||||
--
|
--
|
||||||
-- if From( "Planned" ) then
|
-- if From( "Planned" ) then
|
||||||
-- self:T( "*** FSM *** Assign *** " .. Task:GetName() .. "/" .. ProcessUnit:GetName() .. " *** " .. From .. " --> " .. Event .. " --> " .. To )
|
-- self:T( "*** FSM *** Assign *** " .. Task:GetName() .. "/" .. ProcessUnit:GetName() .. " *** " .. From .. " --> " .. Event .. " --> " .. To )
|
||||||
-- self.Task:Assign()
|
-- self.Task:Assign()
|
||||||
-- end
|
-- end
|
||||||
-- end
|
-- end
|
||||||
|
|
||||||
function FSM_PROCESS:onenterFailed( ProcessUnit, Task, From, Event, To )
|
function FSM_PROCESS:onenterFailed( ProcessUnit, Task, From, Event, To )
|
||||||
self:T( "*** FSM *** Failed *** " .. Task:GetName() .. "/" .. ProcessUnit:GetName() .. " *** " .. From .. " --> " .. Event .. " --> " .. To )
|
self:T( "*** FSM *** Failed *** " .. Task:GetName() .. "/" .. ProcessUnit:GetName() .. " *** " .. From .. " --> " .. Event .. " --> " .. To )
|
||||||
@@ -1296,7 +1288,6 @@ do -- FSM_PROCESS
|
|||||||
self.Task:Fail()
|
self.Task:Fail()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- StateMachine callback function for a FSM_PROCESS
|
--- StateMachine callback function for a FSM_PROCESS
|
||||||
-- @param #FSM_PROCESS self
|
-- @param #FSM_PROCESS self
|
||||||
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
|
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
|
||||||
@@ -1309,10 +1300,10 @@ do -- FSM_PROCESS
|
|||||||
self:T( "*** FSM *** Change *** " .. Task:GetName() .. "/" .. ProcessUnit:GetName() .. " *** " .. From .. " --> " .. Event .. " --> " .. To )
|
self:T( "*** FSM *** Change *** " .. Task:GetName() .. "/" .. ProcessUnit:GetName() .. " *** " .. From .. " --> " .. Event .. " --> " .. To )
|
||||||
end
|
end
|
||||||
|
|
||||||
-- if self:IsTrace() then
|
-- if self:IsTrace() then
|
||||||
-- MESSAGE:New( "@ Process " .. self:GetClassNameAndID() .. " : " .. Event .. " changed to state " .. To, 2 ):ToAll()
|
-- MESSAGE:New( "@ Process " .. self:GetClassNameAndID() .. " : " .. Event .. " changed to state " .. To, 2 ):ToAll()
|
||||||
-- self:F2( { Scores = self._Scores, To = To } )
|
-- self:F2( { Scores = self._Scores, To = To } )
|
||||||
-- end
|
-- end
|
||||||
|
|
||||||
-- TODO: This needs to be reworked with a callback functions allocated within Task, and set within the mission script from the Task Objects...
|
-- TODO: This needs to be reworked with a callback functions allocated within Task, and set within the mission script from the Task Objects...
|
||||||
if self._Scores[To] then
|
if self._Scores[To] then
|
||||||
@@ -1374,8 +1365,10 @@ do -- FSM_TASK
|
|||||||
if self[handler] then
|
if self[handler] then
|
||||||
self:T( "*** FSM *** " .. step .. " *** " .. params[1] .. " --> " .. params[2] .. " --> " .. params[3] .. " *** Task: " .. self.TaskName )
|
self:T( "*** FSM *** " .. step .. " *** " .. params[1] .. " --> " .. params[2] .. " --> " .. params[3] .. " *** Task: " .. self.TaskName )
|
||||||
self._EventSchedules[EventName] = nil
|
self._EventSchedules[EventName] = nil
|
||||||
--return self[handler]( self, unpack( params ) )
|
-- return self[handler]( self, unpack( params ) )
|
||||||
local Result, Value = xpcall( function() return self[handler]( self, unpack( params ) ) end, ErrorHandler )
|
local Result, Value = xpcall( function()
|
||||||
|
return self[handler]( self, unpack( params ) )
|
||||||
|
end, ErrorHandler )
|
||||||
return Value
|
return Value
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -1389,14 +1382,12 @@ do -- FSM_SET
|
|||||||
-- @field Core.Set#SET_BASE Set
|
-- @field Core.Set#SET_BASE Set
|
||||||
-- @extends Core.Fsm#FSM
|
-- @extends Core.Fsm#FSM
|
||||||
|
|
||||||
|
|
||||||
--- FSM_SET class models Finite State Machines for @{Set}s. Note that these FSMs control multiple objects!!! So State concerns here
|
--- FSM_SET class models Finite State Machines for @{Set}s. Note that these FSMs control multiple objects!!! So State concerns here
|
||||||
-- for multiple objects or the position of the state machine in the process.
|
-- for multiple objects or the position of the state machine in the process.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- @field #FSM_SET FSM_SET
|
-- @field #FSM_SET FSM_SET
|
||||||
--
|
|
||||||
FSM_SET = {
|
FSM_SET = {
|
||||||
ClassName = "FSM_SET",
|
ClassName = "FSM_SET",
|
||||||
}
|
}
|
||||||
@@ -1434,8 +1425,8 @@ do -- FSM_SET
|
|||||||
return self.Controllable
|
return self.Controllable
|
||||||
end
|
end
|
||||||
|
|
||||||
function FSM_SET:_call_handler( step, trigger, params, EventName )
|
function FSM_SET:_call_handler( step, trigger, params, EventName )
|
||||||
local handler = step .. trigger
|
local handler = step .. trigger
|
||||||
if self[handler] then
|
if self[handler] then
|
||||||
self:T( "*** FSM *** " .. step .. " *** " .. params[1] .. " --> " .. params[2] .. " --> " .. params[3] )
|
self:T( "*** FSM *** " .. step .. " *** " .. params[1] .. " --> " .. params[2] .. " --> " .. params[3] )
|
||||||
self._EventSchedules[EventName] = nil
|
self._EventSchedules[EventName] = nil
|
||||||
|
|||||||
@@ -22,13 +22,11 @@
|
|||||||
-- @module Core.Goal
|
-- @module Core.Goal
|
||||||
-- @image Core_Goal.JPG
|
-- @image Core_Goal.JPG
|
||||||
|
|
||||||
|
|
||||||
do -- Goal
|
do -- Goal
|
||||||
|
|
||||||
--- @type GOAL
|
--- @type GOAL
|
||||||
-- @extends Core.Fsm#FSM
|
-- @extends Core.Fsm#FSM
|
||||||
|
|
||||||
|
|
||||||
--- Models processes that have an objective with a defined achievement. Derived classes implement the ways how the achievements can be realized.
|
--- 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
|
||||||
@@ -105,9 +103,8 @@ do -- Goal
|
|||||||
-- @param #string Event
|
-- @param #string Event
|
||||||
-- @param #string To
|
-- @param #string To
|
||||||
|
|
||||||
|
|
||||||
self:SetStartState( "Pending" )
|
self:SetStartState( "Pending" )
|
||||||
self:AddTransition( "*", "Achieved", "Achieved" )
|
self:AddTransition( "*", "Achieved", "Achieved" )
|
||||||
|
|
||||||
--- Achieved Handler OnBefore for GOAL
|
--- Achieved Handler OnBefore for GOAL
|
||||||
-- @function [parent=#GOAL] OnBeforeAchieved
|
-- @function [parent=#GOAL] OnBeforeAchieved
|
||||||
@@ -138,25 +135,22 @@ do -- Goal
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Add a new contribution by a player.
|
--- Add a new contribution by a player.
|
||||||
-- @param #GOAL self
|
-- @param #GOAL self
|
||||||
-- @param #string PlayerName The name of the player.
|
-- @param #string PlayerName The name of the player.
|
||||||
function GOAL:AddPlayerContribution( PlayerName )
|
function GOAL:AddPlayerContribution( PlayerName )
|
||||||
self:F({PlayerName})
|
self:F( { PlayerName } )
|
||||||
self.Players[PlayerName] = self.Players[PlayerName] or 0
|
self.Players[PlayerName] = self.Players[PlayerName] or 0
|
||||||
self.Players[PlayerName] = self.Players[PlayerName] + 1
|
self.Players[PlayerName] = self.Players[PlayerName] + 1
|
||||||
self.TotalContributions = self.TotalContributions + 1
|
self.TotalContributions = self.TotalContributions + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- @param #GOAL self
|
--- @param #GOAL self
|
||||||
-- @param #number Player contribution.
|
-- @param #number Player contribution.
|
||||||
function GOAL:GetPlayerContribution( PlayerName )
|
function GOAL:GetPlayerContribution( PlayerName )
|
||||||
return self.Players[PlayerName] or 0
|
return self.Players[PlayerName] or 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Get the players who contributed to achieve the goal.
|
--- Get the players who contributed to achieve the goal.
|
||||||
-- The result is a list of players, sorted by the name of the players.
|
-- The result is a list of players, sorted by the name of the players.
|
||||||
-- @param #GOAL self
|
-- @param #GOAL self
|
||||||
@@ -165,7 +159,6 @@ do -- Goal
|
|||||||
return self.Players or {}
|
return self.Players or {}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Gets the total contributions that happened to achieve the goal.
|
--- Gets the total contributions that happened to achieve the goal.
|
||||||
-- The result is a number.
|
-- The result is a number.
|
||||||
-- @param #GOAL self
|
-- @param #GOAL self
|
||||||
@@ -174,8 +167,6 @@ do -- Goal
|
|||||||
return self.TotalContributions or 0
|
return self.TotalContributions or 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- Validates if the goal is achieved.
|
--- Validates if the goal is achieved.
|
||||||
-- @param #GOAL self
|
-- @param #GOAL self
|
||||||
-- @return #boolean true if the goal is achieved.
|
-- @return #boolean true if the goal is achieved.
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
-- * Only create or delete menus when required, and keep existing menus persistent.
|
-- * Only create or delete menus when required, and keep existing menus persistent.
|
||||||
-- * Update menu structures.
|
-- * Update menu structures.
|
||||||
-- * Refresh menu structures intelligently, based on a time stamp of updates.
|
-- * Refresh menu structures intelligently, based on a time stamp of updates.
|
||||||
-- - Delete obscolete menus.
|
-- - Delete obsolete menus.
|
||||||
-- - Create new one where required.
|
-- - Create new one where required.
|
||||||
-- - Don't touch the existing ones.
|
-- - Don't touch the existing ones.
|
||||||
-- * Provide a variable amount of parameters to menus.
|
-- * Provide a variable amount of parameters to menus.
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
-- * Provide a great tool to manage menus in your code.
|
-- * Provide a great tool to manage menus in your code.
|
||||||
--
|
--
|
||||||
-- DCS Menus can be managed using the MENU classes.
|
-- 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
|
-- The advantage of using MENU classes is that it hides the complexity of dealing with menu management in more advanced scenarios 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
|
-- 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
|
||||||
-- menus is not a easy feat if you have complex menu hierarchies defined.
|
-- menus is not a easy feat if you have complex menu hierarchies defined.
|
||||||
-- Using the MOOSE menu classes, the removal and refreshing of menus are nicely being handled within these classes, and becomes much more easy.
|
-- Using the MOOSE menu classes, the removal and refreshing of menus are nicely being handled within these classes, and becomes much more easy.
|
||||||
@@ -53,7 +53,6 @@
|
|||||||
-- @module Core.Menu
|
-- @module Core.Menu
|
||||||
-- @image Core_Menu.JPG
|
-- @image Core_Menu.JPG
|
||||||
|
|
||||||
|
|
||||||
MENU_INDEX = {}
|
MENU_INDEX = {}
|
||||||
MENU_INDEX.MenuMission = {}
|
MENU_INDEX.MenuMission = {}
|
||||||
MENU_INDEX.MenuMission.Menus = {}
|
MENU_INDEX.MenuMission.Menus = {}
|
||||||
@@ -64,8 +63,6 @@ MENU_INDEX.Coalition[coalition.side.RED] = {}
|
|||||||
MENU_INDEX.Coalition[coalition.side.RED].Menus = {}
|
MENU_INDEX.Coalition[coalition.side.RED].Menus = {}
|
||||||
MENU_INDEX.Group = {}
|
MENU_INDEX.Group = {}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function MENU_INDEX:ParentPath( ParentMenu, MenuText )
|
function MENU_INDEX:ParentPath( ParentMenu, MenuText )
|
||||||
|
|
||||||
local Path = ParentMenu and "@" .. table.concat( ParentMenu.MenuPath or {}, "@" ) or ""
|
local Path = ParentMenu and "@" .. table.concat( ParentMenu.MenuPath or {}, "@" ) or ""
|
||||||
@@ -98,29 +95,25 @@ function MENU_INDEX:ParentPath( ParentMenu, MenuText )
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function MENU_INDEX:PrepareMission()
|
function MENU_INDEX:PrepareMission()
|
||||||
self.MenuMission.Menus = self.MenuMission.Menus or {}
|
self.MenuMission.Menus = self.MenuMission.Menus or {}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function MENU_INDEX:PrepareCoalition( CoalitionSide )
|
function MENU_INDEX:PrepareCoalition( CoalitionSide )
|
||||||
self.Coalition[CoalitionSide] = self.Coalition[CoalitionSide] or {}
|
self.Coalition[CoalitionSide] = self.Coalition[CoalitionSide] or {}
|
||||||
self.Coalition[CoalitionSide].Menus = self.Coalition[CoalitionSide].Menus or {}
|
self.Coalition[CoalitionSide].Menus = self.Coalition[CoalitionSide].Menus or {}
|
||||||
end
|
end
|
||||||
|
|
||||||
---
|
---
|
||||||
-- @param Wrapper.Group#GROUP Group
|
-- @param Wrapper.Group#GROUP Group
|
||||||
function MENU_INDEX:PrepareGroup( Group )
|
function MENU_INDEX:PrepareGroup( Group )
|
||||||
if Group and Group:IsAlive() ~= nil then -- something was changed here!
|
if Group and Group:IsAlive() ~= nil then -- something was changed here!
|
||||||
local GroupName = Group:GetName()
|
local GroupName = Group:GetName()
|
||||||
self.Group[GroupName] = self.Group[GroupName] or {}
|
self.Group[GroupName] = self.Group[GroupName] or {}
|
||||||
self.Group[GroupName].Menus = self.Group[GroupName].Menus or {}
|
self.Group[GroupName].Menus = self.Group[GroupName].Menus or {}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function MENU_INDEX:HasMissionMenu( Path )
|
function MENU_INDEX:HasMissionMenu( Path )
|
||||||
|
|
||||||
return self.MenuMission.Menus[Path]
|
return self.MenuMission.Menus[Path]
|
||||||
@@ -136,8 +129,6 @@ function MENU_INDEX:ClearMissionMenu( Path )
|
|||||||
self.MenuMission.Menus[Path] = nil
|
self.MenuMission.Menus[Path] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function MENU_INDEX:HasCoalitionMenu( Coalition, Path )
|
function MENU_INDEX:HasCoalitionMenu( Coalition, Path )
|
||||||
|
|
||||||
return self.Coalition[Coalition].Menus[Path]
|
return self.Coalition[Coalition].Menus[Path]
|
||||||
@@ -153,8 +144,6 @@ function MENU_INDEX:ClearCoalitionMenu( Coalition, Path )
|
|||||||
self.Coalition[Coalition].Menus[Path] = nil
|
self.Coalition[Coalition].Menus[Path] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function MENU_INDEX:HasGroupMenu( Group, Path )
|
function MENU_INDEX:HasGroupMenu( Group, Path )
|
||||||
if Group and Group:IsAlive() then
|
if Group and Group:IsAlive() then
|
||||||
local MenuGroupName = Group:GetName()
|
local MenuGroupName = Group:GetName()
|
||||||
@@ -166,7 +155,7 @@ end
|
|||||||
function MENU_INDEX:SetGroupMenu( Group, Path, Menu )
|
function MENU_INDEX:SetGroupMenu( Group, Path, Menu )
|
||||||
|
|
||||||
local MenuGroupName = Group:GetName()
|
local MenuGroupName = Group:GetName()
|
||||||
Group:F({MenuGroupName=MenuGroupName,Path=Path})
|
Group:F( { MenuGroupName = MenuGroupName, Path = Path } )
|
||||||
self.Group[MenuGroupName].Menus[Path] = Menu
|
self.Group[MenuGroupName].Menus[Path] = Menu
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -178,36 +167,29 @@ end
|
|||||||
|
|
||||||
function MENU_INDEX:Refresh( Group )
|
function MENU_INDEX:Refresh( Group )
|
||||||
|
|
||||||
for MenuID, Menu in pairs( self.MenuMission.Menus ) do
|
for MenuID, Menu in pairs( self.MenuMission.Menus ) do
|
||||||
Menu:Refresh()
|
Menu:Refresh()
|
||||||
end
|
end
|
||||||
|
|
||||||
for MenuID, Menu in pairs( self.Coalition[coalition.side.BLUE].Menus ) do
|
for MenuID, Menu in pairs( self.Coalition[coalition.side.BLUE].Menus ) do
|
||||||
Menu:Refresh()
|
Menu:Refresh()
|
||||||
end
|
end
|
||||||
|
|
||||||
for MenuID, Menu in pairs( self.Coalition[coalition.side.RED].Menus ) do
|
for MenuID, Menu in pairs( self.Coalition[coalition.side.RED].Menus ) do
|
||||||
Menu:Refresh()
|
Menu:Refresh()
|
||||||
end
|
end
|
||||||
|
|
||||||
local GroupName = Group:GetName()
|
local GroupName = Group:GetName()
|
||||||
for MenuID, Menu in pairs( self.Group[GroupName].Menus ) do
|
for MenuID, Menu in pairs( self.Group[GroupName].Menus ) do
|
||||||
Menu:Refresh()
|
Menu:Refresh()
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
do -- MENU_BASE
|
do -- MENU_BASE
|
||||||
|
|
||||||
--- @type MENU_BASE
|
--- @type MENU_BASE
|
||||||
-- @extends Base#BASE
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
--- Defines the main MENU class where other MENU classes are derived from.
|
--- Defines the main MENU class where other MENU classes are derived from.
|
||||||
-- This is an abstract class, so don't use it.
|
-- This is an abstract class, so don't use it.
|
||||||
@@ -216,10 +198,10 @@ do -- MENU_BASE
|
|||||||
ClassName = "MENU_BASE",
|
ClassName = "MENU_BASE",
|
||||||
MenuPath = nil,
|
MenuPath = nil,
|
||||||
MenuText = "",
|
MenuText = "",
|
||||||
MenuParentPath = nil
|
MenuParentPath = nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Consructor
|
--- Constructor
|
||||||
-- @param #MENU_BASE
|
-- @param #MENU_BASE
|
||||||
-- @return #MENU_BASE
|
-- @return #MENU_BASE
|
||||||
function MENU_BASE:New( MenuText, ParentMenu )
|
function MENU_BASE:New( MenuText, ParentMenu )
|
||||||
@@ -229,13 +211,13 @@ do -- MENU_BASE
|
|||||||
MenuParentPath = ParentMenu.MenuPath
|
MenuParentPath = ParentMenu.MenuPath
|
||||||
end
|
end
|
||||||
|
|
||||||
local self = BASE:Inherit( self, BASE:New() )
|
local self = BASE:Inherit( self, BASE:New() )
|
||||||
|
|
||||||
self.MenuPath = nil
|
self.MenuPath = nil
|
||||||
self.MenuText = MenuText
|
self.MenuText = MenuText
|
||||||
self.ParentMenu = ParentMenu
|
self.ParentMenu = ParentMenu
|
||||||
self.MenuParentPath = MenuParentPath
|
self.MenuParentPath = MenuParentPath
|
||||||
self.Path = ( self.ParentMenu and "@" .. table.concat( self.MenuParentPath or {}, "@" ) or "" ) .. "@" .. self.MenuText
|
self.Path = (self.ParentMenu and "@" .. table.concat( self.MenuParentPath or {}, "@" ) or "") .. "@" .. self.MenuText
|
||||||
self.Menus = {}
|
self.Menus = {}
|
||||||
self.MenuCount = 0
|
self.MenuCount = 0
|
||||||
self.MenuStamp = timer.getTime()
|
self.MenuStamp = timer.getTime()
|
||||||
@@ -246,7 +228,7 @@ do -- MENU_BASE
|
|||||||
self.ParentMenu.Menus[MenuText] = self
|
self.ParentMenu.Menus[MenuText] = self
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
function MENU_BASE:SetParentMenu( MenuText, Menu )
|
function MENU_BASE:SetParentMenu( MenuText, Menu )
|
||||||
@@ -262,7 +244,7 @@ do -- MENU_BASE
|
|||||||
self.ParentMenu.Menus[MenuText] = nil
|
self.ParentMenu.Menus[MenuText] = nil
|
||||||
self.ParentMenu.MenuCount = self.ParentMenu.MenuCount - 1
|
self.ParentMenu.MenuCount = self.ParentMenu.MenuCount - 1
|
||||||
if self.ParentMenu.MenuCount == 0 then
|
if self.ParentMenu.MenuCount == 0 then
|
||||||
--self.ParentMenu:Remove()
|
-- self.ParentMenu:Remove()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -272,12 +254,11 @@ do -- MENU_BASE
|
|||||||
-- @param #boolean RemoveParent If true, the parent menu is automatically removed when this menu is the last child menu of that parent @{Menu}.
|
-- @param #boolean RemoveParent If true, the parent menu is automatically removed when this menu is the last child menu of that parent @{Menu}.
|
||||||
-- @return #MENU_BASE
|
-- @return #MENU_BASE
|
||||||
function MENU_BASE:SetRemoveParent( RemoveParent )
|
function MENU_BASE:SetRemoveParent( RemoveParent )
|
||||||
--self:F( { RemoveParent } )
|
-- self:F( { RemoveParent } )
|
||||||
self.MenuRemoveParent = RemoveParent
|
self.MenuRemoveParent = RemoveParent
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Gets a @{Menu} from a parent @{Menu}
|
--- Gets a @{Menu} from a parent @{Menu}
|
||||||
-- @param #MENU_BASE self
|
-- @param #MENU_BASE self
|
||||||
-- @param #string MenuText The text of the child menu.
|
-- @param #string MenuText The text of the child menu.
|
||||||
@@ -295,7 +276,6 @@ do -- MENU_BASE
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Gets a menu stamp for later prevention of menu removal.
|
--- Gets a menu stamp for later prevention of menu removal.
|
||||||
-- @param #MENU_BASE self
|
-- @param #MENU_BASE self
|
||||||
-- @return MenuStamp
|
-- @return MenuStamp
|
||||||
@@ -303,7 +283,6 @@ do -- MENU_BASE
|
|||||||
return timer.getTime()
|
return timer.getTime()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Sets a time stamp for later prevention of menu removal.
|
--- Sets a time stamp for later prevention of menu removal.
|
||||||
-- @param #MENU_BASE self
|
-- @param #MENU_BASE self
|
||||||
-- @param MenuStamp
|
-- @param MenuStamp
|
||||||
@@ -346,7 +325,7 @@ do -- MENU_COMMAND_BASE
|
|||||||
-- @return #MENU_COMMAND_BASE
|
-- @return #MENU_COMMAND_BASE
|
||||||
function MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, CommandMenuArguments )
|
function MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, CommandMenuArguments )
|
||||||
|
|
||||||
local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) -- #MENU_COMMAND_BASE
|
local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) -- #MENU_COMMAND_BASE
|
||||||
|
|
||||||
-- When a menu function goes into error, DCS displays an obscure menu message.
|
-- When a menu function goes into error, DCS displays an obscure menu message.
|
||||||
-- This error handler catches the menu error and displays the full call stack.
|
-- This error handler catches the menu error and displays the full call stack.
|
||||||
@@ -367,7 +346,7 @@ do -- MENU_COMMAND_BASE
|
|||||||
local Status, Result = xpcall( MenuFunction, ErrorHandler )
|
local Status, Result = xpcall( MenuFunction, ErrorHandler )
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- This sets the new command function of a menu,
|
--- This sets the new command function of a menu,
|
||||||
@@ -394,7 +373,6 @@ do -- MENU_COMMAND_BASE
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
do -- MENU_MISSION
|
do -- MENU_MISSION
|
||||||
|
|
||||||
--- @type MENU_MISSION
|
--- @type MENU_MISSION
|
||||||
@@ -406,13 +384,13 @@ do -- MENU_MISSION
|
|||||||
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_MISSION.Remove}.
|
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_MISSION.Remove}.
|
||||||
-- @field #MENU_MISSION
|
-- @field #MENU_MISSION
|
||||||
MENU_MISSION = {
|
MENU_MISSION = {
|
||||||
ClassName = "MENU_MISSION"
|
ClassName = "MENU_MISSION",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- MENU_MISSION constructor. Creates a new MENU_MISSION object and creates the menu for a complete mission file.
|
--- MENU_MISSION constructor. Creates a new MENU_MISSION object and creates the menu for a complete mission file.
|
||||||
-- @param #MENU_MISSION self
|
-- @param #MENU_MISSION self
|
||||||
-- @param #string MenuText The text for the menu.
|
-- @param #string MenuText The text for the menu.
|
||||||
-- @param #table ParentMenu The parent menu. This parameter can be ignored if you want the menu to be located at the perent menu of DCS world (under F10 other).
|
-- @param #table ParentMenu The parent menu. This parameter can be ignored if you want the menu to be located at the parent menu of DCS world (under F10 other).
|
||||||
-- @return #MENU_MISSION
|
-- @return #MENU_MISSION
|
||||||
function MENU_MISSION:New( MenuText, ParentMenu )
|
function MENU_MISSION:New( MenuText, ParentMenu )
|
||||||
|
|
||||||
@@ -470,7 +448,7 @@ do -- MENU_MISSION
|
|||||||
if MissionMenu == self then
|
if MissionMenu == self then
|
||||||
self:RemoveSubMenus()
|
self:RemoveSubMenus()
|
||||||
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
||||||
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
|
if (not MenuTag) or (MenuTag and self.MenuTag and MenuTag == self.MenuTag) then
|
||||||
self:F( { Text = self.MenuText, Path = self.MenuPath } )
|
self:F( { Text = self.MenuText, Path = self.MenuPath } )
|
||||||
if self.MenuPath ~= nil then
|
if self.MenuPath ~= nil then
|
||||||
missionCommands.removeItem( self.MenuPath )
|
missionCommands.removeItem( self.MenuPath )
|
||||||
@@ -487,8 +465,6 @@ do -- MENU_MISSION
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
do -- MENU_MISSION_COMMAND
|
do -- MENU_MISSION_COMMAND
|
||||||
@@ -503,7 +479,7 @@ do -- MENU_MISSION_COMMAND
|
|||||||
--
|
--
|
||||||
-- @field #MENU_MISSION_COMMAND
|
-- @field #MENU_MISSION_COMMAND
|
||||||
MENU_MISSION_COMMAND = {
|
MENU_MISSION_COMMAND = {
|
||||||
ClassName = "MENU_MISSION_COMMAND"
|
ClassName = "MENU_MISSION_COMMAND",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- MENU_MISSION constructor. Creates a new radio command item for a complete mission file, which can invoke a function with parameters.
|
--- MENU_MISSION constructor. Creates a new radio command item for a complete mission file, which can invoke a function with parameters.
|
||||||
@@ -556,7 +532,7 @@ do -- MENU_MISSION_COMMAND
|
|||||||
|
|
||||||
if MissionMenu == self then
|
if MissionMenu == self then
|
||||||
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
||||||
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
|
if (not MenuTag) or (MenuTag and self.MenuTag and MenuTag == self.MenuTag) then
|
||||||
self:F( { Text = self.MenuText, Path = self.MenuPath } )
|
self:F( { Text = self.MenuText, Path = self.MenuPath } )
|
||||||
if self.MenuPath ~= nil then
|
if self.MenuPath ~= nil then
|
||||||
missionCommands.removeItem( self.MenuPath )
|
missionCommands.removeItem( self.MenuPath )
|
||||||
@@ -575,8 +551,6 @@ do -- MENU_MISSION_COMMAND
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
do -- MENU_COALITION
|
do -- MENU_COALITION
|
||||||
|
|
||||||
--- @type MENU_COALITION
|
--- @type MENU_COALITION
|
||||||
@@ -627,14 +601,14 @@ do -- MENU_COALITION
|
|||||||
--
|
--
|
||||||
-- @field #MENU_COALITION
|
-- @field #MENU_COALITION
|
||||||
MENU_COALITION = {
|
MENU_COALITION = {
|
||||||
ClassName = "MENU_COALITION"
|
ClassName = "MENU_COALITION",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- MENU_COALITION constructor. Creates a new MENU_COALITION object and creates the menu for a complete coalition.
|
--- MENU_COALITION constructor. Creates a new MENU_COALITION object and creates the menu for a complete coalition.
|
||||||
-- @param #MENU_COALITION self
|
-- @param #MENU_COALITION self
|
||||||
-- @param DCS#coalition.side Coalition The coalition owning the menu.
|
-- @param DCS#coalition.side Coalition The coalition owning the menu.
|
||||||
-- @param #string MenuText The text for the menu.
|
-- @param #string MenuText The text for the menu.
|
||||||
-- @param #table ParentMenu The parent menu. This parameter can be ignored if you want the menu to be located at the perent menu of DCS world (under F10 other).
|
-- @param #table ParentMenu The parent menu. This parameter can be ignored if you want the menu to be located at the parent menu of DCS world (under F10 other).
|
||||||
-- @return #MENU_COALITION self
|
-- @return #MENU_COALITION self
|
||||||
function MENU_COALITION:New( Coalition, MenuText, ParentMenu )
|
function MENU_COALITION:New( Coalition, MenuText, ParentMenu )
|
||||||
|
|
||||||
@@ -693,7 +667,7 @@ do -- MENU_COALITION
|
|||||||
if CoalitionMenu == self then
|
if CoalitionMenu == self then
|
||||||
self:RemoveSubMenus()
|
self:RemoveSubMenus()
|
||||||
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
||||||
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) 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 } )
|
self:F( { Coalition = self.Coalition, Text = self.MenuText, Path = self.MenuPath } )
|
||||||
if self.MenuPath ~= nil then
|
if self.MenuPath ~= nil then
|
||||||
missionCommands.removeItemForCoalition( self.Coalition, self.MenuPath )
|
missionCommands.removeItemForCoalition( self.Coalition, self.MenuPath )
|
||||||
@@ -712,8 +686,6 @@ do -- MENU_COALITION
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
do -- MENU_COALITION_COMMAND
|
do -- MENU_COALITION_COMMAND
|
||||||
|
|
||||||
--- @type MENU_COALITION_COMMAND
|
--- @type MENU_COALITION_COMMAND
|
||||||
@@ -726,7 +698,7 @@ do -- MENU_COALITION_COMMAND
|
|||||||
--
|
--
|
||||||
-- @field #MENU_COALITION_COMMAND
|
-- @field #MENU_COALITION_COMMAND
|
||||||
MENU_COALITION_COMMAND = {
|
MENU_COALITION_COMMAND = {
|
||||||
ClassName = "MENU_COALITION_COMMAND"
|
ClassName = "MENU_COALITION_COMMAND",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- MENU_COALITION constructor. Creates a new radio command item for a coalition, which can invoke a function with parameters.
|
--- MENU_COALITION constructor. Creates a new radio command item for a coalition, which can invoke a function with parameters.
|
||||||
@@ -760,7 +732,6 @@ do -- MENU_COALITION_COMMAND
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Refreshes a radio item for a coalition
|
--- Refreshes a radio item for a coalition
|
||||||
-- @param #MENU_COALITION_COMMAND self
|
-- @param #MENU_COALITION_COMMAND self
|
||||||
-- @return #MENU_COALITION_COMMAND
|
-- @return #MENU_COALITION_COMMAND
|
||||||
@@ -784,7 +755,7 @@ do -- MENU_COALITION_COMMAND
|
|||||||
|
|
||||||
if CoalitionMenu == self then
|
if CoalitionMenu == self then
|
||||||
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
||||||
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) 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 } )
|
self:F( { Coalition = self.Coalition, Text = self.MenuText, Path = self.MenuPath } )
|
||||||
if self.MenuPath ~= nil then
|
if self.MenuPath ~= nil then
|
||||||
missionCommands.removeItemForCoalition( self.Coalition, self.MenuPath )
|
missionCommands.removeItemForCoalition( self.Coalition, self.MenuPath )
|
||||||
@@ -803,7 +774,6 @@ do -- MENU_COALITION_COMMAND
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- MENU_GROUP
|
--- MENU_GROUP
|
||||||
|
|
||||||
do
|
do
|
||||||
@@ -817,7 +787,6 @@ do
|
|||||||
--- @type MENU_GROUP
|
--- @type MENU_GROUP
|
||||||
-- @extends Core.Menu#MENU_BASE
|
-- @extends Core.Menu#MENU_BASE
|
||||||
|
|
||||||
|
|
||||||
--- Manages the main menus for @{Wrapper.Group}s.
|
--- Manages the main menus for @{Wrapper.Group}s.
|
||||||
--
|
--
|
||||||
-- You can add menus with the @{#MENU_GROUP.New} method, which constructs a MENU_GROUP object and returns you the object reference.
|
-- You can add menus with the @{#MENU_GROUP.New} method, which constructs a MENU_GROUP object and returns you the object reference.
|
||||||
@@ -875,7 +844,7 @@ do
|
|||||||
--
|
--
|
||||||
-- @field #MENU_GROUP
|
-- @field #MENU_GROUP
|
||||||
MENU_GROUP = {
|
MENU_GROUP = {
|
||||||
ClassName = "MENU_GROUP"
|
ClassName = "MENU_GROUP",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- MENU_GROUP constructor. Creates a new radio menu item for a group.
|
--- MENU_GROUP constructor. Creates a new radio menu item for a group.
|
||||||
@@ -938,7 +907,6 @@ do
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Removes the main menu and sub menus recursively of this MENU_GROUP.
|
--- Removes the main menu and sub menus recursively of this MENU_GROUP.
|
||||||
-- @param #MENU_GROUP self
|
-- @param #MENU_GROUP self
|
||||||
-- @param MenuStamp
|
-- @param MenuStamp
|
||||||
@@ -953,7 +921,7 @@ do
|
|||||||
if GroupMenu == self then
|
if GroupMenu == self then
|
||||||
self:RemoveSubMenus( MenuStamp, MenuTag )
|
self:RemoveSubMenus( MenuStamp, MenuTag )
|
||||||
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
||||||
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
|
if (not MenuTag) or (MenuTag and self.MenuTag and MenuTag == self.MenuTag) then
|
||||||
if self.MenuPath ~= nil then
|
if self.MenuPath ~= nil then
|
||||||
self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } )
|
self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } )
|
||||||
missionCommands.removeItemForGroup( self.GroupID, self.MenuPath )
|
missionCommands.removeItemForGroup( self.GroupID, self.MenuPath )
|
||||||
@@ -971,7 +939,6 @@ do
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- @type MENU_GROUP_COMMAND
|
--- @type MENU_GROUP_COMMAND
|
||||||
-- @extends Core.Menu#MENU_COMMAND_BASE
|
-- @extends Core.Menu#MENU_COMMAND_BASE
|
||||||
|
|
||||||
@@ -981,7 +948,7 @@ do
|
|||||||
--
|
--
|
||||||
-- @field #MENU_GROUP_COMMAND
|
-- @field #MENU_GROUP_COMMAND
|
||||||
MENU_GROUP_COMMAND = {
|
MENU_GROUP_COMMAND = {
|
||||||
ClassName = "MENU_GROUP_COMMAND"
|
ClassName = "MENU_GROUP_COMMAND",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Creates a new radio command item for a group
|
--- Creates a new radio command item for a group
|
||||||
@@ -1043,9 +1010,9 @@ do
|
|||||||
|
|
||||||
if GroupMenu == self then
|
if GroupMenu == self then
|
||||||
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
||||||
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
|
if (not MenuTag) or (MenuTag and self.MenuTag and MenuTag == self.MenuTag) then
|
||||||
if self.MenuPath ~= nil then
|
if self.MenuPath ~= nil then
|
||||||
self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } )
|
self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } )
|
||||||
missionCommands.removeItemForGroup( self.GroupID, self.MenuPath )
|
missionCommands.removeItemForGroup( self.GroupID, self.MenuPath )
|
||||||
end
|
end
|
||||||
MENU_INDEX:ClearGroupMenu( self.Group, Path )
|
MENU_INDEX:ClearGroupMenu( self.Group, Path )
|
||||||
@@ -1069,7 +1036,6 @@ do
|
|||||||
--- @type MENU_GROUP_DELAYED
|
--- @type MENU_GROUP_DELAYED
|
||||||
-- @extends Core.Menu#MENU_BASE
|
-- @extends Core.Menu#MENU_BASE
|
||||||
|
|
||||||
|
|
||||||
--- The MENU_GROUP_DELAYED class manages the main menus for groups.
|
--- The MENU_GROUP_DELAYED class manages the main menus for groups.
|
||||||
-- You can add menus with the @{#MENU_GROUP.New} method, which constructs a MENU_GROUP object and returns you the object reference.
|
-- You can add menus with the @{#MENU_GROUP.New} method, which constructs a MENU_GROUP object and returns you the object reference.
|
||||||
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_GROUP.Remove}.
|
-- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_GROUP.Remove}.
|
||||||
@@ -1079,7 +1045,7 @@ do
|
|||||||
--
|
--
|
||||||
-- @field #MENU_GROUP_DELAYED
|
-- @field #MENU_GROUP_DELAYED
|
||||||
MENU_GROUP_DELAYED = {
|
MENU_GROUP_DELAYED = {
|
||||||
ClassName = "MENU_GROUP_DELAYED"
|
ClassName = "MENU_GROUP_DELAYED",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- MENU_GROUP_DELAYED constructor. Creates a new radio menu item for a group.
|
--- MENU_GROUP_DELAYED constructor. Creates a new radio menu item for a group.
|
||||||
@@ -1116,7 +1082,6 @@ do
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Refreshes a new radio item for a group and submenus
|
--- Refreshes a new radio item for a group and submenus
|
||||||
-- @param #MENU_GROUP_DELAYED self
|
-- @param #MENU_GROUP_DELAYED self
|
||||||
-- @return #MENU_GROUP_DELAYED
|
-- @return #MENU_GROUP_DELAYED
|
||||||
@@ -1135,7 +1100,6 @@ do
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Refreshes a new radio item for a group and submenus
|
--- Refreshes a new radio item for a group and submenus
|
||||||
-- @param #MENU_GROUP_DELAYED self
|
-- @param #MENU_GROUP_DELAYED self
|
||||||
-- @return #MENU_GROUP_DELAYED
|
-- @return #MENU_GROUP_DELAYED
|
||||||
@@ -1167,7 +1131,6 @@ do
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Removes the main menu and sub menus recursively of this MENU_GROUP.
|
--- Removes the main menu and sub menus recursively of this MENU_GROUP.
|
||||||
-- @param #MENU_GROUP_DELAYED self
|
-- @param #MENU_GROUP_DELAYED self
|
||||||
-- @param MenuStamp
|
-- @param MenuStamp
|
||||||
@@ -1182,7 +1145,7 @@ do
|
|||||||
if GroupMenu == self then
|
if GroupMenu == self then
|
||||||
self:RemoveSubMenus( MenuStamp, MenuTag )
|
self:RemoveSubMenus( MenuStamp, MenuTag )
|
||||||
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
||||||
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
|
if (not MenuTag) or (MenuTag and self.MenuTag and MenuTag == self.MenuTag) then
|
||||||
if self.MenuPath ~= nil then
|
if self.MenuPath ~= nil then
|
||||||
self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } )
|
self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } )
|
||||||
missionCommands.removeItemForGroup( self.GroupID, self.MenuPath )
|
missionCommands.removeItemForGroup( self.GroupID, self.MenuPath )
|
||||||
@@ -1200,7 +1163,6 @@ do
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- @type MENU_GROUP_COMMAND_DELAYED
|
--- @type MENU_GROUP_COMMAND_DELAYED
|
||||||
-- @extends Core.Menu#MENU_COMMAND_BASE
|
-- @extends Core.Menu#MENU_COMMAND_BASE
|
||||||
|
|
||||||
@@ -1211,7 +1173,7 @@ do
|
|||||||
--
|
--
|
||||||
-- @field #MENU_GROUP_COMMAND_DELAYED
|
-- @field #MENU_GROUP_COMMAND_DELAYED
|
||||||
MENU_GROUP_COMMAND_DELAYED = {
|
MENU_GROUP_COMMAND_DELAYED = {
|
||||||
ClassName = "MENU_GROUP_COMMAND_DELAYED"
|
ClassName = "MENU_GROUP_COMMAND_DELAYED",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Creates a new radio command item for a group
|
--- Creates a new radio command item for a group
|
||||||
@@ -1292,7 +1254,7 @@ do
|
|||||||
|
|
||||||
if GroupMenu == self then
|
if GroupMenu == self then
|
||||||
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
if not MenuStamp or self.MenuStamp ~= MenuStamp then
|
||||||
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
|
if (not MenuTag) or (MenuTag and self.MenuTag and MenuTag == self.MenuTag) then
|
||||||
if self.MenuPath ~= nil then
|
if self.MenuPath ~= nil then
|
||||||
self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } )
|
self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } )
|
||||||
missionCommands.removeItemForGroup( self.GroupID, self.MenuPath )
|
missionCommands.removeItemForGroup( self.GroupID, self.MenuPath )
|
||||||
|
|||||||
@@ -56,9 +56,9 @@
|
|||||||
--
|
--
|
||||||
-- @field #MESSAGE
|
-- @field #MESSAGE
|
||||||
MESSAGE = {
|
MESSAGE = {
|
||||||
ClassName = "MESSAGE",
|
ClassName = "MESSAGE",
|
||||||
MessageCategory = 0,
|
MessageCategory = 0,
|
||||||
MessageID = 0,
|
MessageID = 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Message Types
|
--- Message Types
|
||||||
@@ -68,10 +68,9 @@ MESSAGE.Type = {
|
|||||||
Information = "Information",
|
Information = "Information",
|
||||||
Briefing = "Briefing Report",
|
Briefing = "Briefing Report",
|
||||||
Overview = "Overview Report",
|
Overview = "Overview Report",
|
||||||
Detailed = "Detailed Report"
|
Detailed = "Detailed Report",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
--- Creates a new MESSAGE object. Note that these MESSAGE objects are not yet displayed on the display panel. You must use the functions @{ToClient} or @{ToCoalition} or @{ToAll} to send these Messages to the respective recipients.
|
--- Creates a new MESSAGE object. Note that these MESSAGE objects are not yet displayed on the display panel. You must use the functions @{ToClient} or @{ToCoalition} or @{ToAll} to send these Messages to the respective recipients.
|
||||||
-- @param self
|
-- @param self
|
||||||
-- @param #string MessageText is the text of the Message.
|
-- @param #string MessageText is the text of the Message.
|
||||||
@@ -80,25 +79,26 @@ MESSAGE.Type = {
|
|||||||
-- @param #boolean ClearScreen (optional) Clear all previous messages if true.
|
-- @param #boolean ClearScreen (optional) Clear all previous messages if true.
|
||||||
-- @return #MESSAGE
|
-- @return #MESSAGE
|
||||||
-- @usage
|
-- @usage
|
||||||
-- -- Create a series of new Messages.
|
--
|
||||||
-- -- MessageAll is meant to be sent to all players, for 25 seconds, and is classified as "Score".
|
-- -- Create a series of new Messages.
|
||||||
-- -- MessageRED is meant to be sent to the RED players only, for 10 seconds, and is classified as "End of Mission", with ID "Win".
|
-- -- MessageAll is meant to be sent to all players, for 25 seconds, and is classified as "Score".
|
||||||
-- -- MessageClient1 is meant to be sent to a Client, for 25 seconds, and is classified as "Score", with ID "Score".
|
-- -- MessageRED is meant to be sent to the RED players only, for 10 seconds, and is classified as "End of Mission", with ID "Win".
|
||||||
-- -- MessageClient1 is meant to be sent to a Client, for 25 seconds, and is classified as "Score", with ID "Score".
|
-- -- MessageClient1 is meant to be sent to a Client, for 25 seconds, and is classified as "Score", with ID "Score".
|
||||||
-- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", 25, "End of Mission" )
|
-- -- MessageClient1 is meant to be sent to a Client, for 25 seconds, and is classified as "Score", with ID "Score".
|
||||||
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", 25, "Penalty" )
|
-- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", 25, "End of Mission" )
|
||||||
-- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", 25, "Score" )
|
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", 25, "Penalty" )
|
||||||
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", 25, "Score")
|
-- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", 25, "Score" )
|
||||||
|
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", 25, "Score")
|
||||||
|
--
|
||||||
function MESSAGE:New( MessageText, MessageDuration, MessageCategory, ClearScreen )
|
function MESSAGE:New( MessageText, MessageDuration, MessageCategory, ClearScreen )
|
||||||
local self = BASE:Inherit( self, BASE:New() )
|
local self = BASE:Inherit( self, BASE:New() )
|
||||||
self:F( { MessageText, MessageDuration, MessageCategory } )
|
self:F( { MessageText, MessageDuration, MessageCategory } )
|
||||||
|
|
||||||
|
|
||||||
self.MessageType = nil
|
self.MessageType = nil
|
||||||
|
|
||||||
-- When no MessageCategory is given, we don't show it as a title...
|
-- When no MessageCategory is given, we don't show it as a title...
|
||||||
if MessageCategory and MessageCategory ~= "" then
|
if MessageCategory and MessageCategory ~= "" then
|
||||||
if MessageCategory:sub(-1) ~= "\n" then
|
if MessageCategory:sub( -1 ) ~= "\n" then
|
||||||
self.MessageCategory = MessageCategory .. ": "
|
self.MessageCategory = MessageCategory .. ": "
|
||||||
else
|
else
|
||||||
self.MessageCategory = MessageCategory:sub( 1, -2 ) .. ":\n"
|
self.MessageCategory = MessageCategory:sub( 1, -2 ) .. ":\n"
|
||||||
@@ -107,23 +107,22 @@ function MESSAGE:New( MessageText, MessageDuration, MessageCategory, ClearScreen
|
|||||||
self.MessageCategory = ""
|
self.MessageCategory = ""
|
||||||
end
|
end
|
||||||
|
|
||||||
self.ClearScreen=false
|
self.ClearScreen = false
|
||||||
if ClearScreen~=nil then
|
if ClearScreen ~= nil then
|
||||||
self.ClearScreen=ClearScreen
|
self.ClearScreen = ClearScreen
|
||||||
end
|
end
|
||||||
|
|
||||||
self.MessageDuration = MessageDuration or 5
|
self.MessageDuration = MessageDuration or 5
|
||||||
self.MessageTime = timer.getTime()
|
self.MessageTime = timer.getTime()
|
||||||
self.MessageText = MessageText:gsub("^\n","",1):gsub("\n$","",1)
|
self.MessageText = MessageText:gsub( "^\n", "", 1 ):gsub( "\n$", "", 1 )
|
||||||
|
|
||||||
self.MessageSent = false
|
self.MessageSent = false
|
||||||
self.MessageGroup = false
|
self.MessageGroup = false
|
||||||
self.MessageCoalition = false
|
self.MessageCoalition = false
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Creates a new MESSAGE object of a certain type.
|
--- Creates a new MESSAGE object of a certain type.
|
||||||
-- Note that these MESSAGE objects are not yet displayed on the display panel.
|
-- Note that these MESSAGE objects are not yet displayed on the display panel.
|
||||||
-- You must use the functions @{ToClient} or @{ToCoalition} or @{ToAll} to send these Messages to the respective recipients.
|
-- You must use the functions @{ToClient} or @{ToCoalition} or @{ToAll} to send these Messages to the respective recipients.
|
||||||
@@ -134,10 +133,12 @@ end
|
|||||||
-- @param #boolean ClearScreen (optional) Clear all previous messages.
|
-- @param #boolean ClearScreen (optional) Clear all previous messages.
|
||||||
-- @return #MESSAGE
|
-- @return #MESSAGE
|
||||||
-- @usage
|
-- @usage
|
||||||
|
--
|
||||||
-- MessageAll = MESSAGE:NewType( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", MESSAGE.Type.Information )
|
-- MessageAll = MESSAGE:NewType( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", MESSAGE.Type.Information )
|
||||||
-- MessageRED = MESSAGE:NewType( "To the RED Players: You receive a penalty because you've killed one of your own units", MESSAGE.Type.Information )
|
-- MessageRED = MESSAGE:NewType( "To the RED Players: You receive a penalty because you've killed one of your own units", MESSAGE.Type.Information )
|
||||||
-- MessageClient1 = MESSAGE:NewType( "Congratulations, you've just hit a target", MESSAGE.Type.Update )
|
-- MessageClient1 = MESSAGE:NewType( "Congratulations, you've just hit a target", MESSAGE.Type.Update )
|
||||||
-- MessageClient2 = MESSAGE:NewType( "Congratulations, you've just killed a target", MESSAGE.Type.Update )
|
-- MessageClient2 = MESSAGE:NewType( "Congratulations, you've just killed a target", MESSAGE.Type.Update )
|
||||||
|
--
|
||||||
function MESSAGE:NewType( MessageText, MessageType, ClearScreen )
|
function MESSAGE:NewType( MessageText, MessageType, ClearScreen )
|
||||||
|
|
||||||
local self = BASE:Inherit( self, BASE:New() )
|
local self = BASE:Inherit( self, BASE:New() )
|
||||||
@@ -145,69 +146,67 @@ function MESSAGE:NewType( MessageText, MessageType, ClearScreen )
|
|||||||
|
|
||||||
self.MessageType = MessageType
|
self.MessageType = MessageType
|
||||||
|
|
||||||
self.ClearScreen=false
|
self.ClearScreen = false
|
||||||
if ClearScreen~=nil then
|
if ClearScreen ~= nil then
|
||||||
self.ClearScreen=ClearScreen
|
self.ClearScreen = ClearScreen
|
||||||
end
|
end
|
||||||
|
|
||||||
self.MessageTime = timer.getTime()
|
self.MessageTime = timer.getTime()
|
||||||
self.MessageText = MessageText:gsub("^\n","",1):gsub("\n$","",1)
|
self.MessageText = MessageText:gsub( "^\n", "", 1 ):gsub( "\n$", "", 1 )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- Clears all previous messages from the screen before the new message is displayed. Not that this must come before all functions starting with ToX(), e.g. ToAll(), ToGroup() etc.
|
--- Clears all previous messages from the screen before the new message is displayed. Not that this must come before all functions starting with ToX(), e.g. ToAll(), ToGroup() etc.
|
||||||
-- @param #MESSAGE self
|
-- @param #MESSAGE self
|
||||||
-- @return #MESSAGE
|
-- @return #MESSAGE
|
||||||
function MESSAGE:Clear()
|
function MESSAGE:Clear()
|
||||||
self:F()
|
self:F()
|
||||||
self.ClearScreen=true
|
self.ClearScreen = true
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- Sends a MESSAGE to a Client Group. Note that the Group needs to be defined within the ME with the skillset "Client" or "Player".
|
--- Sends a MESSAGE to a Client Group. Note that the Group needs to be defined within the ME with the skillset "Client" or "Player".
|
||||||
-- @param #MESSAGE self
|
-- @param #MESSAGE self
|
||||||
-- @param Wrapper.Client#CLIENT Client is the Group of the Client.
|
-- @param Wrapper.Client#CLIENT Client is the Group of the Client.
|
||||||
-- @param Core.Settings#SETTINGS Settings Settings used to display the message.
|
-- @param Core.Settings#SETTINGS Settings Settings used to display the message.
|
||||||
-- @return #MESSAGE
|
-- @return #MESSAGE
|
||||||
-- @usage
|
-- @usage
|
||||||
-- -- Send the 2 messages created with the @{New} method to the Client Group.
|
|
||||||
-- -- Note that the Message of MessageClient2 is overwriting the Message of MessageClient1.
|
|
||||||
-- ClientGroup = Group.getByName( "ClientGroup" )
|
|
||||||
--
|
--
|
||||||
-- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" ):ToClient( ClientGroup )
|
-- -- Send the 2 messages created with the @{New} method to the Client Group.
|
||||||
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" ):ToClient( ClientGroup )
|
-- -- Note that the Message of MessageClient2 is overwriting the Message of MessageClient1.
|
||||||
-- or
|
-- ClientGroup = Group.getByName( "ClientGroup" )
|
||||||
-- MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" ):ToClient( ClientGroup )
|
--
|
||||||
-- MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" ):ToClient( ClientGroup )
|
-- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" ):ToClient( ClientGroup )
|
||||||
-- or
|
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" ):ToClient( ClientGroup )
|
||||||
-- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" )
|
-- or
|
||||||
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" )
|
-- MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" ):ToClient( ClientGroup )
|
||||||
-- MessageClient1:ToClient( ClientGroup )
|
-- MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" ):ToClient( ClientGroup )
|
||||||
-- MessageClient2:ToClient( ClientGroup )
|
-- or
|
||||||
|
-- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" )
|
||||||
|
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" )
|
||||||
|
-- MessageClient1:ToClient( ClientGroup )
|
||||||
|
-- MessageClient2:ToClient( ClientGroup )
|
||||||
|
--
|
||||||
function MESSAGE:ToClient( Client, Settings )
|
function MESSAGE:ToClient( Client, Settings )
|
||||||
self:F( Client )
|
self:F( Client )
|
||||||
|
|
||||||
if Client and Client:GetClientGroupID() then
|
if Client and Client:GetClientGroupID() then
|
||||||
|
|
||||||
if self.MessageType then
|
if self.MessageType then
|
||||||
local Settings = Settings or ( Client and _DATABASE:GetPlayerSettings( Client:GetPlayerName() ) ) or _SETTINGS -- Core.Settings#SETTINGS
|
local Settings = Settings or (Client and _DATABASE:GetPlayerSettings( Client:GetPlayerName() )) or _SETTINGS -- Core.Settings#SETTINGS
|
||||||
self.MessageDuration = Settings:GetMessageTime( self.MessageType )
|
self.MessageDuration = Settings:GetMessageTime( self.MessageType )
|
||||||
self.MessageCategory = "" -- self.MessageType .. ": "
|
self.MessageCategory = "" -- self.MessageType .. ": "
|
||||||
end
|
end
|
||||||
|
|
||||||
if self.MessageDuration ~= 0 then
|
if self.MessageDuration ~= 0 then
|
||||||
local ClientGroupID = Client:GetClientGroupID()
|
local ClientGroupID = Client:GetClientGroupID()
|
||||||
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
|
self:T( self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ) .. " / " .. self.MessageDuration )
|
||||||
trigger.action.outTextForGroup( ClientGroupID, self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration , self.ClearScreen)
|
trigger.action.outTextForGroup( ClientGroupID, self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ), self.MessageDuration, self.ClearScreen )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Sends a MESSAGE to a Group.
|
--- Sends a MESSAGE to a Group.
|
||||||
@@ -220,14 +219,14 @@ function MESSAGE:ToGroup( Group, Settings )
|
|||||||
if Group then
|
if Group then
|
||||||
|
|
||||||
if self.MessageType then
|
if self.MessageType then
|
||||||
local Settings = Settings or ( Group and _DATABASE:GetPlayerSettings( Group:GetPlayerName() ) ) or _SETTINGS -- Core.Settings#SETTINGS
|
local Settings = Settings or (Group and _DATABASE:GetPlayerSettings( Group:GetPlayerName() )) or _SETTINGS -- Core.Settings#SETTINGS
|
||||||
self.MessageDuration = Settings:GetMessageTime( self.MessageType )
|
self.MessageDuration = Settings:GetMessageTime( self.MessageType )
|
||||||
self.MessageCategory = "" -- self.MessageType .. ": "
|
self.MessageCategory = "" -- self.MessageType .. ": "
|
||||||
end
|
end
|
||||||
|
|
||||||
if self.MessageDuration ~= 0 then
|
if self.MessageDuration ~= 0 then
|
||||||
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
|
self:T( self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ) .. " / " .. self.MessageDuration )
|
||||||
trigger.action.outTextForGroup( Group:GetID(), self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration, self.ClearScreen )
|
trigger.action.outTextForGroup( Group:GetID(), self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ), self.MessageDuration, self.ClearScreen )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -237,38 +236,42 @@ end
|
|||||||
-- @param #MESSAGE self
|
-- @param #MESSAGE self
|
||||||
-- @return #MESSAGE
|
-- @return #MESSAGE
|
||||||
-- @usage
|
-- @usage
|
||||||
-- -- Send a message created with the @{New} method to the BLUE coalition.
|
--
|
||||||
-- MessageBLUE = MESSAGE:New( "To the BLUE Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToBlue()
|
-- -- Send a message created with the @{New} method to the BLUE coalition.
|
||||||
-- or
|
-- MessageBLUE = MESSAGE:New( "To the BLUE Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToBlue()
|
||||||
-- MESSAGE:New( "To the BLUE Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToBlue()
|
-- or
|
||||||
-- or
|
-- MESSAGE:New( "To the BLUE Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToBlue()
|
||||||
-- MessageBLUE = MESSAGE:New( "To the BLUE Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" )
|
-- or
|
||||||
-- MessageBLUE:ToBlue()
|
-- MessageBLUE = MESSAGE:New( "To the BLUE Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" )
|
||||||
|
-- MessageBLUE:ToBlue()
|
||||||
|
--
|
||||||
function MESSAGE:ToBlue()
|
function MESSAGE:ToBlue()
|
||||||
self:F()
|
self:F()
|
||||||
|
|
||||||
self:ToCoalition( coalition.side.BLUE )
|
self:ToCoalition( coalition.side.BLUE )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Sends a MESSAGE to the Red Coalition.
|
--- Sends a MESSAGE to the Red Coalition.
|
||||||
-- @param #MESSAGE self
|
-- @param #MESSAGE self
|
||||||
-- @return #MESSAGE
|
-- @return #MESSAGE
|
||||||
-- @usage
|
-- @usage
|
||||||
-- -- Send a message created with the @{New} method to the RED coalition.
|
--
|
||||||
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToRed()
|
-- -- Send a message created with the @{New} method to the RED coalition.
|
||||||
-- or
|
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToRed()
|
||||||
-- MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToRed()
|
-- or
|
||||||
-- or
|
-- MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToRed()
|
||||||
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" )
|
-- or
|
||||||
-- MessageRED:ToRed()
|
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" )
|
||||||
function MESSAGE:ToRed( )
|
-- MessageRED:ToRed()
|
||||||
self:F()
|
--
|
||||||
|
function MESSAGE:ToRed()
|
||||||
|
self:F()
|
||||||
|
|
||||||
self:ToCoalition( coalition.side.RED )
|
self:ToCoalition( coalition.side.RED )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Sends a MESSAGE to a Coalition.
|
--- Sends a MESSAGE to a Coalition.
|
||||||
@@ -277,15 +280,17 @@ end
|
|||||||
-- @param Core.Settings#SETTINGS Settings (Optional) Settings for message display.
|
-- @param Core.Settings#SETTINGS Settings (Optional) Settings for message display.
|
||||||
-- @return #MESSAGE Message object.
|
-- @return #MESSAGE Message object.
|
||||||
-- @usage
|
-- @usage
|
||||||
-- -- Send a message created with the @{New} method to the RED coalition.
|
--
|
||||||
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToCoalition( coalition.side.RED )
|
-- -- Send a message created with the @{New} method to the RED coalition.
|
||||||
-- or
|
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToCoalition( coalition.side.RED )
|
||||||
-- MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToCoalition( coalition.side.RED )
|
-- or
|
||||||
-- or
|
-- MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToCoalition( coalition.side.RED )
|
||||||
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" )
|
-- or
|
||||||
-- MessageRED:ToCoalition( coalition.side.RED )
|
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" )
|
||||||
|
-- MessageRED:ToCoalition( coalition.side.RED )
|
||||||
|
--
|
||||||
function MESSAGE:ToCoalition( CoalitionSide, Settings )
|
function MESSAGE:ToCoalition( CoalitionSide, Settings )
|
||||||
self:F( CoalitionSide )
|
self:F( CoalitionSide )
|
||||||
|
|
||||||
if self.MessageType then
|
if self.MessageType then
|
||||||
local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS
|
local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS
|
||||||
@@ -293,14 +298,14 @@ function MESSAGE:ToCoalition( CoalitionSide, Settings )
|
|||||||
self.MessageCategory = "" -- self.MessageType .. ": "
|
self.MessageCategory = "" -- self.MessageType .. ": "
|
||||||
end
|
end
|
||||||
|
|
||||||
if CoalitionSide then
|
if CoalitionSide then
|
||||||
if self.MessageDuration ~= 0 then
|
if self.MessageDuration ~= 0 then
|
||||||
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
|
self:T( self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ) .. " / " .. self.MessageDuration )
|
||||||
trigger.action.outTextForCoalition( CoalitionSide, self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration, self.ClearScreen )
|
trigger.action.outTextForCoalition( CoalitionSide, self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ), self.MessageDuration, self.ClearScreen )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Sends a MESSAGE to a Coalition if the given Condition is true.
|
--- Sends a MESSAGE to a Coalition if the given Condition is true.
|
||||||
@@ -323,14 +328,16 @@ end
|
|||||||
-- @param Core.Settings#Settings Settings (Optional) Settings for message display.
|
-- @param Core.Settings#Settings Settings (Optional) Settings for message display.
|
||||||
-- @return #MESSAGE
|
-- @return #MESSAGE
|
||||||
-- @usage
|
-- @usage
|
||||||
-- -- Send a message created to all players.
|
--
|
||||||
-- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" ):ToAll()
|
-- -- Send a message created to all players.
|
||||||
-- or
|
-- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" ):ToAll()
|
||||||
-- MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" ):ToAll()
|
-- or
|
||||||
-- or
|
-- MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" ):ToAll()
|
||||||
-- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" )
|
-- or
|
||||||
-- MessageAll:ToAll()
|
-- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" )
|
||||||
function MESSAGE:ToAll(Settings)
|
-- MessageAll:ToAll()
|
||||||
|
--
|
||||||
|
function MESSAGE:ToAll( Settings )
|
||||||
self:F()
|
self:F()
|
||||||
|
|
||||||
if self.MessageType then
|
if self.MessageType then
|
||||||
@@ -340,14 +347,13 @@ function MESSAGE:ToAll(Settings)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if self.MessageDuration ~= 0 then
|
if self.MessageDuration ~= 0 then
|
||||||
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
|
self:T( self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ) .. " / " .. self.MessageDuration )
|
||||||
trigger.action.outText( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration, self.ClearScreen )
|
trigger.action.outText( self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ), self.MessageDuration, self.ClearScreen )
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Sends a MESSAGE to all players if the given Condition is true.
|
--- Sends a MESSAGE to all players if the given Condition is true.
|
||||||
-- @param #MESSAGE self
|
-- @param #MESSAGE self
|
||||||
-- @return #MESSAGE
|
-- @return #MESSAGE
|
||||||
@@ -357,5 +363,5 @@ function MESSAGE:ToAllIf( Condition )
|
|||||||
self:ToAll()
|
self:ToAll()
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,832 +0,0 @@
|
|||||||
--- **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?
|
|
||||||
--
|
|
||||||
-- * Radio transmissions consist of **sound files** that are broadcasted on a specific **frequency** (e.g. 115MHz) and **modulation** (e.g. AM),
|
|
||||||
-- * They can be **subtitled** for a specific **duration**, the **power** in Watts of the transmiter's antenna can be set, and the transmission can be **looped**.
|
|
||||||
--
|
|
||||||
-- How to supply DCS my own Sound Files?
|
|
||||||
--
|
|
||||||
-- * Your sound files need to be encoded in **.ogg** or .wav,
|
|
||||||
-- * Your sound files should be **as tiny as possible**. It is suggested you encode in .ogg with low bitrate and sampling settings,
|
|
||||||
-- * They need to be added in .\l10n\DEFAULT\ in you .miz file (wich can be decompressed like a .zip file),
|
|
||||||
-- * For 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 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 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.
|
|
||||||
--
|
|
||||||
-- ===
|
|
||||||
--
|
|
||||||
-- ### Authors: Hugues "Grey_Echo" Bousquet, funkyfranky
|
|
||||||
--
|
|
||||||
-- @module Core.Radio
|
|
||||||
-- @image Core_Radio.JPG
|
|
||||||
|
|
||||||
|
|
||||||
--- Models the radio capability.
|
|
||||||
--
|
|
||||||
-- ## RADIO usage
|
|
||||||
--
|
|
||||||
-- There are 3 steps to a successful radio transmission.
|
|
||||||
--
|
|
||||||
-- * First, you need to **"add a @{#RADIO} object** to your @{Wrapper.Positionable#POSITIONABLE}. This is done using the @{Wrapper.Positionable#POSITIONABLE.GetRadio}() function,
|
|
||||||
-- * Then, you will **set the relevant parameters** to the transmission (see below),
|
|
||||||
-- * When done, you can actually **broadcast the transmission** (i.e. play the sound) with the @{RADIO.Broadcast}() function.
|
|
||||||
--
|
|
||||||
-- Methods to set relevant parameters for both a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP} or any other @{Wrapper.Positionable#POSITIONABLE}
|
|
||||||
--
|
|
||||||
-- * @{#RADIO.SetFileName}() : Sets the file name of your sound file (e.g. "Noise.ogg"),
|
|
||||||
-- * @{#RADIO.SetFrequency}() : Sets the frequency of your transmission.
|
|
||||||
-- * @{#RADIO.SetModulation}() : Sets the modulation of your transmission.
|
|
||||||
-- * @{#RADIO.SetLoop}() : Choose if you want the transmission to be looped. If you need your transmission to be looped, you might need a @{#BEACON} instead...
|
|
||||||
--
|
|
||||||
-- Additional Methods to set relevant parameters if the 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 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?
|
|
||||||
--
|
|
||||||
-- * 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 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 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 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",
|
|
||||||
FileName = "",
|
|
||||||
Frequency = 0,
|
|
||||||
Modulation = radio.modulation.AM,
|
|
||||||
Subtitle = "",
|
|
||||||
SubtitleDuration = 0,
|
|
||||||
Power = 100,
|
|
||||||
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.
|
|
||||||
-- @param #RADIO self
|
|
||||||
-- @param Wrapper.Positionable#POSITIONABLE Positionable The @{Positionable} that will receive radio capabilities.
|
|
||||||
-- @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:F(Positionable)
|
|
||||||
|
|
||||||
if Positionable:GetPointVec2() then -- It's stupid, but the only way I found to make sure positionable is valid
|
|
||||||
self.Positionable = Positionable
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
self:E({error="The passed positionable is invalid, no RADIO created!", positionable=Positionable})
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
--- 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
|
|
||||||
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?", FileName})
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
--- 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 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 <= 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
|
|
||||||
|
|
||||||
local commandSetFrequency={
|
|
||||||
id = "SetFrequency",
|
|
||||||
params = {
|
|
||||||
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.", Frequency})
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Set AM or FM modulation of the radio transmitter.
|
|
||||||
-- @param #RADIO self
|
|
||||||
-- @param #number Modulation Modulation is either radio.modulation.AM or radio.modulation.FM.
|
|
||||||
-- @return #RADIO self
|
|
||||||
function RADIO:SetModulation(Modulation)
|
|
||||||
self:F2(Modulation)
|
|
||||||
if type(Modulation) == "number" then
|
|
||||||
if Modulation == radio.modulation.AM or Modulation == radio.modulation.FM then --TODO Maybe make this future proof if ED decides to add an other modulation ?
|
|
||||||
self.Modulation = Modulation
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
end
|
|
||||||
self:E({"Modulation is invalid. Use DCS's enum radio.modulation. Modulation unchanged.", self.Modulation})
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Check validity of the power passed and sets RADIO.Power
|
|
||||||
-- @param #RADIO self
|
|
||||||
-- @param #number Power 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
|
|
||||||
else
|
|
||||||
self:E({"Power is invalid. Power unchanged.", self.Power})
|
|
||||||
end
|
|
||||||
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Set message looping on or off.
|
|
||||||
-- @param #RADIO self
|
|
||||||
-- @param #boolean Loop If true, message is repeated indefinitely.
|
|
||||||
-- @return #RADIO self
|
|
||||||
function RADIO:SetLoop(Loop)
|
|
||||||
self:F2(Loop)
|
|
||||||
if type(Loop) == "boolean" then
|
|
||||||
self.Loop = Loop
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
self:E({"Loop is invalid. Loop unchanged.", self.Loop})
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Check validity of the subtitle and the subtitleDuration passed and sets RADIO.subtitle and RADIO.subtitleDuration
|
|
||||||
-- Both parameters are mandatory, since it wouldn't make much sense to change the Subtitle and not its duration
|
|
||||||
-- @param #RADIO self
|
|
||||||
-- @param #string Subtitle
|
|
||||||
-- @param #number SubtitleDuration in s
|
|
||||||
-- @return #RADIO self
|
|
||||||
-- @usage
|
|
||||||
-- -- create the broadcaster and attaches it a RADIO
|
|
||||||
-- local MyUnit = UNIT:FindByName("MyUnit")
|
|
||||||
-- local MyUnitRadio = MyUnit:GetRadio()
|
|
||||||
--
|
|
||||||
-- -- add a subtitle for the next transmission, which will be up for 10s
|
|
||||||
-- MyUnitRadio:SetSubtitle("My Subtitle, 10)
|
|
||||||
function RADIO:SetSubtitle(Subtitle, SubtitleDuration)
|
|
||||||
self:F2({Subtitle, SubtitleDuration})
|
|
||||||
if type(Subtitle) == "string" then
|
|
||||||
self.Subtitle = Subtitle
|
|
||||||
else
|
|
||||||
self.Subtitle = ""
|
|
||||||
self:E({"Subtitle is invalid. Subtitle reset.", self.Subtitle})
|
|
||||||
end
|
|
||||||
if type(SubtitleDuration) == "number" then
|
|
||||||
self.SubtitleDuration = SubtitleDuration
|
|
||||||
else
|
|
||||||
self.SubtitleDuration = 0
|
|
||||||
self:E({"SubtitleDuration is invalid. SubtitleDuration reset.", self.SubtitleDuration})
|
|
||||||
end
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Create a new transmission, that is to say, populate the RADIO with relevant data
|
|
||||||
-- In this function the data is especially relevant if the broadcaster is anything but a UNIT or a GROUP,
|
|
||||||
-- but it will work with a UNIT or a GROUP anyway.
|
|
||||||
-- Only the #RADIO and the Filename are mandatory
|
|
||||||
-- @param #RADIO self
|
|
||||||
-- @param #string FileName 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})
|
|
||||||
|
|
||||||
self:SetFileName(FileName)
|
|
||||||
if Frequency then self:SetFrequency(Frequency) end
|
|
||||||
if Modulation then self:SetModulation(Modulation) end
|
|
||||||
if Power then self:SetPower(Power) end
|
|
||||||
if Loop then self:SetLoop(Loop) end
|
|
||||||
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--- Create a new transmission, that is to say, populate the RADIO with relevant data
|
|
||||||
-- In this function the data is especially relevant if the broadcaster is a UNIT or a GROUP,
|
|
||||||
-- but it will work for any @{Wrapper.Positionable#POSITIONABLE}.
|
|
||||||
-- Only the RADIO and the Filename are mandatory.
|
|
||||||
-- @param #RADIO self
|
|
||||||
-- @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)
|
|
||||||
|
|
||||||
-- 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
|
|
||||||
|
|
||||||
--- 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
|
|
||||||
-- * If the POSITIONABLE is not a UNIT or a GROUP, we use the generic (but limited) trigger.action.radioTransmission()
|
|
||||||
-- * If the POSITIONABLE is a UNIT or a GROUP, we use the "TransmitMessage" Command
|
|
||||||
-- * If your POSITIONABLE is a UNIT or a GROUP, the Power is ignored.
|
|
||||||
-- * If your POSITIONABLE is not a UNIT or a GROUP, the Subtitle, SubtitleDuration are ignored
|
|
||||||
-- @param #RADIO self
|
|
||||||
-- @param #boolean viatrigger Use trigger.action.radioTransmission() in any case, i.e. also for UNITS and GROUPS.
|
|
||||||
-- @return #RADIO self
|
|
||||||
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") 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: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
|
|
||||||
-- @return #RADIO self
|
|
||||||
function RADIO:StopBroadcast()
|
|
||||||
self:F()
|
|
||||||
-- If the POSITIONABLE is a UNIT or a GROUP, stop the transmission with the DCS "StopTransmission" command
|
|
||||||
if self.Positionable.ClassName == "UNIT" or self.Positionable.ClassName == "GROUP" then
|
|
||||||
|
|
||||||
local commandStopTransmission={id="StopTransmission", params={}}
|
|
||||||
|
|
||||||
self.Positionable:SetCommand(commandStopTransmission)
|
|
||||||
else
|
|
||||||
-- Else, we use the appropriate singleton funciton
|
|
||||||
trigger.action.stopRadioTransmission(tostring(self.ID))
|
|
||||||
end
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--- After attaching a @{#BEACON} to your @{Wrapper.Positionable#POSITIONABLE}, you need to select the right function to activate the kind of beacon you want.
|
|
||||||
-- There are two types of BEACONs available : the AA TACAN Beacon and the general purpose Radio Beacon.
|
|
||||||
-- Note that in both case, you can set an optional parameter : the `BeaconDuration`. This can be very usefull to simulate the battery time if your BEACON is
|
|
||||||
-- attach to a cargo crate, for exemple.
|
|
||||||
--
|
|
||||||
-- ## AA TACAN Beacon usage
|
|
||||||
--
|
|
||||||
-- This beacon only works with airborne @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}. Use @{#BEACON:AATACAN}() to set the beacon parameters and start the beacon.
|
|
||||||
-- Use @#BEACON:StopAATACAN}() to stop it.
|
|
||||||
--
|
|
||||||
-- ## General Purpose Radio Beacon usage
|
|
||||||
--
|
|
||||||
-- This beacon will work with any @{Wrapper.Positionable#POSITIONABLE}, but **it won't follow the @{Wrapper.Positionable#POSITIONABLE}** ! This means that you should only use it with
|
|
||||||
-- @{Wrapper.Positionable#POSITIONABLE} that don't move, or move very slowly. Use @{#BEACON:RadioBeacon}() to set the beacon parameters and start the beacon.
|
|
||||||
-- Use @{#BEACON:StopRadioBeacon}() to stop it.
|
|
||||||
--
|
|
||||||
-- @type BEACON
|
|
||||||
-- @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,
|
|
||||||
}
|
|
||||||
|
|
||||||
--- Beacon types supported by DCS.
|
|
||||||
-- @type BEACON.Type
|
|
||||||
-- @field #number NULL
|
|
||||||
-- @field #number VOR
|
|
||||||
-- @field #number DME
|
|
||||||
-- @field #number VOR_DME
|
|
||||||
-- @field #number TACAN TACtical Air Navigation system.
|
|
||||||
-- @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 PRMG_LOCALIZER
|
|
||||||
-- @field #number PRMG_GLIDESLOPE
|
|
||||||
-- @field #number ICLS Same as ICLS glideslope.
|
|
||||||
-- @field #number ICLS_LOCALIZER
|
|
||||||
-- @field #number ICLS_GLIDESLOPE
|
|
||||||
-- @field #number NAUTICAL_HOMER
|
|
||||||
BEACON.Type={
|
|
||||||
NULL = 0,
|
|
||||||
VOR = 1,
|
|
||||||
DME = 2,
|
|
||||||
VOR_DME = 3,
|
|
||||||
TACAN = 4,
|
|
||||||
VORTAC = 5,
|
|
||||||
RSBN = 128,
|
|
||||||
BROADCAST_STATION = 1024,
|
|
||||||
HOMER = 8,
|
|
||||||
AIRPORT_HOMER = 4104,
|
|
||||||
AIRPORT_HOMER_WITH_MARKER = 4136,
|
|
||||||
ILS_FAR_HOMER = 16408,
|
|
||||||
ILS_NEAR_HOMER = 16424,
|
|
||||||
ILS_LOCALIZER = 16640,
|
|
||||||
ILS_GLIDESLOPE = 16896,
|
|
||||||
PRMG_LOCALIZER = 33024,
|
|
||||||
PRMG_GLIDESLOPE = 33280,
|
|
||||||
ICLS = 131584, --leaving this in here but it is the same as ICLS_GLIDESLOPE
|
|
||||||
ICLS_LOCALIZER = 131328,
|
|
||||||
ICLS_GLIDESLOPE = 131584,
|
|
||||||
NAUTICAL_HOMER = 65536,
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
--- Beacon systems supported by DCS. https://wiki.hoggitworld.com/view/DCS_command_activateBeacon
|
|
||||||
-- @type BEACON.System
|
|
||||||
-- @field #number PAR_10 ?
|
|
||||||
-- @field #number RSBN_5 Russian VOR/DME system.
|
|
||||||
-- @field #number TACAN TACtical Air Navigation system on ground.
|
|
||||||
-- @field #number TACAN_TANKER_X TACtical Air Navigation system for tankers on X band.
|
|
||||||
-- @field #number TACAN_TANKER_Y TACtical Air Navigation system for tankers on Y band.
|
|
||||||
-- @field #number VOR Very High Frequency Omni-Directional Range
|
|
||||||
-- @field #number ILS_LOCALIZER ILS localizer
|
|
||||||
-- @field #number ILS_GLIDESLOPE ILS glideslope.
|
|
||||||
-- @field #number PRGM_LOCALIZER PRGM localizer.
|
|
||||||
-- @field #number PRGM_GLIDESLOPE PRGM glideslope.
|
|
||||||
-- @field #number BROADCAST_STATION Broadcast station.
|
|
||||||
-- @field #number VORTAC Radio-based navigational aid for aircraft pilots consisting of a co-located VHF omnidirectional range (VOR) beacon and a tactical air navigation system (TACAN) beacon.
|
|
||||||
-- @field #number TACAN_AA_MODE_X TACtical Air Navigation for aircraft on X band.
|
|
||||||
-- @field #number TACAN_AA_MODE_Y TACtical Air Navigation for aircraft on Y band.
|
|
||||||
-- @field #number VORDME Radio beacon that combines a VHF omnidirectional range (VOR) with a distance measuring equipment (DME).
|
|
||||||
-- @field #number ICLS_LOCALIZER Carrier landing system.
|
|
||||||
-- @field #number ICLS_GLIDESLOPE Carrier landing system.
|
|
||||||
BEACON.System={
|
|
||||||
PAR_10 = 1,
|
|
||||||
RSBN_5 = 2,
|
|
||||||
TACAN = 3,
|
|
||||||
TACAN_TANKER_X = 4,
|
|
||||||
TACAN_TANKER_Y = 5,
|
|
||||||
VOR = 6,
|
|
||||||
ILS_LOCALIZER = 7,
|
|
||||||
ILS_GLIDESLOPE = 8,
|
|
||||||
PRMG_LOCALIZER = 9,
|
|
||||||
PRMG_GLIDESLOPE = 10,
|
|
||||||
BROADCAST_STATION = 11,
|
|
||||||
VORTAC = 12,
|
|
||||||
TACAN_AA_MODE_X = 13,
|
|
||||||
TACAN_AA_MODE_Y = 14,
|
|
||||||
VORDME = 15,
|
|
||||||
ICLS_LOCALIZER = 16,
|
|
||||||
ICLS_GLIDESLOPE = 17,
|
|
||||||
}
|
|
||||||
|
|
||||||
--- 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 object or #nil if the positionable is invalid.
|
|
||||||
function BEACON:New(Positionable)
|
|
||||||
|
|
||||||
-- 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
|
|
||||||
|
|
||||||
self:E({"The passed positionable is invalid, no BEACON created", Positionable})
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--- Activates a TACAN BEACON.
|
|
||||||
-- @param #BEACON self
|
|
||||||
-- @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
|
|
||||||
|
|
||||||
-- Beacon type.
|
|
||||||
local Type=BEACON.Type.TACAN
|
|
||||||
|
|
||||||
-- Beacon system.
|
|
||||||
local System=BEACON.System.TACAN
|
|
||||||
|
|
||||||
-- 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
|
|
||||||
|
|
||||||
-- 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
|
|
||||||
-- @param #number TACANChannel (the "10" part in "10Y"). Note that AA TACAN are only available on Y Channels
|
|
||||||
-- @param #string Message The Message that is going to be coded in Morse and broadcasted by the beacon
|
|
||||||
-- @param #boolean Bearing Can the BEACON be homed on ?
|
|
||||||
-- @param #number BeaconDuration How long will the beacon last in seconds. Omit for forever.
|
|
||||||
-- @return #BEACON self
|
|
||||||
-- @usage
|
|
||||||
-- -- Let's create a TACAN Beacon for a tanker
|
|
||||||
-- local myUnit = UNIT:FindByName("MyUnit")
|
|
||||||
-- local myBeacon = myUnit:GetBeacon() -- Creates the beacon
|
|
||||||
--
|
|
||||||
-- myBeacon:AATACAN(20, "TEXACO", true) -- Activate the beacon
|
|
||||||
function BEACON:AATACAN(TACANChannel, Message, Bearing, BeaconDuration)
|
|
||||||
self:F({TACANChannel, Message, Bearing, BeaconDuration})
|
|
||||||
|
|
||||||
local IsValid = true
|
|
||||||
|
|
||||||
if not self.Positionable:IsAir() then
|
|
||||||
self:E({"The POSITIONABLE you want to attach the AA Tacan Beacon is not an aircraft ! The BEACON is not emitting", self.Positionable})
|
|
||||||
IsValid = false
|
|
||||||
end
|
|
||||||
|
|
||||||
local Frequency = self:_TACANToFrequency(TACANChannel, "Y")
|
|
||||||
if not Frequency then
|
|
||||||
self:E({"The passed TACAN channel is invalid, the BEACON is not emitting"})
|
|
||||||
IsValid = false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- I'm using the beacon type 4 (BEACON_TYPE_TACAN). For System, I'm using 5 (TACAN_TANKER_MODE_Y) if the bearing shows its bearing
|
|
||||||
-- or 14 (TACAN_AA_MODE_Y) if it does not
|
|
||||||
local System
|
|
||||||
if Bearing then
|
|
||||||
System = 5
|
|
||||||
else
|
|
||||||
System = 14
|
|
||||||
end
|
|
||||||
|
|
||||||
if IsValid then -- Starts the BEACON
|
|
||||||
self:T2({"AA TACAN BEACON started !"})
|
|
||||||
self.Positionable:SetCommand({
|
|
||||||
id = "ActivateBeacon",
|
|
||||||
params = {
|
|
||||||
type = 4,
|
|
||||||
system = System,
|
|
||||||
callsign = Message,
|
|
||||||
frequency = Frequency,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD
|
|
||||||
SCHEDULER:New(nil,
|
|
||||||
function()
|
|
||||||
self:StopAATACAN()
|
|
||||||
end, {}, BeaconDuration)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Stops the AA TACAN BEACON
|
|
||||||
-- @param #BEACON self
|
|
||||||
-- @return #BEACON self
|
|
||||||
function BEACON:StopAATACAN()
|
|
||||||
self:F()
|
|
||||||
if not self.Positionable then
|
|
||||||
self:E({"Start the beacon first before stoping it !"})
|
|
||||||
else
|
|
||||||
self.Positionable:SetCommand({
|
|
||||||
id = 'DeactivateBeacon',
|
|
||||||
params = {
|
|
||||||
}
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--- Activates a general pupose Radio Beacon
|
|
||||||
-- This uses the very generic singleton function "trigger.action.radioTransmission()" provided by DCS to broadcast a sound file on a specific frequency.
|
|
||||||
-- Although any frequency could be used, only 2 DCS Modules can home on radio beacons at the time of writing : the Huey and the Mi-8.
|
|
||||||
-- They can home in on these specific frequencies :
|
|
||||||
-- * **Mi8**
|
|
||||||
-- * R-828 -> 20-60MHz
|
|
||||||
-- * ARKUD -> 100-150MHz (canal 1 : 114166, canal 2 : 114333, canal 3 : 114583, canal 4 : 121500, canal 5 : 123100, canal 6 : 124100) AM
|
|
||||||
-- * ARK9 -> 150-1300KHz
|
|
||||||
-- * **Huey**
|
|
||||||
-- * AN/ARC-131 -> 30-76 Mhz FM
|
|
||||||
-- @param #BEACON self
|
|
||||||
-- @param #string FileName The name of the audio file
|
|
||||||
-- @param #number Frequency in MHz
|
|
||||||
-- @param #number Modulation either radio.modulation.AM or radio.modulation.FM
|
|
||||||
-- @param #number Power in W
|
|
||||||
-- @param #number BeaconDuration How long will the beacon last in seconds. Omit for forever.
|
|
||||||
-- @return #BEACON self
|
|
||||||
-- @usage
|
|
||||||
-- -- Let's create a beacon for a unit in distress.
|
|
||||||
-- -- Frequency will be 40MHz FM (home-able by a Huey's AN/ARC-131)
|
|
||||||
-- -- The beacon they use is battery-powered, and only lasts for 5 min
|
|
||||||
-- local UnitInDistress = UNIT:FindByName("Unit1")
|
|
||||||
-- local UnitBeacon = UnitInDistress:GetBeacon()
|
|
||||||
--
|
|
||||||
-- -- Set the beacon and start it
|
|
||||||
-- UnitBeacon:RadioBeacon("MySoundFileSOS.ogg", 40, radio.modulation.FM, 20, 5*60)
|
|
||||||
function BEACON:RadioBeacon(FileName, Frequency, Modulation, Power, BeaconDuration)
|
|
||||||
self:F({FileName, Frequency, Modulation, Power, BeaconDuration})
|
|
||||||
local IsValid = false
|
|
||||||
|
|
||||||
-- Check the filename
|
|
||||||
if type(FileName) == "string" then
|
|
||||||
if FileName:find(".ogg") or FileName:find(".wav") then
|
|
||||||
if not FileName:find("l10n/DEFAULT/") then
|
|
||||||
FileName = "l10n/DEFAULT/" .. FileName
|
|
||||||
end
|
|
||||||
IsValid = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if not IsValid then
|
|
||||||
self:E({"File name invalid. Maybe something wrong with the extension ? ", FileName})
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Check the Frequency
|
|
||||||
if type(Frequency) ~= "number" and IsValid then
|
|
||||||
self:E({"Frequency invalid. ", Frequency})
|
|
||||||
IsValid = false
|
|
||||||
end
|
|
||||||
Frequency = Frequency * 1000000 -- Conversion to Hz
|
|
||||||
|
|
||||||
-- Check the modulation
|
|
||||||
if Modulation ~= radio.modulation.AM and Modulation ~= radio.modulation.FM and IsValid then --TODO Maybe make this future proof if ED decides to add an other modulation ?
|
|
||||||
self:E({"Modulation is invalid. Use DCS's enum radio.modulation.", Modulation})
|
|
||||||
IsValid = false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Check the Power
|
|
||||||
if type(Power) ~= "number" and IsValid then
|
|
||||||
self:E({"Power is invalid. ", Power})
|
|
||||||
IsValid = false
|
|
||||||
end
|
|
||||||
Power = math.floor(math.abs(Power)) --TODO Find what is the maximum power allowed by DCS and limit power to that
|
|
||||||
|
|
||||||
if IsValid then
|
|
||||||
self:T2({"Activating Beacon on ", Frequency, Modulation})
|
|
||||||
-- Note that this is looped. I have to give this transmission a unique name, I use the class ID
|
|
||||||
trigger.action.radioTransmission(FileName, self.Positionable:GetPositionVec3(), Modulation, true, Frequency, Power, tostring(self.ID))
|
|
||||||
|
|
||||||
if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD
|
|
||||||
SCHEDULER:New( nil,
|
|
||||||
function()
|
|
||||||
self:StopRadioBeacon()
|
|
||||||
end, {}, BeaconDuration)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Stops the AA TACAN BEACON
|
|
||||||
-- @param #BEACON self
|
|
||||||
-- @return #BEACON self
|
|
||||||
function BEACON:StopRadioBeacon()
|
|
||||||
self:F()
|
|
||||||
-- The unique name of the transmission is the class ID
|
|
||||||
trigger.action.stopRadioTransmission(tostring(self.ID))
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
@@ -15,7 +15,6 @@
|
|||||||
-- @module Core.Report
|
-- @module Core.Report
|
||||||
-- @image Core_Report.JPG
|
-- @image Core_Report.JPG
|
||||||
|
|
||||||
|
|
||||||
--- @type REPORT
|
--- @type REPORT
|
||||||
-- @extends Core.Base#BASE
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
@@ -45,28 +44,26 @@ end
|
|||||||
--- Has the REPORT Text?
|
--- Has the REPORT Text?
|
||||||
-- @param #REPORT self
|
-- @param #REPORT self
|
||||||
-- @return #boolean
|
-- @return #boolean
|
||||||
function REPORT:HasText() --R2.1
|
function REPORT:HasText() -- R2.1
|
||||||
|
|
||||||
return #self.Report > 0
|
return #self.Report > 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Set indent of a REPORT.
|
--- Set indent of a REPORT.
|
||||||
-- @param #REPORT self
|
-- @param #REPORT self
|
||||||
-- @param #number Indent
|
-- @param #number Indent
|
||||||
-- @return #REPORT
|
-- @return #REPORT
|
||||||
function REPORT:SetIndent( Indent ) --R2.1
|
function REPORT:SetIndent( Indent ) -- R2.1
|
||||||
self.Indent = Indent
|
self.Indent = Indent
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Add a new line to a REPORT.
|
--- Add a new line to a REPORT.
|
||||||
-- @param #REPORT self
|
-- @param #REPORT self
|
||||||
-- @param #string Text
|
-- @param #string Text
|
||||||
-- @return #REPORT
|
-- @return #REPORT
|
||||||
function REPORT:Add( Text )
|
function REPORT:Add( Text )
|
||||||
self.Report[#self.Report+1] = Text
|
self.Report[#self.Report + 1] = Text
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -76,17 +73,17 @@ end
|
|||||||
-- @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.
|
-- @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
|
-- @return #REPORT
|
||||||
function REPORT:AddIndent( Text, Separator )
|
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 ) )
|
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
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Produces the text of the report, taking into account an optional delimeter, which is \n by default.
|
--- Produces the text of the report, taking into account an optional delimiter, which is \n by default.
|
||||||
-- @param #REPORT self
|
-- @param #REPORT self
|
||||||
-- @param #string Delimiter (optional) A delimiter text.
|
-- @param #string Delimiter (optional) A delimiter text.
|
||||||
-- @return #string The report text.
|
-- @return #string The report text.
|
||||||
function REPORT:Text( Delimiter )
|
function REPORT:Text( Delimiter )
|
||||||
Delimiter = Delimiter or "\n"
|
Delimiter = Delimiter or "\n"
|
||||||
local ReportText = ( self.Title ~= "" and self.Title .. Delimiter or self.Title ) .. table.concat( self.Report, Delimiter ) or ""
|
local ReportText = (self.Title ~= "" and self.Title .. Delimiter or self.Title) .. table.concat( self.Report, Delimiter ) or ""
|
||||||
return ReportText
|
return ReportText
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
-- - 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!
|
-- - 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.
|
-- The non-persistency of SCHEDULERS attached to objects is required to allow SCHEDULER objects to be garbage collected when the parent object is destroyed, or set to nil and garbage collected.
|
||||||
-- Even when there are pending timer scheduled functions to be executed for the SCHEDULER object,
|
-- 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.
|
-- these will not be executed anymore when the SCHEDULER object has been destroyed.
|
||||||
--
|
--
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
-- @type SCHEDULEDISPATCHER
|
-- @type SCHEDULEDISPATCHER
|
||||||
-- @field #string ClassName Name of the class.
|
-- @field #string ClassName Name of the class.
|
||||||
-- @field #number CallID Call ID counter.
|
-- @field #number CallID Call ID counter.
|
||||||
-- @field #table PersistentSchedulers Persistant schedulers.
|
-- @field #table PersistentSchedulers Persistent schedulers.
|
||||||
-- @field #table ObjectSchedulers Schedulers that only exist as long as the master object exists.
|
-- @field #table ObjectSchedulers Schedulers that only exist as long as the master object exists.
|
||||||
-- @field #table Schedule Meta table setmetatable( {}, { __mode = "k" } ).
|
-- @field #table Schedule Meta table setmetatable( {}, { __mode = "k" } ).
|
||||||
-- @extends Core.Base#BASE
|
-- @extends Core.Base#BASE
|
||||||
@@ -46,11 +46,11 @@
|
|||||||
--- The SCHEDULEDISPATCHER structure
|
--- The SCHEDULEDISPATCHER structure
|
||||||
-- @type SCHEDULEDISPATCHER
|
-- @type SCHEDULEDISPATCHER
|
||||||
SCHEDULEDISPATCHER = {
|
SCHEDULEDISPATCHER = {
|
||||||
ClassName = "SCHEDULEDISPATCHER",
|
ClassName = "SCHEDULEDISPATCHER",
|
||||||
CallID = 0,
|
CallID = 0,
|
||||||
PersistentSchedulers = {},
|
PersistentSchedulers = {},
|
||||||
ObjectSchedulers = {},
|
ObjectSchedulers = {},
|
||||||
Schedule = nil,
|
Schedule = nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Player data table holding all important parameters of each player.
|
--- Player data table holding all important parameters of each player.
|
||||||
@@ -58,7 +58,7 @@ SCHEDULEDISPATCHER = {
|
|||||||
-- @field #function Function The schedule function to be called.
|
-- @field #function Function The schedule function to be called.
|
||||||
-- @field #table Arguments Schedule function arguments.
|
-- @field #table Arguments Schedule function arguments.
|
||||||
-- @field #number Start Start time in seconds.
|
-- @field #number Start Start time in seconds.
|
||||||
-- @field #number Repeat Repeat time intervall in seconds.
|
-- @field #number Repeat Repeat time interval in seconds.
|
||||||
-- @field #number Randomize Randomization factor [0,1].
|
-- @field #number Randomize Randomization factor [0,1].
|
||||||
-- @field #number Stop Stop time in seconds.
|
-- @field #number Stop Stop time in seconds.
|
||||||
-- @field #number StartTime Time in seconds when the scheduler is created.
|
-- @field #number StartTime Time in seconds when the scheduler is created.
|
||||||
@@ -77,7 +77,7 @@ end
|
|||||||
|
|
||||||
--- Add a Schedule to the ScheduleDispatcher.
|
--- Add a Schedule to the ScheduleDispatcher.
|
||||||
-- The development of this method was really tidy.
|
-- The development of this method was really tidy.
|
||||||
-- It is constructed as such that a garbage collection is executed on the weak tables, when the Scheduler is nillified.
|
-- It is constructed as such that a garbage collection is executed on the weak tables, when the Scheduler is set to nil.
|
||||||
-- Nothing of this code should be modified without testing it thoroughly.
|
-- Nothing of this code should be modified without testing it thoroughly.
|
||||||
-- @param #SCHEDULEDISPATCHER self
|
-- @param #SCHEDULEDISPATCHER self
|
||||||
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
|
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
|
||||||
@@ -85,7 +85,7 @@ end
|
|||||||
-- @param #table ScheduleArguments Table of arguments passed to the ScheduleFunction.
|
-- @param #table ScheduleArguments Table of arguments passed to the ScheduleFunction.
|
||||||
-- @param #number Start Start time in seconds.
|
-- @param #number Start Start time in seconds.
|
||||||
-- @param #number Repeat Repeat interval in seconds.
|
-- @param #number Repeat Repeat interval in seconds.
|
||||||
-- @param #number Randomize Radomization factor [0,1].
|
-- @param #number Randomize Randomization factor [0,1].
|
||||||
-- @param #number Stop Stop time in seconds.
|
-- @param #number Stop Stop time in seconds.
|
||||||
-- @param #number TraceLevel Trace level [0,3].
|
-- @param #number TraceLevel Trace level [0,3].
|
||||||
-- @param Core.Fsm#FSM Fsm Finite state model.
|
-- @param Core.Fsm#FSM Fsm Finite state model.
|
||||||
@@ -97,9 +97,9 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
|||||||
self.CallID = self.CallID + 1
|
self.CallID = self.CallID + 1
|
||||||
|
|
||||||
-- Create ID.
|
-- Create ID.
|
||||||
local CallID = self.CallID .. "#" .. ( Scheduler.MasterObject and Scheduler.MasterObject.GetClassNameAndID and Scheduler.MasterObject:GetClassNameAndID() or "" ) or ""
|
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))
|
self:T2( string.format( "Adding schedule #%d CallID=%s", self.CallID, CallID ) )
|
||||||
|
|
||||||
-- Initialize PersistentSchedulers
|
-- Initialize PersistentSchedulers
|
||||||
self.PersistentSchedulers = self.PersistentSchedulers or {}
|
self.PersistentSchedulers = self.PersistentSchedulers or {}
|
||||||
@@ -110,7 +110,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
|||||||
|
|
||||||
if Scheduler.MasterObject then
|
if Scheduler.MasterObject then
|
||||||
self.ObjectSchedulers[CallID] = Scheduler
|
self.ObjectSchedulers[CallID] = Scheduler
|
||||||
self:F3( { CallID = CallID, ObjectScheduler = tostring(self.ObjectSchedulers[CallID]), MasterObject = tostring(Scheduler.MasterObject) } )
|
self:F3( { CallID = CallID, ObjectScheduler = tostring( self.ObjectSchedulers[CallID] ), MasterObject = tostring( Scheduler.MasterObject ) } )
|
||||||
else
|
else
|
||||||
self.PersistentSchedulers[CallID] = Scheduler
|
self.PersistentSchedulers[CallID] = Scheduler
|
||||||
self:F3( { CallID = CallID, PersistentScheduler = self.PersistentSchedulers[CallID] } )
|
self:F3( { CallID = CallID, PersistentScheduler = self.PersistentSchedulers[CallID] } )
|
||||||
@@ -118,16 +118,15 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
|||||||
|
|
||||||
self.Schedule = self.Schedule or setmetatable( {}, { __mode = "k" } )
|
self.Schedule = self.Schedule or setmetatable( {}, { __mode = "k" } )
|
||||||
self.Schedule[Scheduler] = self.Schedule[Scheduler] or {}
|
self.Schedule[Scheduler] = self.Schedule[Scheduler] or {}
|
||||||
self.Schedule[Scheduler][CallID] = {} --#SCHEDULEDISPATCHER.ScheduleData
|
self.Schedule[Scheduler][CallID] = {} -- #SCHEDULEDISPATCHER.ScheduleData
|
||||||
self.Schedule[Scheduler][CallID].Function = ScheduleFunction
|
self.Schedule[Scheduler][CallID].Function = ScheduleFunction
|
||||||
self.Schedule[Scheduler][CallID].Arguments = ScheduleArguments
|
self.Schedule[Scheduler][CallID].Arguments = ScheduleArguments
|
||||||
self.Schedule[Scheduler][CallID].StartTime = timer.getTime() + ( Start or 0 )
|
self.Schedule[Scheduler][CallID].StartTime = timer.getTime() + (Start or 0)
|
||||||
self.Schedule[Scheduler][CallID].Start = Start + 0.1
|
self.Schedule[Scheduler][CallID].Start = Start + 0.1
|
||||||
self.Schedule[Scheduler][CallID].Repeat = Repeat or 0
|
self.Schedule[Scheduler][CallID].Repeat = Repeat or 0
|
||||||
self.Schedule[Scheduler][CallID].Randomize = Randomize or 0
|
self.Schedule[Scheduler][CallID].Randomize = Randomize or 0
|
||||||
self.Schedule[Scheduler][CallID].Stop = Stop
|
self.Schedule[Scheduler][CallID].Stop = Stop
|
||||||
|
|
||||||
|
|
||||||
-- This section handles the tracing of the scheduled calls.
|
-- 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.
|
-- 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 Info structure contains the output of the debug.getinfo() calls, which inspects the call stack for the function name, line number and source name.
|
||||||
@@ -149,7 +148,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
|||||||
-- 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.
|
-- 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.
|
-- 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 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.
|
-- So this little piece of code does its magic wonderfully, performance overhead is negligible, as scheduled calls don't happen that often.
|
||||||
|
|
||||||
local Info = {}
|
local Info = {}
|
||||||
|
|
||||||
@@ -181,24 +180,24 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
|||||||
return errmsg
|
return errmsg
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Get object or persistant scheduler object.
|
-- Get object or persistent scheduler object.
|
||||||
local Scheduler = self.ObjectSchedulers[CallID] --Core.Scheduler#SCHEDULER
|
local Scheduler = self.ObjectSchedulers[CallID] -- Core.Scheduler#SCHEDULER
|
||||||
if not Scheduler then
|
if not Scheduler then
|
||||||
Scheduler = self.PersistentSchedulers[CallID]
|
Scheduler = self.PersistentSchedulers[CallID]
|
||||||
end
|
end
|
||||||
|
|
||||||
--self:T3( { Scheduler = Scheduler } )
|
-- self:T3( { Scheduler = Scheduler } )
|
||||||
|
|
||||||
if Scheduler then
|
if Scheduler then
|
||||||
|
|
||||||
local MasterObject = tostring(Scheduler.MasterObject)
|
local MasterObject = tostring( Scheduler.MasterObject )
|
||||||
|
|
||||||
-- Schedule object.
|
-- Schedule object.
|
||||||
local Schedule = self.Schedule[Scheduler][CallID] --#SCHEDULEDISPATCHER.ScheduleData
|
local Schedule = self.Schedule[Scheduler][CallID] -- #SCHEDULEDISPATCHER.ScheduleData
|
||||||
|
|
||||||
--self:T3( { Schedule = Schedule } )
|
-- self:T3( { Schedule = Schedule } )
|
||||||
|
|
||||||
local SchedulerObject = Scheduler.MasterObject --Scheduler.SchedulerObject Now is this the Maste or Scheduler object?
|
local SchedulerObject = Scheduler.MasterObject -- Scheduler.SchedulerObject Now is this the Master or Scheduler object?
|
||||||
local ShowTrace = Scheduler.ShowTrace
|
local ShowTrace = Scheduler.ShowTrace
|
||||||
|
|
||||||
local ScheduleFunction = Schedule.Function
|
local ScheduleFunction = Schedule.Function
|
||||||
@@ -209,11 +208,10 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
|||||||
local Stop = Schedule.Stop or 0
|
local Stop = Schedule.Stop or 0
|
||||||
local ScheduleID = Schedule.ScheduleID
|
local ScheduleID = Schedule.ScheduleID
|
||||||
|
|
||||||
|
local Prefix = (Repeat == 0) and "--->" or "+++>"
|
||||||
local Prefix = ( Repeat == 0 ) and "--->" or "+++>"
|
|
||||||
|
|
||||||
local Status, Result
|
local Status, Result
|
||||||
--self:E( { SchedulerObject = SchedulerObject } )
|
-- self:E( { SchedulerObject = SchedulerObject } )
|
||||||
if SchedulerObject then
|
if SchedulerObject then
|
||||||
local function Timer()
|
local function Timer()
|
||||||
if ShowTrace then
|
if ShowTrace then
|
||||||
@@ -236,14 +234,13 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
|||||||
local StartTime = Schedule.StartTime
|
local StartTime = Schedule.StartTime
|
||||||
|
|
||||||
-- Debug info.
|
-- Debug info.
|
||||||
self:F3( { CallID=CallID, ScheduleID=ScheduleID, Master = MasterObject, CurrentTime = CurrentTime, StartTime = StartTime, Start = Start, Repeat = Repeat, Randomize = Randomize, Stop = Stop } )
|
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 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.0001 -- Accuracy
|
||||||
if Repeat ~= 0 and ( ( Stop == 0 ) or ( Stop ~= 0 and CurrentTime <= StartTime + Stop ) ) then
|
-- self:T3( { Repeat = CallID, CurrentTime, ScheduleTime, ScheduleArguments } )
|
||||||
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.
|
return ScheduleTime -- returns the next time the function needs to be called.
|
||||||
else
|
else
|
||||||
self:Stop( Scheduler, CallID )
|
self:Stop( Scheduler, CallID )
|
||||||
@@ -287,21 +284,21 @@ function SCHEDULEDISPATCHER:Start( Scheduler, CallID, Info )
|
|||||||
|
|
||||||
if CallID then
|
if CallID then
|
||||||
|
|
||||||
local Schedule = self.Schedule[Scheduler][CallID] --#SCHEDULEDISPATCHER.ScheduleData
|
local Schedule = self.Schedule[Scheduler][CallID] -- #SCHEDULEDISPATCHER.ScheduleData
|
||||||
|
|
||||||
-- Only start when there is no ScheduleID defined!
|
-- Only start when there is no ScheduleID defined!
|
||||||
-- This prevents to "Start" the scheduler twice with the same CallID...
|
-- This prevents to "Start" the scheduler twice with the same CallID...
|
||||||
if not Schedule.ScheduleID then
|
if not Schedule.ScheduleID then
|
||||||
|
|
||||||
-- Current time in seconds.
|
-- Current time in seconds.
|
||||||
local Tnow=timer.getTime()
|
local Tnow = timer.getTime()
|
||||||
|
|
||||||
Schedule.StartTime = Tnow -- Set the StartTime field to indicate when the scheduler started.
|
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
|
-- 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)
|
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)))
|
self:T( string.format( "Starting SCHEDULEDISPATCHER Call ID=%s ==> Schedule ID=%s", tostring( CallID ), tostring( Schedule.ScheduleID ) ) )
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
@@ -323,20 +320,20 @@ function SCHEDULEDISPATCHER:Stop( Scheduler, CallID )
|
|||||||
|
|
||||||
if CallID then
|
if CallID then
|
||||||
|
|
||||||
local Schedule = self.Schedule[Scheduler][CallID] --#SCHEDULEDISPATCHER.ScheduleData
|
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.
|
-- Only stop when there is a ScheduleID defined for the CallID. So, when the scheduler was stopped before, do nothing.
|
||||||
if Schedule.ScheduleID then
|
if Schedule.ScheduleID then
|
||||||
|
|
||||||
self:T(string.format("scheduledispatcher stopping scheduler CallID=%s, ScheduleID=%s", tostring(CallID), tostring(Schedule.ScheduleID)))
|
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
|
-- Remove schedule function https://wiki.hoggitworld.com/view/DCS_func_removeFunction
|
||||||
timer.removeFunction(Schedule.ScheduleID)
|
timer.removeFunction( Schedule.ScheduleID )
|
||||||
|
|
||||||
Schedule.ScheduleID = nil
|
Schedule.ScheduleID = nil
|
||||||
|
|
||||||
else
|
else
|
||||||
self:T(string.format("Error no ScheduleID for CallID=%s", tostring(CallID)))
|
self:T( string.format( "Error no ScheduleID for CallID=%s", tostring( CallID ) ) )
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
@@ -359,7 +356,7 @@ function SCHEDULEDISPATCHER:Clear( Scheduler )
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Shopw tracing info.
|
--- Show tracing info.
|
||||||
-- @param #SCHEDULEDISPATCHER self
|
-- @param #SCHEDULEDISPATCHER self
|
||||||
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
|
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
|
||||||
function SCHEDULEDISPATCHER:ShowTrace( Scheduler )
|
function SCHEDULEDISPATCHER:ShowTrace( Scheduler )
|
||||||
|
|||||||
@@ -48,7 +48,6 @@
|
|||||||
-- @field #boolean ShowTrace Trace info if true.
|
-- @field #boolean ShowTrace Trace info if true.
|
||||||
-- @extends Core.Base#BASE
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
|
|
||||||
--- Creates and handles schedules over time, which allow to execute code at specific time intervals with randomization.
|
--- Creates and handles schedules over time, which allow to execute code at specific time intervals with randomization.
|
||||||
--
|
--
|
||||||
-- A SCHEDULER can manage **multiple** (repeating) schedules. Each planned or executing schedule has a unique **ScheduleID**.
|
-- A SCHEDULER can manage **multiple** (repeating) schedules. Each planned or executing schedule has a unique **ScheduleID**.
|
||||||
@@ -79,7 +78,7 @@
|
|||||||
--
|
--
|
||||||
-- ### Construct a SCHEDULER object without a volatile schedule, but volatile to the Object existence...
|
-- ### 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.
|
-- * @{#SCHEDULER.New}( Object ): Setup a new SCHEDULER object, which is linked to the Object. When the Object is set to nil or destroyed, the SCHEDULER object will also be destroyed and stopped after garbage collection.
|
||||||
--
|
--
|
||||||
-- ZoneObject = ZONE:New( "ZoneName" )
|
-- ZoneObject = ZONE:New( "ZoneName" )
|
||||||
-- MasterObject = SCHEDULER:New( ZoneObject )
|
-- MasterObject = SCHEDULER:New( ZoneObject )
|
||||||
@@ -149,13 +148,13 @@
|
|||||||
-- ZoneObject = ZONE:New( "ZoneName" )
|
-- ZoneObject = ZONE:New( "ZoneName" )
|
||||||
-- MasterObject = SCHEDULER:New( ZoneObject )
|
-- MasterObject = SCHEDULER:New( ZoneObject )
|
||||||
--
|
--
|
||||||
-- Several parameters can be specified that influence the behaviour of a Schedule.
|
-- Several parameters can be specified that influence the behavior of a Schedule.
|
||||||
--
|
--
|
||||||
-- ### A single schedule, immediately executed
|
-- ### A single schedule, immediately executed
|
||||||
--
|
--
|
||||||
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {} )
|
-- SchedulerID = MasterObject:Schedule( ZoneObject, ScheduleFunction, {} )
|
||||||
--
|
--
|
||||||
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within milleseconds ...
|
-- The above example schedules a new ScheduleFunction call to be executed asynchronously, within milliseconds ...
|
||||||
--
|
--
|
||||||
-- ### A single schedule, planned over time
|
-- ### A single schedule, planned over time
|
||||||
--
|
--
|
||||||
@@ -193,10 +192,10 @@
|
|||||||
--
|
--
|
||||||
-- @field #SCHEDULER
|
-- @field #SCHEDULER
|
||||||
SCHEDULER = {
|
SCHEDULER = {
|
||||||
ClassName = "SCHEDULER",
|
ClassName = "SCHEDULER",
|
||||||
Schedules = {},
|
Schedules = {},
|
||||||
MasterObject = nil,
|
MasterObject = nil,
|
||||||
ShowTrace = nil,
|
ShowTrace = nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
--- SCHEDULER constructor.
|
--- SCHEDULER constructor.
|
||||||
@@ -235,7 +234,7 @@ end
|
|||||||
-- @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 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 time 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 RandomizeFactor Specifies a randomization factor between 0 and 1 to randomize the Repeat.
|
||||||
-- @param #number Stop Time interval in seconds after which the scheduler will be stoppe.
|
-- @param #number Stop Time interval in seconds after which the scheduler will be stopped.
|
||||||
-- @param #number TraceLevel Trace level [0,3]. Default 3.
|
-- @param #number TraceLevel Trace level [0,3]. Default 3.
|
||||||
-- @param Core.Fsm#FSM Fsm Finite state model.
|
-- @param Core.Fsm#FSM Fsm Finite state model.
|
||||||
-- @return #table The ScheduleID of the planned schedule.
|
-- @return #table The ScheduleID of the planned schedule.
|
||||||
@@ -248,25 +247,24 @@ function SCHEDULER:Schedule( MasterObject, SchedulerFunction, SchedulerArguments
|
|||||||
if MasterObject and MasterObject.ClassName and MasterObject.ClassID then
|
if MasterObject and MasterObject.ClassName and MasterObject.ClassID then
|
||||||
ObjectName = MasterObject.ClassName .. MasterObject.ClassID
|
ObjectName = MasterObject.ClassName .. MasterObject.ClassID
|
||||||
end
|
end
|
||||||
self:F3( { "Schedule :", ObjectName, tostring( MasterObject ), Start, Repeat, RandomizeFactor, Stop } )
|
self:F3( { "Schedule :", ObjectName, tostring( MasterObject ), Start, Repeat, RandomizeFactor, Stop } )
|
||||||
|
|
||||||
-- Set master object.
|
-- Set master object.
|
||||||
self.MasterObject = MasterObject
|
self.MasterObject = MasterObject
|
||||||
|
|
||||||
-- Add schedule.
|
-- Add schedule.
|
||||||
local ScheduleID = _SCHEDULEDISPATCHER:AddSchedule(
|
local ScheduleID = _SCHEDULEDISPATCHER:AddSchedule( self,
|
||||||
self,
|
SchedulerFunction,
|
||||||
SchedulerFunction,
|
SchedulerArguments,
|
||||||
SchedulerArguments,
|
Start,
|
||||||
Start,
|
Repeat,
|
||||||
Repeat,
|
RandomizeFactor,
|
||||||
RandomizeFactor,
|
Stop,
|
||||||
Stop,
|
TraceLevel or 3,
|
||||||
TraceLevel or 3,
|
Fsm
|
||||||
Fsm
|
)
|
||||||
)
|
|
||||||
|
|
||||||
self.Schedules[#self.Schedules+1] = ScheduleID
|
self.Schedules[#self.Schedules + 1] = ScheduleID
|
||||||
|
|
||||||
return ScheduleID
|
return ScheduleID
|
||||||
end
|
end
|
||||||
@@ -276,7 +274,7 @@ end
|
|||||||
-- @param #string ScheduleID (Optional) The ScheduleID of the planned (repeating) schedule.
|
-- @param #string ScheduleID (Optional) The ScheduleID of the planned (repeating) schedule.
|
||||||
function SCHEDULER:Start( ScheduleID )
|
function SCHEDULER:Start( ScheduleID )
|
||||||
self:F3( { ScheduleID } )
|
self:F3( { ScheduleID } )
|
||||||
self:T(string.format("Starting scheduler ID=%s", tostring(ScheduleID)))
|
self:T( string.format( "Starting scheduler ID=%s", tostring( ScheduleID ) ) )
|
||||||
_SCHEDULEDISPATCHER:Start( self, ScheduleID )
|
_SCHEDULEDISPATCHER:Start( self, ScheduleID )
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -285,7 +283,7 @@ end
|
|||||||
-- @param #string ScheduleID (Optional) The ScheduleID of the planned (repeating) schedule.
|
-- @param #string ScheduleID (Optional) The ScheduleID of the planned (repeating) schedule.
|
||||||
function SCHEDULER:Stop( ScheduleID )
|
function SCHEDULER:Stop( ScheduleID )
|
||||||
self:F3( { ScheduleID } )
|
self:F3( { ScheduleID } )
|
||||||
self:T(string.format("Stopping scheduler ID=%s", tostring(ScheduleID)))
|
self:T( string.format( "Stopping scheduler ID=%s", tostring( ScheduleID ) ) )
|
||||||
_SCHEDULEDISPATCHER:Stop( self, ScheduleID )
|
_SCHEDULEDISPATCHER:Stop( self, ScheduleID )
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -294,15 +292,15 @@ end
|
|||||||
-- @param #string ScheduleID (optional) The ScheduleID of the planned (repeating) schedule.
|
-- @param #string ScheduleID (optional) The ScheduleID of the planned (repeating) schedule.
|
||||||
function SCHEDULER:Remove( ScheduleID )
|
function SCHEDULER:Remove( ScheduleID )
|
||||||
self:F3( { ScheduleID } )
|
self:F3( { ScheduleID } )
|
||||||
self:T(string.format("Removing scheduler ID=%s", tostring(ScheduleID)))
|
self:T( string.format( "Removing scheduler ID=%s", tostring( ScheduleID ) ) )
|
||||||
_SCHEDULEDISPATCHER:RemoveSchedule( self, ScheduleID )
|
_SCHEDULEDISPATCHER:RemoveSchedule( self, ScheduleID )
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Clears all pending schedules.
|
--- Clears all pending schedules.
|
||||||
-- @param #SCHEDULER self
|
-- @param #SCHEDULER self
|
||||||
function SCHEDULER:Clear()
|
function SCHEDULER:Clear()
|
||||||
self:F3( )
|
self:F3()
|
||||||
self:T(string.format("Clearing scheduler"))
|
self:T( string.format( "Clearing scheduler" ) )
|
||||||
_SCHEDULEDISPATCHER:Clear( self )
|
_SCHEDULEDISPATCHER:Clear( self )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -29,15 +29,14 @@
|
|||||||
-- @module Core.Settings
|
-- @module Core.Settings
|
||||||
-- @image Core_Settings.JPG
|
-- @image Core_Settings.JPG
|
||||||
|
|
||||||
|
|
||||||
--- @type SETTINGS
|
--- @type SETTINGS
|
||||||
-- @extends Core.Base#BASE
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
--- Takes care of various settings that influence the behaviour of certain functionalities and classes within the MOOSE framework.
|
--- Takes care of various settings that influence the behavior of certain functionalities and classes within the MOOSE framework.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- The SETTINGS class takes care of various settings that influence the behaviour of certain functionalities and classes within the MOOSE framework.
|
-- The SETTINGS class takes care of various settings that influence the behavior of certain functionalities and classes within the MOOSE framework.
|
||||||
-- SETTINGS can work on 2 levels:
|
-- SETTINGS can work on 2 levels:
|
||||||
--
|
--
|
||||||
-- - **Default settings**: A running mission has **Default settings**.
|
-- - **Default settings**: A running mission has **Default settings**.
|
||||||
@@ -59,7 +58,7 @@
|
|||||||
--
|
--
|
||||||
-- A menu is created automatically per Command Center that allows to modify the **Default** settings.
|
-- A menu is created automatically per Command Center that allows to modify the **Default** settings.
|
||||||
-- So, when joining a CC unit, a menu will be available that allows to change the settings parameters **FOR ALL THE PLAYERS**!
|
-- So, when joining a CC unit, a menu will be available that allows to change the settings parameters **FOR ALL THE PLAYERS**!
|
||||||
-- Note that the **Default settings** will only be used when a player has not choosen its own settings.
|
-- Note that the **Default settings** will only be used when a player has not chosen its own settings.
|
||||||
--
|
--
|
||||||
-- ## 2.2) Player settings menu
|
-- ## 2.2) Player settings menu
|
||||||
--
|
--
|
||||||
@@ -69,7 +68,7 @@
|
|||||||
--
|
--
|
||||||
-- ## 2.3) Show or Hide the Player Setting menus
|
-- ## 2.3) Show or Hide the Player Setting menus
|
||||||
--
|
--
|
||||||
-- Of course, it may be requried not to show any setting menus. In this case, a method is available on the **\_SETTINGS object**.
|
-- Of course, it may be required not to show any setting menus. In this case, a method is available on the **\_SETTINGS object**.
|
||||||
-- Use @{#SETTINGS.SetPlayerMenuOff}() to hide the player menus, and use @{#SETTINGS.SetPlayerMenuOn}() show the player menus.
|
-- Use @{#SETTINGS.SetPlayerMenuOff}() to hide the player menus, and use @{#SETTINGS.SetPlayerMenuOn}() show the player menus.
|
||||||
-- Note that when this method is used, any player already in a slot will not have its menus visibility changed.
|
-- Note that when this method is used, any player already in a slot will not have its menus visibility changed.
|
||||||
-- The option will only have effect when a player enters a new slot or changes a slot.
|
-- The option will only have effect when a player enters a new slot or changes a slot.
|
||||||
@@ -94,8 +93,8 @@
|
|||||||
--
|
--
|
||||||
-- - A2G BR: [Bearing Range](https://en.wikipedia.org/wiki/Bearing_(navigation)).
|
-- - A2G BR: [Bearing Range](https://en.wikipedia.org/wiki/Bearing_(navigation)).
|
||||||
-- - A2G MGRS: The [Military Grid Reference System](https://en.wikipedia.org/wiki/Military_Grid_Reference_System). The accuracy can also be adapted.
|
-- - A2G MGRS: The [Military Grid Reference System](https://en.wikipedia.org/wiki/Military_Grid_Reference_System). The accuracy can also be adapted.
|
||||||
-- - A2G LL DMS: Lattitude Longitude [Degrees Minutes Seconds](https://en.wikipedia.org/wiki/Geographic_coordinate_conversion). The accuracy can also be adapted.
|
-- - A2G LL DMS: Latitude Longitude [Degrees Minutes Seconds](https://en.wikipedia.org/wiki/Geographic_coordinate_conversion). The accuracy can also be adapted.
|
||||||
-- - A2G LL DDM: Lattitude Longitude [Decimal Degrees Minutes](https://en.wikipedia.org/wiki/Decimal_degrees). The accuracy can also be adapted.
|
-- - A2G LL DDM: Latitude Longitude [Decimal Degrees Minutes](https://en.wikipedia.org/wiki/Decimal_degrees). The accuracy can also be adapted.
|
||||||
--
|
--
|
||||||
-- ### 3.1.2) A2G coordinates setting **menu**
|
-- ### 3.1.2) A2G coordinates setting **menu**
|
||||||
--
|
--
|
||||||
@@ -183,7 +182,7 @@
|
|||||||
-- The settings can be changed by using the **Default settings menu** on the Command Center or the **Player settings menu** on the Player Slot.
|
-- The settings can be changed by using the **Default settings menu** on the Command Center or the **Player settings menu** on the Player Slot.
|
||||||
--
|
--
|
||||||
-- Each Message Type has specific timings that will be applied when the message is displayed.
|
-- Each Message Type has specific timings that will be applied when the message is displayed.
|
||||||
-- The Settings Menu will provide for each Message Type a selection of proposed durations from which can be choosen.
|
-- The Settings Menu will provide for each Message Type a selection of proposed durations from which can be chosen.
|
||||||
-- So the player can choose its own amount of seconds how long a message should be displayed of a certain type.
|
-- So the player can choose its own amount of seconds how long a message should be displayed of a certain type.
|
||||||
-- Note that **Update** messages can be chosen not to be displayed at all!
|
-- Note that **Update** messages can be chosen not to be displayed at all!
|
||||||
--
|
--
|
||||||
@@ -196,7 +195,7 @@
|
|||||||
--
|
--
|
||||||
-- ## 3.5) **Era** of the battle
|
-- ## 3.5) **Era** of the battle
|
||||||
--
|
--
|
||||||
-- The threat level metric is scaled according the era of the battle. A target that is AAA, will pose a much greather threat in WWII than on modern warfare.
|
-- The threat level metric is scaled according the era of the battle. A target that is AAA, will pose a much greater threat in WWII than on modern warfare.
|
||||||
-- Therefore, there are 4 era that are defined within the settings:
|
-- Therefore, there are 4 era that are defined within the settings:
|
||||||
--
|
--
|
||||||
-- - **WWII** era: Use for warfare with equipment during the world war II time.
|
-- - **WWII** era: Use for warfare with equipment during the world war II time.
|
||||||
@@ -213,8 +212,8 @@
|
|||||||
SETTINGS = {
|
SETTINGS = {
|
||||||
ClassName = "SETTINGS",
|
ClassName = "SETTINGS",
|
||||||
ShowPlayerMenu = true,
|
ShowPlayerMenu = true,
|
||||||
MenuShort = false,
|
MenuShort = false,
|
||||||
MenuStatic = false,
|
MenuStatic = false,
|
||||||
}
|
}
|
||||||
|
|
||||||
SETTINGS.__Enum = {}
|
SETTINGS.__Enum = {}
|
||||||
@@ -231,11 +230,11 @@ SETTINGS.__Enum.Era = {
|
|||||||
Modern = 4,
|
Modern = 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
do -- SETTINGS
|
do -- SETTINGS
|
||||||
|
|
||||||
--- SETTINGS constructor.
|
--- SETTINGS constructor.
|
||||||
-- @param #SETTINGS self
|
-- @param #SETTINGS self
|
||||||
|
-- @param #string PlayerName (Optional) Set settings for this player.
|
||||||
-- @return #SETTINGS
|
-- @return #SETTINGS
|
||||||
function SETTINGS:Set( PlayerName )
|
function SETTINGS:Set( PlayerName )
|
||||||
|
|
||||||
@@ -267,14 +266,14 @@ do -- SETTINGS
|
|||||||
-- Short text are better suited for, e.g., VR.
|
-- Short text are better suited for, e.g., VR.
|
||||||
-- @param #SETTINGS self
|
-- @param #SETTINGS self
|
||||||
-- @param #boolean onoff If *true* use short menu texts. If *false* long ones (default).
|
-- @param #boolean onoff If *true* use short menu texts. If *false* long ones (default).
|
||||||
function SETTINGS:SetMenutextShort(onoff)
|
function SETTINGS:SetMenutextShort( onoff )
|
||||||
_SETTINGS.MenuShort = onoff
|
_SETTINGS.MenuShort = onoff
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set menu to be static.
|
--- Set menu to be static.
|
||||||
-- @param #SETTINGS self
|
-- @param #SETTINGS self
|
||||||
-- @param #boolean onoff If *true* menu is static. If *false* menu will be updated after changes (default).
|
-- @param #boolean onoff If *true* menu is static. If *false* menu will be updated after changes (default).
|
||||||
function SETTINGS:SetMenuStatic(onoff)
|
function SETTINGS:SetMenuStatic( onoff )
|
||||||
_SETTINGS.MenuStatic = onoff
|
_SETTINGS.MenuStatic = onoff
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -288,7 +287,7 @@ do -- SETTINGS
|
|||||||
-- @param #SETTINGS self
|
-- @param #SETTINGS self
|
||||||
-- @return #boolean true if metric.
|
-- @return #boolean true if metric.
|
||||||
function SETTINGS:IsMetric()
|
function SETTINGS:IsMetric()
|
||||||
return ( self.Metric ~= nil and self.Metric == true ) or ( self.Metric == nil and _SETTINGS:IsMetric() )
|
return (self.Metric ~= nil and self.Metric == true) or (self.Metric == nil and _SETTINGS:IsMetric())
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Sets the SETTINGS imperial.
|
--- Sets the SETTINGS imperial.
|
||||||
@@ -301,7 +300,7 @@ do -- SETTINGS
|
|||||||
-- @param #SETTINGS self
|
-- @param #SETTINGS self
|
||||||
-- @return #boolean true if imperial.
|
-- @return #boolean true if imperial.
|
||||||
function SETTINGS:IsImperial()
|
function SETTINGS:IsImperial()
|
||||||
return ( self.Metric ~= nil and self.Metric == false ) or ( self.Metric == nil and _SETTINGS:IsMetric() )
|
return (self.Metric ~= nil and self.Metric == false) or (self.Metric == nil and _SETTINGS:IsMetric())
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Sets the SETTINGS LL accuracy.
|
--- Sets the SETTINGS LL accuracy.
|
||||||
@@ -343,13 +342,12 @@ do -- SETTINGS
|
|||||||
self.MessageTypeTimings[MessageType] = MessageTime
|
self.MessageTypeTimings[MessageType] = MessageTime
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Gets the SETTINGS Message Display Timing of a MessageType
|
--- Gets the SETTINGS Message Display Timing of a MessageType
|
||||||
-- @param #SETTINGS self
|
-- @param #SETTINGS self
|
||||||
-- @param Core.Message#MESSAGE MessageType The type of the message.
|
-- @param Core.Message#MESSAGE MessageType The type of the message.
|
||||||
-- @return #number
|
-- @return #number
|
||||||
function SETTINGS:GetMessageTime( MessageType )
|
function SETTINGS:GetMessageTime( MessageType )
|
||||||
return ( self.MessageTypeTimings and self.MessageTypeTimings[MessageType] ) or _SETTINGS:GetMessageTime( MessageType )
|
return (self.MessageTypeTimings and self.MessageTypeTimings[MessageType]) or _SETTINGS:GetMessageTime( MessageType )
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Sets A2G LL DMS
|
--- Sets A2G LL DMS
|
||||||
@@ -370,14 +368,14 @@ do -- SETTINGS
|
|||||||
-- @param #SETTINGS self
|
-- @param #SETTINGS self
|
||||||
-- @return #boolean true if LL DMS
|
-- @return #boolean true if LL DMS
|
||||||
function SETTINGS:IsA2G_LL_DMS()
|
function SETTINGS:IsA2G_LL_DMS()
|
||||||
return ( self.A2GSystem and self.A2GSystem == "LL DMS" ) or ( not self.A2GSystem and _SETTINGS:IsA2G_LL_DMS() )
|
return (self.A2GSystem and self.A2GSystem == "LL DMS") or (not self.A2GSystem and _SETTINGS:IsA2G_LL_DMS())
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Is LL DDM
|
--- Is LL DDM
|
||||||
-- @param #SETTINGS self
|
-- @param #SETTINGS self
|
||||||
-- @return #boolean true if LL DDM
|
-- @return #boolean true if LL DDM
|
||||||
function SETTINGS:IsA2G_LL_DDM()
|
function SETTINGS:IsA2G_LL_DDM()
|
||||||
return ( self.A2GSystem and self.A2GSystem == "LL DDM" ) or ( not self.A2GSystem and _SETTINGS:IsA2G_LL_DDM() )
|
return (self.A2GSystem and self.A2GSystem == "LL DDM") or (not self.A2GSystem and _SETTINGS:IsA2G_LL_DDM())
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Sets A2G MGRS
|
--- Sets A2G MGRS
|
||||||
@@ -391,7 +389,7 @@ do -- SETTINGS
|
|||||||
-- @param #SETTINGS self
|
-- @param #SETTINGS self
|
||||||
-- @return #boolean true if MGRS
|
-- @return #boolean true if MGRS
|
||||||
function SETTINGS:IsA2G_MGRS()
|
function SETTINGS:IsA2G_MGRS()
|
||||||
return ( self.A2GSystem and self.A2GSystem == "MGRS" ) or ( not self.A2GSystem and _SETTINGS:IsA2G_MGRS() )
|
return (self.A2GSystem and self.A2GSystem == "MGRS") or (not self.A2GSystem and _SETTINGS:IsA2G_MGRS())
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Sets A2G BRA
|
--- Sets A2G BRA
|
||||||
@@ -405,7 +403,7 @@ do -- SETTINGS
|
|||||||
-- @param #SETTINGS self
|
-- @param #SETTINGS self
|
||||||
-- @return #boolean true if BRA
|
-- @return #boolean true if BRA
|
||||||
function SETTINGS:IsA2G_BR()
|
function SETTINGS:IsA2G_BR()
|
||||||
return ( self.A2GSystem and self.A2GSystem == "BR" ) or ( not self.A2GSystem and _SETTINGS:IsA2G_BR() )
|
return (self.A2GSystem and self.A2GSystem == "BR") or (not self.A2GSystem and _SETTINGS:IsA2G_BR())
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Sets A2A BRA
|
--- Sets A2A BRA
|
||||||
@@ -419,7 +417,7 @@ do -- SETTINGS
|
|||||||
-- @param #SETTINGS self
|
-- @param #SETTINGS self
|
||||||
-- @return #boolean true if BRA
|
-- @return #boolean true if BRA
|
||||||
function SETTINGS:IsA2A_BRAA()
|
function SETTINGS:IsA2A_BRAA()
|
||||||
return ( self.A2ASystem and self.A2ASystem == "BRAA" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_BRAA() )
|
return (self.A2ASystem and self.A2ASystem == "BRAA") or (not self.A2ASystem and _SETTINGS:IsA2A_BRAA())
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Sets A2A BULLS
|
--- Sets A2A BULLS
|
||||||
@@ -433,7 +431,7 @@ do -- SETTINGS
|
|||||||
-- @param #SETTINGS self
|
-- @param #SETTINGS self
|
||||||
-- @return #boolean true if BULLS
|
-- @return #boolean true if BULLS
|
||||||
function SETTINGS:IsA2A_BULLS()
|
function SETTINGS:IsA2A_BULLS()
|
||||||
return ( self.A2ASystem and self.A2ASystem == "BULLS" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_BULLS() )
|
return (self.A2ASystem and self.A2ASystem == "BULLS") or (not self.A2ASystem and _SETTINGS:IsA2A_BULLS())
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Sets A2A LL DMS
|
--- Sets A2A LL DMS
|
||||||
@@ -454,14 +452,14 @@ do -- SETTINGS
|
|||||||
-- @param #SETTINGS self
|
-- @param #SETTINGS self
|
||||||
-- @return #boolean true if LL DMS
|
-- @return #boolean true if LL DMS
|
||||||
function SETTINGS:IsA2A_LL_DMS()
|
function SETTINGS:IsA2A_LL_DMS()
|
||||||
return ( self.A2ASystem and self.A2ASystem == "LL DMS" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_LL_DMS() )
|
return (self.A2ASystem and self.A2ASystem == "LL DMS") or (not self.A2ASystem and _SETTINGS:IsA2A_LL_DMS())
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Is LL DDM
|
--- Is LL DDM
|
||||||
-- @param #SETTINGS self
|
-- @param #SETTINGS self
|
||||||
-- @return #boolean true if LL DDM
|
-- @return #boolean true if LL DDM
|
||||||
function SETTINGS:IsA2A_LL_DDM()
|
function SETTINGS:IsA2A_LL_DDM()
|
||||||
return ( self.A2ASystem and self.A2ASystem == "LL DDM" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_LL_DDM() )
|
return (self.A2ASystem and self.A2ASystem == "LL DDM") or (not self.A2ASystem and _SETTINGS:IsA2A_LL_DDM())
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Sets A2A MGRS
|
--- Sets A2A MGRS
|
||||||
@@ -475,7 +473,7 @@ do -- SETTINGS
|
|||||||
-- @param #SETTINGS self
|
-- @param #SETTINGS self
|
||||||
-- @return #boolean true if MGRS
|
-- @return #boolean true if MGRS
|
||||||
function SETTINGS:IsA2A_MGRS()
|
function SETTINGS:IsA2A_MGRS()
|
||||||
return ( self.A2ASystem and self.A2ASystem == "MGRS" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_MGRS() )
|
return (self.A2ASystem and self.A2ASystem == "MGRS") or (not self.A2ASystem and _SETTINGS:IsA2A_MGRS())
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param #SETTINGS self
|
--- @param #SETTINGS self
|
||||||
@@ -494,37 +492,37 @@ do -- SETTINGS
|
|||||||
-- A2G Coordinate System
|
-- A2G Coordinate System
|
||||||
-------
|
-------
|
||||||
|
|
||||||
local text="A2G Coordinate System"
|
local text = "A2G Coordinate System"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="A2G Coordinates"
|
text = "A2G Coordinates"
|
||||||
end
|
end
|
||||||
local A2GCoordinateMenu = MENU_GROUP:New( MenuGroup, text, SettingsMenu ):SetTime( MenuTime )
|
local A2GCoordinateMenu = MENU_GROUP:New( MenuGroup, text, SettingsMenu ):SetTime( MenuTime )
|
||||||
|
|
||||||
-- Set LL DMS
|
-- Set LL DMS
|
||||||
if not self:IsA2G_LL_DMS() then
|
if not self:IsA2G_LL_DMS() then
|
||||||
local text="Lat/Lon Degree Min Sec (LL DMS)"
|
local text = "Lat/Lon Degree Min Sec (LL DMS)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="LL DMS"
|
text = "LL DMS"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( MenuGroup, text, A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "LL DMS" ):SetTime( MenuTime )
|
MENU_GROUP_COMMAND:New( MenuGroup, text, A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "LL DMS" ):SetTime( MenuTime )
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Set LL DDM
|
-- Set LL DDM
|
||||||
if not self:IsA2G_LL_DDM() then
|
if not self:IsA2G_LL_DDM() then
|
||||||
local text="Lat/Lon Degree Dec Min (LL DDM)"
|
local text = "Lat/Lon Degree Dec Min (LL DDM)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="LL DDM"
|
text = "LL DDM"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( MenuGroup, "Lat/Lon Degree Dec Min (LL DDM)", A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "LL DDM" ):SetTime( MenuTime )
|
MENU_GROUP_COMMAND:New( MenuGroup, "Lat/Lon Degree Dec Min (LL DDM)", A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "LL DDM" ):SetTime( MenuTime )
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Set LL DMS accuracy.
|
-- Set LL DMS accuracy.
|
||||||
if self:IsA2G_LL_DDM() then
|
if self:IsA2G_LL_DDM() then
|
||||||
local text1="LL DDM Accuracy 1"
|
local text1 = "LL DDM Accuracy 1"
|
||||||
local text2="LL DDM Accuracy 2"
|
local text2 = "LL DDM Accuracy 2"
|
||||||
local text3="LL DDM Accuracy 3"
|
local text3 = "LL DDM Accuracy 3"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text1="LL DDM"
|
text1 = "LL DDM"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( MenuGroup, "LL DDM Accuracy 1", A2GCoordinateMenu, self.MenuLL_DDM_Accuracy, self, MenuGroup, RootMenu, 1 ):SetTime( MenuTime )
|
MENU_GROUP_COMMAND:New( MenuGroup, "LL DDM Accuracy 1", A2GCoordinateMenu, self.MenuLL_DDM_Accuracy, self, MenuGroup, RootMenu, 1 ):SetTime( MenuTime )
|
||||||
MENU_GROUP_COMMAND:New( MenuGroup, "LL DDM Accuracy 2", A2GCoordinateMenu, self.MenuLL_DDM_Accuracy, self, MenuGroup, RootMenu, 2 ):SetTime( MenuTime )
|
MENU_GROUP_COMMAND:New( MenuGroup, "LL DDM Accuracy 2", A2GCoordinateMenu, self.MenuLL_DDM_Accuracy, self, MenuGroup, RootMenu, 2 ):SetTime( MenuTime )
|
||||||
@@ -533,18 +531,18 @@ do -- SETTINGS
|
|||||||
|
|
||||||
-- Set BR.
|
-- Set BR.
|
||||||
if not self:IsA2G_BR() then
|
if not self:IsA2G_BR() then
|
||||||
local text="Bearing, Range (BR)"
|
local text = "Bearing, Range (BR)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="BR"
|
text = "BR"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( MenuGroup, text , A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "BR" ):SetTime( MenuTime )
|
MENU_GROUP_COMMAND:New( MenuGroup, text, A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "BR" ):SetTime( MenuTime )
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Set MGRS.
|
-- Set MGRS.
|
||||||
if not self:IsA2G_MGRS() then
|
if not self:IsA2G_MGRS() then
|
||||||
local text="Military Grid (MGRS)"
|
local text = "Military Grid (MGRS)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="MGRS"
|
text = "MGRS"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( MenuGroup, text, A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "MGRS" ):SetTime( MenuTime )
|
MENU_GROUP_COMMAND:New( MenuGroup, text, A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "MGRS" ):SetTime( MenuTime )
|
||||||
end
|
end
|
||||||
@@ -562,24 +560,24 @@ do -- SETTINGS
|
|||||||
-- A2A Coordinate System
|
-- A2A Coordinate System
|
||||||
-------
|
-------
|
||||||
|
|
||||||
local text="A2A Coordinate System"
|
local text = "A2A Coordinate System"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="A2A Coordinates"
|
text = "A2A Coordinates"
|
||||||
end
|
end
|
||||||
local A2ACoordinateMenu = MENU_GROUP:New( MenuGroup, text, SettingsMenu ):SetTime( MenuTime )
|
local A2ACoordinateMenu = MENU_GROUP:New( MenuGroup, text, SettingsMenu ):SetTime( MenuTime )
|
||||||
|
|
||||||
if not self:IsA2A_LL_DMS() then
|
if not self:IsA2A_LL_DMS() then
|
||||||
local text="Lat/Lon Degree Min Sec (LL DMS)"
|
local text = "Lat/Lon Degree Min Sec (LL DMS)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="LL DMS"
|
text = "LL DMS"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "LL DMS" ):SetTime( MenuTime )
|
MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "LL DMS" ):SetTime( MenuTime )
|
||||||
end
|
end
|
||||||
|
|
||||||
if not self:IsA2A_LL_DDM() then
|
if not self:IsA2A_LL_DDM() then
|
||||||
local text="Lat/Lon Degree Dec Min (LL DDM)"
|
local text = "Lat/Lon Degree Dec Min (LL DDM)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="LL DDM"
|
text = "LL DDM"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "LL DDM" ):SetTime( MenuTime )
|
MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "LL DDM" ):SetTime( MenuTime )
|
||||||
end
|
end
|
||||||
@@ -592,25 +590,25 @@ do -- SETTINGS
|
|||||||
end
|
end
|
||||||
|
|
||||||
if not self:IsA2A_BULLS() then
|
if not self:IsA2A_BULLS() then
|
||||||
local text="Bullseye (BULLS)"
|
local text = "Bullseye (BULLS)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="Bulls"
|
text = "Bulls"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "BULLS" ):SetTime( MenuTime )
|
MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "BULLS" ):SetTime( MenuTime )
|
||||||
end
|
end
|
||||||
|
|
||||||
if not self:IsA2A_BRAA() then
|
if not self:IsA2A_BRAA() then
|
||||||
local text="Bearing Range Altitude Aspect (BRAA)"
|
local text = "Bearing Range Altitude Aspect (BRAA)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="BRAA"
|
text = "BRAA"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "BRAA" ):SetTime( MenuTime )
|
MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "BRAA" ):SetTime( MenuTime )
|
||||||
end
|
end
|
||||||
|
|
||||||
if not self:IsA2A_MGRS() then
|
if not self:IsA2A_MGRS() then
|
||||||
local text="Military Grid (MGRS)"
|
local text = "Military Grid (MGRS)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="MGRS"
|
text = "MGRS"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "MGRS" ):SetTime( MenuTime )
|
MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "MGRS" ):SetTime( MenuTime )
|
||||||
end
|
end
|
||||||
@@ -623,31 +621,31 @@ do -- SETTINGS
|
|||||||
MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 5", A2ACoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 5 ):SetTime( MenuTime )
|
MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 5", A2ACoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 5 ):SetTime( MenuTime )
|
||||||
end
|
end
|
||||||
|
|
||||||
local text="Measures and Weights System"
|
local text = "Measures and Weights System"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="Unit System"
|
text = "Unit System"
|
||||||
end
|
end
|
||||||
local MetricsMenu = MENU_GROUP:New( MenuGroup, text, SettingsMenu ):SetTime( MenuTime )
|
local MetricsMenu = MENU_GROUP:New( MenuGroup, text, SettingsMenu ):SetTime( MenuTime )
|
||||||
|
|
||||||
if self:IsMetric() then
|
if self:IsMetric() then
|
||||||
local text="Imperial (Miles,Feet)"
|
local text = "Imperial (Miles,Feet)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="Imperial"
|
text = "Imperial"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( MenuGroup, text, MetricsMenu, self.MenuMWSystem, self, MenuGroup, RootMenu, false ):SetTime( MenuTime )
|
MENU_GROUP_COMMAND:New( MenuGroup, text, MetricsMenu, self.MenuMWSystem, self, MenuGroup, RootMenu, false ):SetTime( MenuTime )
|
||||||
end
|
end
|
||||||
|
|
||||||
if self:IsImperial() then
|
if self:IsImperial() then
|
||||||
local text="Metric (Kilometers,Meters)"
|
local text = "Metric (Kilometers,Meters)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="Metric"
|
text = "Metric"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( MenuGroup, text, MetricsMenu, self.MenuMWSystem, self, MenuGroup, RootMenu, true ):SetTime( MenuTime )
|
MENU_GROUP_COMMAND:New( MenuGroup, text, MetricsMenu, self.MenuMWSystem, self, MenuGroup, RootMenu, true ):SetTime( MenuTime )
|
||||||
end
|
end
|
||||||
|
|
||||||
local text="Messages and Reports"
|
local text = "Messages and Reports"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="Messages & Reports"
|
text = "Messages & Reports"
|
||||||
end
|
end
|
||||||
local MessagesMenu = MENU_GROUP:New( MenuGroup, text, SettingsMenu ):SetTime( MenuTime )
|
local MessagesMenu = MENU_GROUP:New( MenuGroup, text, SettingsMenu ):SetTime( MenuTime )
|
||||||
|
|
||||||
@@ -688,7 +686,6 @@ do -- SETTINGS
|
|||||||
MENU_GROUP_COMMAND:New( MenuGroup, "2 minutes", DetailedReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.DetailedReportsMenu, 120 ):SetTime( MenuTime )
|
MENU_GROUP_COMMAND:New( MenuGroup, "2 minutes", DetailedReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.DetailedReportsMenu, 120 ):SetTime( MenuTime )
|
||||||
MENU_GROUP_COMMAND:New( MenuGroup, "3 minutes", DetailedReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.DetailedReportsMenu, 180 ):SetTime( MenuTime )
|
MENU_GROUP_COMMAND:New( MenuGroup, "3 minutes", DetailedReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.DetailedReportsMenu, 180 ):SetTime( MenuTime )
|
||||||
|
|
||||||
|
|
||||||
SettingsMenu:Remove( MenuTime )
|
SettingsMenu:Remove( MenuTime )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
@@ -732,11 +729,11 @@ do -- SETTINGS
|
|||||||
|
|
||||||
self.PlayerMenu = PlayerMenu
|
self.PlayerMenu = PlayerMenu
|
||||||
|
|
||||||
self:I(string.format("Setting menu for player %s", tostring(PlayerName)))
|
self:I( string.format( "Setting menu for player %s", tostring( PlayerName ) ) )
|
||||||
|
|
||||||
local submenu = MENU_GROUP:New( PlayerGroup, "LL Accuracy", PlayerMenu )
|
local submenu = MENU_GROUP:New( PlayerGroup, "LL Accuracy", PlayerMenu )
|
||||||
MENU_GROUP_COMMAND:New( PlayerGroup, "LL 0 Decimals", submenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 0 )
|
MENU_GROUP_COMMAND:New( PlayerGroup, "LL 0 Decimals", submenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 0 )
|
||||||
MENU_GROUP_COMMAND:New( PlayerGroup, "LL 1 Decimal", submenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 1 )
|
MENU_GROUP_COMMAND:New( PlayerGroup, "LL 1 Decimal", submenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 1 )
|
||||||
MENU_GROUP_COMMAND:New( PlayerGroup, "LL 2 Decimals", submenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 2 )
|
MENU_GROUP_COMMAND:New( PlayerGroup, "LL 2 Decimals", submenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 2 )
|
||||||
MENU_GROUP_COMMAND:New( PlayerGroup, "LL 3 Decimals", submenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 3 )
|
MENU_GROUP_COMMAND:New( PlayerGroup, "LL 3 Decimals", submenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 3 )
|
||||||
MENU_GROUP_COMMAND:New( PlayerGroup, "LL 4 Decimals", submenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 4 )
|
MENU_GROUP_COMMAND:New( PlayerGroup, "LL 4 Decimals", submenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 4 )
|
||||||
@@ -753,40 +750,40 @@ do -- SETTINGS
|
|||||||
-- A2G Coordinate System
|
-- A2G Coordinate System
|
||||||
------
|
------
|
||||||
|
|
||||||
local text="A2G Coordinate System"
|
local text = "A2G Coordinate System"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="A2G Coordinates"
|
text = "A2G Coordinates"
|
||||||
end
|
end
|
||||||
local A2GCoordinateMenu = MENU_GROUP:New( PlayerGroup, text, PlayerMenu )
|
local A2GCoordinateMenu = MENU_GROUP:New( PlayerGroup, text, PlayerMenu )
|
||||||
|
|
||||||
if not self:IsA2G_LL_DMS() or _SETTINGS.MenuStatic then
|
if not self:IsA2G_LL_DMS() or _SETTINGS.MenuStatic then
|
||||||
local text="Lat/Lon Degree Min Sec (LL DMS)"
|
local text = "Lat/Lon Degree Min Sec (LL DMS)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="A2G LL DMS"
|
text = "A2G LL DMS"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DMS" )
|
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DMS" )
|
||||||
end
|
end
|
||||||
|
|
||||||
if not self:IsA2G_LL_DDM() or _SETTINGS.MenuStatic then
|
if not self:IsA2G_LL_DDM() or _SETTINGS.MenuStatic then
|
||||||
local text="Lat/Lon Degree Dec Min (LL DDM)"
|
local text = "Lat/Lon Degree Dec Min (LL DDM)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="A2G LL DDM"
|
text = "A2G LL DDM"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DDM" )
|
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DDM" )
|
||||||
end
|
end
|
||||||
|
|
||||||
if not self:IsA2G_BR() or _SETTINGS.MenuStatic then
|
if not self:IsA2G_BR() or _SETTINGS.MenuStatic then
|
||||||
local text="Bearing, Range (BR)"
|
local text = "Bearing, Range (BR)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="A2G BR"
|
text = "A2G BR"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "BR" )
|
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "BR" )
|
||||||
end
|
end
|
||||||
|
|
||||||
if not self:IsA2G_MGRS() or _SETTINGS.MenuStatic then
|
if not self:IsA2G_MGRS() or _SETTINGS.MenuStatic then
|
||||||
local text="Military Grid (MGRS)"
|
local text = "Military Grid (MGRS)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="A2G MGRS"
|
text = "A2G MGRS"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "MGRS" )
|
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "MGRS" )
|
||||||
end
|
end
|
||||||
@@ -795,49 +792,48 @@ do -- SETTINGS
|
|||||||
-- A2A Coordinates Menu
|
-- A2A Coordinates Menu
|
||||||
------
|
------
|
||||||
|
|
||||||
local text="A2A Coordinate System"
|
local text = "A2A Coordinate System"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="A2A Coordinates"
|
text = "A2A Coordinates"
|
||||||
end
|
end
|
||||||
local A2ACoordinateMenu = MENU_GROUP:New( PlayerGroup, text, PlayerMenu )
|
local A2ACoordinateMenu = MENU_GROUP:New( PlayerGroup, text, PlayerMenu )
|
||||||
|
|
||||||
|
|
||||||
if not self:IsA2A_LL_DMS() or _SETTINGS.MenuStatic then
|
if not self:IsA2A_LL_DMS() or _SETTINGS.MenuStatic then
|
||||||
local text="Lat/Lon Degree Min Sec (LL DMS)"
|
local text = "Lat/Lon Degree Min Sec (LL DMS)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="A2A LL DMS"
|
text = "A2A LL DMS"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DMS" )
|
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DMS" )
|
||||||
end
|
end
|
||||||
|
|
||||||
if not self:IsA2A_LL_DDM() or _SETTINGS.MenuStatic then
|
if not self:IsA2A_LL_DDM() or _SETTINGS.MenuStatic then
|
||||||
local text="Lat/Lon Degree Dec Min (LL DDM)"
|
local text = "Lat/Lon Degree Dec Min (LL DDM)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="A2A LL DDM"
|
text = "A2A LL DDM"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DDM" )
|
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DDM" )
|
||||||
end
|
end
|
||||||
|
|
||||||
if not self:IsA2A_BULLS() or _SETTINGS.MenuStatic then
|
if not self:IsA2A_BULLS() or _SETTINGS.MenuStatic then
|
||||||
local text="Bullseye (BULLS)"
|
local text = "Bullseye (BULLS)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="A2A BULLS"
|
text = "A2A BULLS"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "BULLS" )
|
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "BULLS" )
|
||||||
end
|
end
|
||||||
|
|
||||||
if not self:IsA2A_BRAA() or _SETTINGS.MenuStatic then
|
if not self:IsA2A_BRAA() or _SETTINGS.MenuStatic then
|
||||||
local text="Bearing Range Altitude Aspect (BRAA)"
|
local text = "Bearing Range Altitude Aspect (BRAA)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="A2A BRAA"
|
text = "A2A BRAA"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "BRAA" )
|
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "BRAA" )
|
||||||
end
|
end
|
||||||
|
|
||||||
if not self:IsA2A_MGRS() or _SETTINGS.MenuStatic then
|
if not self:IsA2A_MGRS() or _SETTINGS.MenuStatic then
|
||||||
local text="Military Grid (MGRS)"
|
local text = "Military Grid (MGRS)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="A2A MGRS"
|
text = "A2A MGRS"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "MGRS" )
|
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "MGRS" )
|
||||||
end
|
end
|
||||||
@@ -846,24 +842,24 @@ do -- SETTINGS
|
|||||||
-- Unit system
|
-- Unit system
|
||||||
---
|
---
|
||||||
|
|
||||||
local text="Measures and Weights System"
|
local text = "Measures and Weights System"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="Unit System"
|
text = "Unit System"
|
||||||
end
|
end
|
||||||
local MetricsMenu = MENU_GROUP:New( PlayerGroup, text, PlayerMenu )
|
local MetricsMenu = MENU_GROUP:New( PlayerGroup, text, PlayerMenu )
|
||||||
|
|
||||||
if self:IsMetric() or _SETTINGS.MenuStatic then
|
if self:IsMetric() or _SETTINGS.MenuStatic then
|
||||||
local text="Imperial (Miles,Feet)"
|
local text = "Imperial (Miles,Feet)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="Imperial"
|
text = "Imperial"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, MetricsMenu, self.MenuGroupMWSystem, self, PlayerUnit, PlayerGroup, PlayerName, false )
|
MENU_GROUP_COMMAND:New( PlayerGroup, text, MetricsMenu, self.MenuGroupMWSystem, self, PlayerUnit, PlayerGroup, PlayerName, false )
|
||||||
end
|
end
|
||||||
|
|
||||||
if self:IsImperial() or _SETTINGS.MenuStatic then
|
if self:IsImperial() or _SETTINGS.MenuStatic then
|
||||||
local text="Metric (Kilometers,Meters)"
|
local text = "Metric (Kilometers,Meters)"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="Metric"
|
text = "Metric"
|
||||||
end
|
end
|
||||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, MetricsMenu, self.MenuGroupMWSystem, self, PlayerUnit, PlayerGroup, PlayerName, true )
|
MENU_GROUP_COMMAND:New( PlayerGroup, text, MetricsMenu, self.MenuGroupMWSystem, self, PlayerUnit, PlayerGroup, PlayerName, true )
|
||||||
end
|
end
|
||||||
@@ -872,9 +868,9 @@ do -- SETTINGS
|
|||||||
-- Messages and Reports
|
-- Messages and Reports
|
||||||
---
|
---
|
||||||
|
|
||||||
local text="Messages and Reports"
|
local text = "Messages and Reports"
|
||||||
if _SETTINGS.MenuShort then
|
if _SETTINGS.MenuShort then
|
||||||
text="Messages & Reports"
|
text = "Messages & Reports"
|
||||||
end
|
end
|
||||||
local MessagesMenu = MENU_GROUP:New( PlayerGroup, text, PlayerMenu )
|
local MessagesMenu = MENU_GROUP:New( PlayerGroup, text, PlayerMenu )
|
||||||
|
|
||||||
@@ -934,39 +930,38 @@ do -- SETTINGS
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- @param #SETTINGS self
|
--- @param #SETTINGS self
|
||||||
function SETTINGS:A2GMenuSystem( MenuGroup, RootMenu, A2GSystem )
|
function SETTINGS:A2GMenuSystem( MenuGroup, RootMenu, A2GSystem )
|
||||||
self.A2GSystem = A2GSystem
|
self.A2GSystem = A2GSystem
|
||||||
MESSAGE:New( string.format("Settings: Default A2G coordinate system set to %s for all players!", A2GSystem ), 5 ):ToAll()
|
MESSAGE:New( string.format( "Settings: Default A2G coordinate system set to %s for all players!", A2GSystem ), 5 ):ToAll()
|
||||||
self:SetSystemMenu( MenuGroup, RootMenu )
|
self:SetSystemMenu( MenuGroup, RootMenu )
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param #SETTINGS self
|
--- @param #SETTINGS self
|
||||||
function SETTINGS:A2AMenuSystem( MenuGroup, RootMenu, A2ASystem )
|
function SETTINGS:A2AMenuSystem( MenuGroup, RootMenu, A2ASystem )
|
||||||
self.A2ASystem = A2ASystem
|
self.A2ASystem = A2ASystem
|
||||||
MESSAGE:New( string.format("Settings: Default A2A coordinate system set to %s for all players!", A2ASystem ), 5 ):ToAll()
|
MESSAGE:New( string.format( "Settings: Default A2A coordinate system set to %s for all players!", A2ASystem ), 5 ):ToAll()
|
||||||
self:SetSystemMenu( MenuGroup, RootMenu )
|
self:SetSystemMenu( MenuGroup, RootMenu )
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param #SETTINGS self
|
--- @param #SETTINGS self
|
||||||
function SETTINGS:MenuLL_DDM_Accuracy( MenuGroup, RootMenu, LL_Accuracy )
|
function SETTINGS:MenuLL_DDM_Accuracy( MenuGroup, RootMenu, LL_Accuracy )
|
||||||
self.LL_Accuracy = LL_Accuracy
|
self.LL_Accuracy = LL_Accuracy
|
||||||
MESSAGE:New( string.format("Settings: Default LL accuracy set to %s for all players!", LL_Accuracy ), 5 ):ToAll()
|
MESSAGE:New( string.format( "Settings: Default LL accuracy set to %s for all players!", LL_Accuracy ), 5 ):ToAll()
|
||||||
self:SetSystemMenu( MenuGroup, RootMenu )
|
self:SetSystemMenu( MenuGroup, RootMenu )
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param #SETTINGS self
|
--- @param #SETTINGS self
|
||||||
function SETTINGS:MenuMGRS_Accuracy( MenuGroup, RootMenu, MGRS_Accuracy )
|
function SETTINGS:MenuMGRS_Accuracy( MenuGroup, RootMenu, MGRS_Accuracy )
|
||||||
self.MGRS_Accuracy = MGRS_Accuracy
|
self.MGRS_Accuracy = MGRS_Accuracy
|
||||||
MESSAGE:New( string.format("Settings: Default MGRS accuracy set to %s for all players!", MGRS_Accuracy ), 5 ):ToAll()
|
MESSAGE:New( string.format( "Settings: Default MGRS accuracy set to %s for all players!", MGRS_Accuracy ), 5 ):ToAll()
|
||||||
self:SetSystemMenu( MenuGroup, RootMenu )
|
self:SetSystemMenu( MenuGroup, RootMenu )
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param #SETTINGS self
|
--- @param #SETTINGS self
|
||||||
function SETTINGS:MenuMWSystem( MenuGroup, RootMenu, MW )
|
function SETTINGS:MenuMWSystem( MenuGroup, RootMenu, MW )
|
||||||
self.Metric = MW
|
self.Metric = MW
|
||||||
MESSAGE:New( string.format("Settings: Default measurement format set to %s for all players!", MW and "Metric" or "Imperial" ), 5 ):ToAll()
|
MESSAGE:New( string.format( "Settings: Default measurement format set to %s for all players!", MW and "Metric" or "Imperial" ), 5 ):ToAll()
|
||||||
self:SetSystemMenu( MenuGroup, RootMenu )
|
self:SetSystemMenu( MenuGroup, RootMenu )
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -979,12 +974,12 @@ do -- SETTINGS
|
|||||||
do
|
do
|
||||||
--- @param #SETTINGS self
|
--- @param #SETTINGS self
|
||||||
function SETTINGS:MenuGroupA2GSystem( PlayerUnit, PlayerGroup, PlayerName, A2GSystem )
|
function SETTINGS:MenuGroupA2GSystem( PlayerUnit, PlayerGroup, PlayerName, A2GSystem )
|
||||||
BASE:E( {self, PlayerUnit:GetName(), A2GSystem} )
|
BASE:E( { self, PlayerUnit:GetName(), A2GSystem } )
|
||||||
self.A2GSystem = A2GSystem
|
self.A2GSystem = A2GSystem
|
||||||
MESSAGE:New( string.format( "Settings: A2G format set to %s for player %s.", A2GSystem, PlayerName ), 5 ):ToGroup( PlayerGroup )
|
MESSAGE:New( string.format( "Settings: A2G format set to %s for player %s.", A2GSystem, PlayerName ), 5 ):ToGroup( PlayerGroup )
|
||||||
if _SETTINGS.MenuStatic==false then
|
if _SETTINGS.MenuStatic == false then
|
||||||
self:RemovePlayerMenu(PlayerUnit)
|
self:RemovePlayerMenu( PlayerUnit )
|
||||||
self:SetPlayerMenu(PlayerUnit)
|
self:SetPlayerMenu( PlayerUnit )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -992,9 +987,9 @@ do -- SETTINGS
|
|||||||
function SETTINGS:MenuGroupA2ASystem( PlayerUnit, PlayerGroup, PlayerName, A2ASystem )
|
function SETTINGS:MenuGroupA2ASystem( PlayerUnit, PlayerGroup, PlayerName, A2ASystem )
|
||||||
self.A2ASystem = A2ASystem
|
self.A2ASystem = A2ASystem
|
||||||
MESSAGE:New( string.format( "Settings: A2A format set to %s for player %s.", A2ASystem, PlayerName ), 5 ):ToGroup( PlayerGroup )
|
MESSAGE:New( string.format( "Settings: A2A format set to %s for player %s.", A2ASystem, PlayerName ), 5 ):ToGroup( PlayerGroup )
|
||||||
if _SETTINGS.MenuStatic==false then
|
if _SETTINGS.MenuStatic == false then
|
||||||
self:RemovePlayerMenu(PlayerUnit)
|
self:RemovePlayerMenu( PlayerUnit )
|
||||||
self:SetPlayerMenu(PlayerUnit)
|
self:SetPlayerMenu( PlayerUnit )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1002,9 +997,9 @@ do -- SETTINGS
|
|||||||
function SETTINGS:MenuGroupLL_DDM_AccuracySystem( PlayerUnit, PlayerGroup, PlayerName, LL_Accuracy )
|
function SETTINGS:MenuGroupLL_DDM_AccuracySystem( PlayerUnit, PlayerGroup, PlayerName, LL_Accuracy )
|
||||||
self.LL_Accuracy = LL_Accuracy
|
self.LL_Accuracy = LL_Accuracy
|
||||||
MESSAGE:New( string.format( "Settings: LL format accuracy set to %d decimal places for player %s.", LL_Accuracy, PlayerName ), 5 ):ToGroup( PlayerGroup )
|
MESSAGE:New( string.format( "Settings: LL format accuracy set to %d decimal places for player %s.", LL_Accuracy, PlayerName ), 5 ):ToGroup( PlayerGroup )
|
||||||
if _SETTINGS.MenuStatic==false then
|
if _SETTINGS.MenuStatic == false then
|
||||||
self:RemovePlayerMenu(PlayerUnit)
|
self:RemovePlayerMenu( PlayerUnit )
|
||||||
self:SetPlayerMenu(PlayerUnit)
|
self:SetPlayerMenu( PlayerUnit )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1012,9 +1007,9 @@ do -- SETTINGS
|
|||||||
function SETTINGS:MenuGroupMGRS_AccuracySystem( PlayerUnit, PlayerGroup, PlayerName, MGRS_Accuracy )
|
function SETTINGS:MenuGroupMGRS_AccuracySystem( PlayerUnit, PlayerGroup, PlayerName, MGRS_Accuracy )
|
||||||
self.MGRS_Accuracy = MGRS_Accuracy
|
self.MGRS_Accuracy = MGRS_Accuracy
|
||||||
MESSAGE:New( string.format( "Settings: MGRS format accuracy set to %d for player %s.", MGRS_Accuracy, PlayerName ), 5 ):ToGroup( PlayerGroup )
|
MESSAGE:New( string.format( "Settings: MGRS format accuracy set to %d for player %s.", MGRS_Accuracy, PlayerName ), 5 ):ToGroup( PlayerGroup )
|
||||||
if _SETTINGS.MenuStatic==false then
|
if _SETTINGS.MenuStatic == false then
|
||||||
self:RemovePlayerMenu(PlayerUnit)
|
self:RemovePlayerMenu( PlayerUnit )
|
||||||
self:SetPlayerMenu(PlayerUnit)
|
self:SetPlayerMenu( PlayerUnit )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1022,9 +1017,9 @@ do -- SETTINGS
|
|||||||
function SETTINGS:MenuGroupMWSystem( PlayerUnit, PlayerGroup, PlayerName, MW )
|
function SETTINGS:MenuGroupMWSystem( PlayerUnit, PlayerGroup, PlayerName, MW )
|
||||||
self.Metric = MW
|
self.Metric = MW
|
||||||
MESSAGE:New( string.format( "Settings: Measurement format set to %s for player %s.", MW and "Metric" or "Imperial", PlayerName ), 5 ):ToGroup( PlayerGroup )
|
MESSAGE:New( string.format( "Settings: Measurement format set to %s for player %s.", MW and "Metric" or "Imperial", PlayerName ), 5 ):ToGroup( PlayerGroup )
|
||||||
if _SETTINGS.MenuStatic==false then
|
if _SETTINGS.MenuStatic == false then
|
||||||
self:RemovePlayerMenu(PlayerUnit)
|
self:RemovePlayerMenu( PlayerUnit )
|
||||||
self:SetPlayerMenu(PlayerUnit)
|
self:SetPlayerMenu( PlayerUnit )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1054,7 +1049,6 @@ do -- SETTINGS
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Configures the era of the mission to be Cold war.
|
--- Configures the era of the mission to be Cold war.
|
||||||
-- @param #SETTINGS self
|
-- @param #SETTINGS self
|
||||||
-- @return #SETTINGS self
|
-- @return #SETTINGS self
|
||||||
@@ -1064,7 +1058,6 @@ do -- SETTINGS
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Configures the era of the mission to be Modern war.
|
--- Configures the era of the mission to be Modern war.
|
||||||
-- @param #SETTINGS self
|
-- @param #SETTINGS self
|
||||||
-- @return #SETTINGS self
|
-- @return #SETTINGS self
|
||||||
@@ -1074,7 +1067,4 @@ do -- SETTINGS
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -138,24 +138,24 @@ SPAWNSTATIC = {
|
|||||||
-- @return #SPAWNSTATIC self
|
-- @return #SPAWNSTATIC self
|
||||||
function SPAWNSTATIC:NewFromStatic(SpawnTemplateName, SpawnCountryID)
|
function SPAWNSTATIC:NewFromStatic(SpawnTemplateName, SpawnCountryID)
|
||||||
|
|
||||||
local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC
|
local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC
|
||||||
|
|
||||||
local TemplateStatic, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate(SpawnTemplateName)
|
local TemplateStatic, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate(SpawnTemplateName)
|
||||||
|
|
||||||
if TemplateStatic then
|
if TemplateStatic then
|
||||||
self.SpawnTemplatePrefix = SpawnTemplateName
|
self.SpawnTemplatePrefix = SpawnTemplateName
|
||||||
self.TemplateStaticUnit = UTILS.DeepCopy(TemplateStatic.units[1])
|
self.TemplateStaticUnit = UTILS.DeepCopy(TemplateStatic.units[1])
|
||||||
self.CountryID = SpawnCountryID or CountryID
|
self.CountryID = SpawnCountryID or CountryID
|
||||||
self.CategoryID = CategoryID
|
self.CategoryID = CategoryID
|
||||||
self.CoalitionID = CoalitionID
|
self.CoalitionID = CoalitionID
|
||||||
self.SpawnIndex = 0
|
self.SpawnIndex = 0
|
||||||
else
|
else
|
||||||
error( "SPAWNSTATIC:New: There is no static declared in the mission editor with SpawnTemplatePrefix = '" .. tostring(SpawnTemplateName) .. "'" )
|
error( "SPAWNSTATIC:New: There is no static declared in the mission editor with SpawnTemplatePrefix = '" .. tostring(SpawnTemplateName) .. "'" )
|
||||||
end
|
end
|
||||||
|
|
||||||
self:SetEventPriority( 5 )
|
self:SetEventPriority( 5 )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Creates the main object to spawn a @{Static} given a template table.
|
--- Creates the main object to spawn a @{Static} given a template table.
|
||||||
@@ -241,6 +241,20 @@ function SPAWNSTATIC:InitShape(StaticShape)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Initialize parameters for spawning FARPs.
|
||||||
|
-- @param #SPAWNSTATIC self
|
||||||
|
-- @param #number CallsignID Callsign ID. Default 1 (="London").
|
||||||
|
-- @param #number Frequency Frequency in MHz. Default 127.5 MHz.
|
||||||
|
-- @param #number Modulation Modulation 0=AM, 1=FM.
|
||||||
|
-- @return #SPAWNSTATIC self
|
||||||
|
function SPAWNSTATIC:InitFARP(CallsignID, Frequency, Modulation)
|
||||||
|
self.InitFarp=true
|
||||||
|
self.InitFarpCallsignID=CallsignID or 1
|
||||||
|
self.InitFarpFreq=Frequency or 127.5
|
||||||
|
self.InitFarpModu=Modulation or 0
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Initialize cargo mass.
|
--- Initialize cargo mass.
|
||||||
-- @param #SPAWNSTATIC self
|
-- @param #SPAWNSTATIC self
|
||||||
-- @param #number Mass Mass of the cargo in kg.
|
-- @param #number Mass Mass of the cargo in kg.
|
||||||
@@ -408,7 +422,11 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if self.InitCargo~=nil then
|
if self.InitCargo~=nil then
|
||||||
Template.isCargo=self.InitCargo
|
Template.canCargo=self.InitCargo
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.InitCargoMass~=nil then
|
||||||
|
Template.mass=self.InitCargoMass
|
||||||
end
|
end
|
||||||
|
|
||||||
if self.InitLinkUnit then
|
if self.InitLinkUnit then
|
||||||
@@ -420,22 +438,49 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
|
|||||||
Template.offsets.angle=self.InitOffsetAngle and math.rad(self.InitOffsetAngle) or 0
|
Template.offsets.angle=self.InitOffsetAngle and math.rad(self.InitOffsetAngle) or 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if self.InitFarp then
|
||||||
|
Template.heliport_callsign_id = self.InitFarpCallsignID
|
||||||
|
Template.heliport_frequency = self.InitFarpFreq
|
||||||
|
Template.heliport_modulation = self.InitFarpModu
|
||||||
|
Template.unitId=nil
|
||||||
|
end
|
||||||
|
|
||||||
-- Increase spawn index counter.
|
-- Increase spawn index counter.
|
||||||
self.SpawnIndex = self.SpawnIndex + 1
|
self.SpawnIndex = self.SpawnIndex + 1
|
||||||
|
|
||||||
-- Name of the spawned static.
|
-- Name of the spawned static.
|
||||||
Template.name = self.InitStaticName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex)
|
Template.name = self.InitStaticName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex)
|
||||||
|
|
||||||
-- Register the new static.
|
-- Add and register the new static.
|
||||||
--_DATABASE:_RegisterStaticTemplate(Template, self.CoalitionID, self.CategoryID, CountryID)
|
local mystatic=_DATABASE:AddStatic(Template.name)
|
||||||
_DATABASE:AddStatic(Template.name)
|
|
||||||
|
|
||||||
-- Debug output.
|
-- Debug output.
|
||||||
self:T(Template)
|
self:T(Template)
|
||||||
|
|
||||||
-- Add static to the game.
|
-- Add static to the game.
|
||||||
local Static=coalition.addStaticObject(CountryID, Template)
|
local Static=nil
|
||||||
|
|
||||||
|
if self.InitFarp then
|
||||||
|
|
||||||
return _DATABASE:FindStatic(Static:getName())
|
local TemplateGroup={}
|
||||||
|
TemplateGroup.units={}
|
||||||
|
TemplateGroup.units[1]=Template
|
||||||
|
|
||||||
|
TemplateGroup.visible=true
|
||||||
|
TemplateGroup.hidden=false
|
||||||
|
TemplateGroup.x=Template.x
|
||||||
|
TemplateGroup.y=Template.y
|
||||||
|
TemplateGroup.name=Template.name
|
||||||
|
|
||||||
|
self:T("Spawning FARP")
|
||||||
|
self:T({Template=Template})
|
||||||
|
self:T({TemplateGroup=TemplateGroup})
|
||||||
|
|
||||||
|
-- ED's dirty way to spawn FARPS.
|
||||||
|
Static=coalition.addGroup(CountryID, -1, TemplateGroup)
|
||||||
|
else
|
||||||
|
Static=coalition.addStaticObject(CountryID, Template)
|
||||||
|
end
|
||||||
|
|
||||||
|
return mystatic
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -341,7 +341,7 @@ do
|
|||||||
-- @return #SPOT
|
-- @return #SPOT
|
||||||
function SPOT:onafterLaseOff( From, Event, To )
|
function SPOT:onafterLaseOff( From, Event, To )
|
||||||
|
|
||||||
self:F( {"Stopped lasing for ", self.Target:GetName() , SpotIR = self.SportIR, SpotLaser = self.SpotLaser } )
|
self:F( {"Stopped lasing for ", self.Target and self.Target:GetName() or "coord", SpotIR = self.SportIR, SpotLaser = self.SpotLaser } )
|
||||||
|
|
||||||
self.Lasing = false
|
self.Lasing = false
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
--
|
--
|
||||||
-- ### Author: **funkyfranky**
|
-- ### Author: **funkyfranky**
|
||||||
-- @module Core.Timer
|
-- @module Core.Timer
|
||||||
-- @image CORE_Timer.png
|
-- @image Core_Scheduler.JPG
|
||||||
|
|
||||||
|
|
||||||
--- TIMER class.
|
--- TIMER class.
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
-- @field #string ClassName Name of the class.
|
-- @field #string ClassName Name of the class.
|
||||||
-- @field #string lid Class id string for output to DCS log file.
|
-- @field #string lid Class id string for output to DCS log file.
|
||||||
-- @field #number tid Timer ID returned by the DCS API function.
|
-- @field #number tid Timer ID returned by the DCS API function.
|
||||||
|
-- @field #number uid Unique ID of the timer.
|
||||||
-- @field #function func Timer function.
|
-- @field #function func Timer function.
|
||||||
-- @field #table para Parameters passed to the timer function.
|
-- @field #table para Parameters passed to the timer function.
|
||||||
-- @field #number Tstart Relative start time in seconds.
|
-- @field #number Tstart Relative start time in seconds.
|
||||||
@@ -26,9 +27,10 @@
|
|||||||
-- @field #number dT Time interval between function calls in seconds.
|
-- @field #number dT Time interval between function calls in seconds.
|
||||||
-- @field #number ncalls Counter of function calls.
|
-- @field #number ncalls Counter of function calls.
|
||||||
-- @field #number ncallsMax Max number of function calls. If reached, timer is stopped.
|
-- @field #number ncallsMax Max number of function calls. If reached, timer is stopped.
|
||||||
|
-- @field #boolean isrunning If `true`, timer is running. Else it was not started yet or was stopped.
|
||||||
-- @extends Core.Base#BASE
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
--- *Better three hours too soon than a minute too late.* – William Shakespeare
|
--- *Better three hours too soon than a minute too late.* - William Shakespeare
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
@@ -36,7 +38,7 @@
|
|||||||
--
|
--
|
||||||
-- # The TIMER Concept
|
-- # The TIMER Concept
|
||||||
--
|
--
|
||||||
-- The TIMER class is the little sister of the SCHEDULER class. It does the same thing but is a bit easier to use and has less overhead. It should be sufficient in many cases.
|
-- The TIMER class is the little sister of the @{Core.Scheduler#SCHEDULER} class. It does the same thing but is a bit easier to use and has less overhead. It should be sufficient in many cases.
|
||||||
--
|
--
|
||||||
-- It provides an easy interface to the DCS [timer.scheduleFunction](https://wiki.hoggitworld.com/view/DCS_func_scheduleFunction).
|
-- It provides an easy interface to the DCS [timer.scheduleFunction](https://wiki.hoggitworld.com/view/DCS_func_scheduleFunction).
|
||||||
--
|
--
|
||||||
@@ -62,20 +64,20 @@
|
|||||||
--
|
--
|
||||||
-- Note that
|
-- Note that
|
||||||
--
|
--
|
||||||
-- * if *Tstart* is not specified (*nil*), the first function call happens immediately.
|
-- * if *Tstart* is not specified (*nil*), the first function call happens immediately, i.e. after one millisecond.
|
||||||
-- * if *dT* is not specified (*nil*), the function is called only once.
|
-- * if *dT* is not specified (*nil*), the function is called only once.
|
||||||
-- * if *Duration* is not specified (*nil*), the timer runs forever or until stopped manually or until the max function calls are reached (see below).
|
-- * if *Duration* is not specified (*nil*), the timer runs forever or until stopped manually or until the max function calls are reached (see below).
|
||||||
--
|
--
|
||||||
-- For example,
|
-- For example,
|
||||||
--
|
--
|
||||||
-- mytimer:Start(3) -- Will call the function once after 3 seconds.
|
-- mytimer:Start(3) -- Will call the function once after 3 seconds.
|
||||||
-- mytimer:Start(nil, 0.5) -- Will call right now and then every 0.5 sec until all eternaty.
|
-- mytimer:Start(nil, 0.5) -- Will call right now and then every 0.5 sec until all eternity.
|
||||||
-- mytimer:Start(nil, 2.0, 20) -- Will call right now and then every 2.0 sec for 20 sec.
|
-- mytimer:Start(nil, 2.0, 20) -- Will call right now and then every 2.0 sec for 20 sec.
|
||||||
-- mytimer:Start(1.0, nil, 10) -- Does not make sense as the function is only called once anyway.
|
-- mytimer:Start(1.0, nil, 10) -- Does not make sense as the function is only called once anyway.
|
||||||
--
|
--
|
||||||
-- ## Stopping the Timer
|
-- ## Stopping the Timer
|
||||||
--
|
--
|
||||||
-- The timer can be stopped manually by the @{#TIMER.Start}(*Delay*) function
|
-- The timer can be stopped manually by the @{#TIMER.Stop}(*Delay*) function
|
||||||
--
|
--
|
||||||
-- mytimer:Stop()
|
-- mytimer:Stop()
|
||||||
--
|
--
|
||||||
@@ -110,7 +112,7 @@ _TIMERID=0
|
|||||||
|
|
||||||
--- TIMER class version.
|
--- TIMER class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
TIMER.version="0.1.0"
|
TIMER.version="0.1.1"
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- TODO list
|
-- TODO list
|
||||||
@@ -142,6 +144,9 @@ function TIMER:New(Function, ...)
|
|||||||
-- Number of function calls.
|
-- Number of function calls.
|
||||||
self.ncalls=0
|
self.ncalls=0
|
||||||
|
|
||||||
|
-- Not running yet.
|
||||||
|
self.isrunning=false
|
||||||
|
|
||||||
-- Increase counter
|
-- Increase counter
|
||||||
_TIMERID=_TIMERID+1
|
_TIMERID=_TIMERID+1
|
||||||
|
|
||||||
@@ -185,6 +190,9 @@ function TIMER:Start(Tstart, dT, Duration)
|
|||||||
-- Set log id.
|
-- Set log id.
|
||||||
self.lid=string.format("TIMER UID=%d/%d | ", self.uid, self.tid)
|
self.lid=string.format("TIMER UID=%d/%d | ", self.uid, self.tid)
|
||||||
|
|
||||||
|
-- Is now running.
|
||||||
|
self.isrunning=true
|
||||||
|
|
||||||
-- Debug info.
|
-- Debug info.
|
||||||
self:T(self.lid..string.format("Starting Timer in %.3f sec, dT=%s, Tstop=%s", self.Tstart-Tnow, tostring(self.dT), tostring(self.Tstop)))
|
self:T(self.lid..string.format("Starting Timer in %.3f sec, dT=%s, Tstop=%s", self.Tstart-Tnow, tostring(self.dT), tostring(self.Tstop)))
|
||||||
|
|
||||||
@@ -209,6 +217,9 @@ function TIMER:Stop(Delay)
|
|||||||
self:T(self.lid..string.format("Stopping timer by removing timer function after %d calls!", self.ncalls))
|
self:T(self.lid..string.format("Stopping timer by removing timer function after %d calls!", self.ncalls))
|
||||||
timer.removeFunction(self.tid)
|
timer.removeFunction(self.tid)
|
||||||
|
|
||||||
|
-- Not running any more.
|
||||||
|
self.isrunning=false
|
||||||
|
|
||||||
-- Remove DB entry.
|
-- Remove DB entry.
|
||||||
--_TIMERDB[self.uid]=nil
|
--_TIMERDB[self.uid]=nil
|
||||||
|
|
||||||
@@ -228,6 +239,13 @@ function TIMER:SetMaxFunctionCalls(Nmax)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Check if the timer has been started and was not stopped.
|
||||||
|
-- @param #TIMER self
|
||||||
|
-- @return #boolean If `true`, the timer is running.
|
||||||
|
function TIMER:IsRunning()
|
||||||
|
return self.isrunning
|
||||||
|
end
|
||||||
|
|
||||||
--- Call timer function.
|
--- Call timer function.
|
||||||
-- @param #TIMER self
|
-- @param #TIMER self
|
||||||
-- @param #number time DCS model time in seconds.
|
-- @param #number time DCS model time in seconds.
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
-- * Get zone properties.
|
-- * Get zone properties.
|
||||||
-- * Get zone bounding box.
|
-- * Get zone bounding box.
|
||||||
-- * Set/get zone name.
|
-- * Set/get zone name.
|
||||||
|
-- * Draw zones (circular and polygon) on the F10 map.
|
||||||
--
|
--
|
||||||
--
|
--
|
||||||
-- There are essentially two core functions that zones accomodate:
|
-- There are essentially two core functions that zones accomodate:
|
||||||
@@ -56,6 +57,8 @@
|
|||||||
--- @type ZONE_BASE
|
--- @type ZONE_BASE
|
||||||
-- @field #string ZoneName Name of the zone.
|
-- @field #string ZoneName Name of the zone.
|
||||||
-- @field #number ZoneProbability A value between 0 and 1. 0 = 0% and 1 = 100% probability.
|
-- @field #number ZoneProbability A value between 0 and 1. 0 = 0% and 1 = 100% probability.
|
||||||
|
-- @field #number DrawID Unique ID of the drawn zone on the F10 map.
|
||||||
|
-- @field #table Color Table with four entries, e.g. {1, 0, 0, 0.15}. First three are RGB color code. Fourth is the transparency Alpha value.
|
||||||
-- @extends Core.Fsm#FSM
|
-- @extends Core.Fsm#FSM
|
||||||
|
|
||||||
|
|
||||||
@@ -104,7 +107,9 @@ ZONE_BASE = {
|
|||||||
ClassName = "ZONE_BASE",
|
ClassName = "ZONE_BASE",
|
||||||
ZoneName = "",
|
ZoneName = "",
|
||||||
ZoneProbability = 1,
|
ZoneProbability = 1,
|
||||||
}
|
DrawID=nil,
|
||||||
|
Color={}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
--- The ZONE_BASE.BoundingSquare
|
--- The ZONE_BASE.BoundingSquare
|
||||||
@@ -178,12 +183,12 @@ function ZONE_BASE:IsCoordinateInZone( Coordinate )
|
|||||||
return InZone
|
return InZone
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns if a PointVec2 is within the zone.
|
--- Returns if a PointVec2 is within the zone. (Name is misleading, actually takes a #COORDINATE)
|
||||||
-- @param #ZONE_BASE self
|
-- @param #ZONE_BASE self
|
||||||
-- @param Core.Point#POINT_VEC2 PointVec2 The PointVec2 to test.
|
-- @param Core.Point#COORDINATE PointVec2 The coordinate to test.
|
||||||
-- @return #boolean true if the PointVec2 is within the zone.
|
-- @return #boolean true if the PointVec2 is within the zone.
|
||||||
function ZONE_BASE:IsPointVec2InZone( PointVec2 )
|
function ZONE_BASE:IsPointVec2InZone( Coordinate )
|
||||||
local InZone = self:IsVec2InZone( PointVec2:GetVec2() )
|
local InZone = self:IsVec2InZone( Coordinate:GetVec2() )
|
||||||
return InZone
|
return InZone
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -221,22 +226,6 @@ function ZONE_BASE:GetPointVec2()
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Returns a @{Core.Point#COORDINATE} of the zone.
|
|
||||||
-- @param #ZONE_BASE self
|
|
||||||
-- @return Core.Point#COORDINATE The Coordinate of the zone.
|
|
||||||
function ZONE_BASE:GetCoordinate()
|
|
||||||
self:F2( self.ZoneName )
|
|
||||||
|
|
||||||
local Vec2 = self:GetVec2()
|
|
||||||
|
|
||||||
local Coordinate = COORDINATE:NewFromVec2( Vec2 )
|
|
||||||
|
|
||||||
self:T2( { Coordinate } )
|
|
||||||
|
|
||||||
return Coordinate
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--- Returns the @{DCS#Vec3} of the zone.
|
--- Returns the @{DCS#Vec3} of the zone.
|
||||||
-- @param #ZONE_BASE self
|
-- @param #ZONE_BASE self
|
||||||
-- @param DCS#Distance Height The height to add to the land height where the center of the zone is located.
|
-- @param DCS#Distance Height The height to add to the land height where the center of the zone is located.
|
||||||
@@ -276,15 +265,27 @@ end
|
|||||||
-- @param DCS#Distance Height The height to add to the land height where the center of the zone is located.
|
-- @param DCS#Distance Height The height to add to the land height where the center of the zone is located.
|
||||||
-- @return Core.Point#COORDINATE The Coordinate of the zone.
|
-- @return Core.Point#COORDINATE The Coordinate of the zone.
|
||||||
function ZONE_BASE:GetCoordinate( Height ) --R2.1
|
function ZONE_BASE:GetCoordinate( Height ) --R2.1
|
||||||
self:F2( self.ZoneName )
|
self:F2(self.ZoneName)
|
||||||
|
|
||||||
local Vec3 = self:GetVec3( Height )
|
local Vec3 = self:GetVec3( Height )
|
||||||
|
|
||||||
local PointVec3 = COORDINATE:NewFromVec3( Vec3 )
|
if self.Coordinate then
|
||||||
|
|
||||||
self:T2( { PointVec3 } )
|
-- Update coordinates.
|
||||||
|
self.Coordinate.x=Vec3.x
|
||||||
|
self.Coordinate.y=Vec3.y
|
||||||
|
self.Coordinate.z=Vec3.z
|
||||||
|
|
||||||
return PointVec3
|
--env.info("FF GetCoordinate NEW for ZONE_BASE "..tostring(self.ZoneName))
|
||||||
|
else
|
||||||
|
|
||||||
|
-- Create a new coordinate object.
|
||||||
|
self.Coordinate=COORDINATE:NewFromVec3(Vec3)
|
||||||
|
|
||||||
|
--env.info("FF GetCoordinate NEW for ZONE_BASE "..tostring(self.ZoneName))
|
||||||
|
end
|
||||||
|
|
||||||
|
return self.Coordinate
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -324,6 +325,76 @@ function ZONE_BASE:BoundZone()
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Set color of zone.
|
||||||
|
-- @param #ZONE_BASE self
|
||||||
|
-- @param #table RGBcolor RGB color table. Default `{1, 0, 0}`.
|
||||||
|
-- @param #number Alpha Transparacy between 0 and 1. Default 0.15.
|
||||||
|
-- @return #ZONE_BASE self
|
||||||
|
function ZONE_BASE:SetColor(RGBcolor, Alpha)
|
||||||
|
|
||||||
|
RGBcolor=RGBcolor or {1, 0, 0}
|
||||||
|
Alpha=Alpha or 0.15
|
||||||
|
|
||||||
|
self.Color={}
|
||||||
|
self.Color[1]=RGBcolor[1]
|
||||||
|
self.Color[2]=RGBcolor[2]
|
||||||
|
self.Color[3]=RGBcolor[3]
|
||||||
|
self.Color[4]=Alpha
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get color table of the zone.
|
||||||
|
-- @param #ZONE_BASE self
|
||||||
|
-- @return #table Table with four entries, e.g. {1, 0, 0, 0.15}. First three are RGB color code. Fourth is the transparency Alpha value.
|
||||||
|
function ZONE_BASE:GetColor()
|
||||||
|
return self.Color
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get RGB color of zone.
|
||||||
|
-- @param #ZONE_BASE self
|
||||||
|
-- @return #table Table with three entries, e.g. {1, 0, 0}, which is the RGB color code.
|
||||||
|
function ZONE_BASE:GetColorRGB()
|
||||||
|
local rgb={}
|
||||||
|
rgb[1]=self.Color[1]
|
||||||
|
rgb[2]=self.Color[2]
|
||||||
|
rgb[3]=self.Color[3]
|
||||||
|
return rgb
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get transperency Alpha value of zone.
|
||||||
|
-- @param #ZONE_BASE self
|
||||||
|
-- @return #number Alpha value.
|
||||||
|
function ZONE_BASE:GetColorAlpha()
|
||||||
|
local alpha=self.Color[4]
|
||||||
|
return alpha
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Remove the drawing of the zone from the F10 map.
|
||||||
|
-- @param #ZONE_BASE self
|
||||||
|
-- @param #number Delay (Optional) Delay before the drawing is removed.
|
||||||
|
-- @return #ZONE_BASE self
|
||||||
|
function ZONE_BASE:UndrawZone(Delay)
|
||||||
|
if Delay and Delay>0 then
|
||||||
|
self:ScheduleOnce(Delay, ZONE_BASE.UndrawZone, self)
|
||||||
|
else
|
||||||
|
if self.DrawID then
|
||||||
|
UTILS.RemoveMark(self.DrawID)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get ID of the zone object drawn on the F10 map.
|
||||||
|
-- The ID can be used to remove the drawn object from the F10 map view via `UTILS.RemoveMark(MarkID)`.
|
||||||
|
-- @param #ZONE_BASE self
|
||||||
|
-- @return #number Unique ID of the
|
||||||
|
function ZONE_BASE:GetDrawID()
|
||||||
|
return self.DrawID
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Smokes the zone boundaries in a color.
|
--- Smokes the zone boundaries in a color.
|
||||||
-- @param #ZONE_BASE self
|
-- @param #ZONE_BASE self
|
||||||
-- @param Utilities.Utils#SMOKECOLOR SmokeColor The smoke color.
|
-- @param Utilities.Utils#SMOKECOLOR SmokeColor The smoke color.
|
||||||
@@ -421,10 +492,14 @@ end
|
|||||||
-- * @{#ZONE_RADIUS.GetRandomPointVec2}(): Gets a @{Core.Point#POINT_VEC2} object representing a random 2D point in the zone.
|
-- * @{#ZONE_RADIUS.GetRandomPointVec2}(): Gets a @{Core.Point#POINT_VEC2} object representing a random 2D point in the zone.
|
||||||
-- * @{#ZONE_RADIUS.GetRandomPointVec3}(): Gets a @{Core.Point#POINT_VEC3} object representing a random 3D point in the zone. Note that the height of the point is at landheight.
|
-- * @{#ZONE_RADIUS.GetRandomPointVec3}(): Gets a @{Core.Point#POINT_VEC3} object representing a random 3D point in the zone. Note that the height of the point is at landheight.
|
||||||
--
|
--
|
||||||
|
-- ## Draw zone
|
||||||
|
--
|
||||||
|
-- * @{#ZONE_RADIUS.DrawZone}(): Draws the zone on the F10 map.
|
||||||
|
--
|
||||||
-- @field #ZONE_RADIUS
|
-- @field #ZONE_RADIUS
|
||||||
ZONE_RADIUS = {
|
ZONE_RADIUS = {
|
||||||
ClassName="ZONE_RADIUS",
|
ClassName="ZONE_RADIUS",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Constructor of @{#ZONE_RADIUS}, taking the zone name, the zone location and a radius.
|
--- Constructor of @{#ZONE_RADIUS}, taking the zone name, the zone location and a radius.
|
||||||
-- @param #ZONE_RADIUS self
|
-- @param #ZONE_RADIUS self
|
||||||
@@ -433,13 +508,52 @@ ZONE_RADIUS = {
|
|||||||
-- @param DCS#Distance Radius The radius of the zone.
|
-- @param DCS#Distance Radius The radius of the zone.
|
||||||
-- @return #ZONE_RADIUS self
|
-- @return #ZONE_RADIUS self
|
||||||
function ZONE_RADIUS:New( ZoneName, Vec2, Radius )
|
function ZONE_RADIUS:New( ZoneName, Vec2, Radius )
|
||||||
local self = BASE:Inherit( self, ZONE_BASE:New( ZoneName ) ) -- #ZONE_RADIUS
|
|
||||||
self:F( { ZoneName, Vec2, Radius } )
|
|
||||||
|
|
||||||
self.Radius = Radius
|
-- Inherit ZONE_BASE.
|
||||||
self.Vec2 = Vec2
|
local self = BASE:Inherit( self, ZONE_BASE:New( ZoneName ) ) -- #ZONE_RADIUS
|
||||||
|
self:F( { ZoneName, Vec2, Radius } )
|
||||||
|
|
||||||
return self
|
self.Radius = Radius
|
||||||
|
self.Vec2 = Vec2
|
||||||
|
|
||||||
|
--self.Coordinate=COORDINATE:NewFromVec2(Vec2)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Update zone from a 2D vector.
|
||||||
|
-- @param #ZONE_RADIUS self
|
||||||
|
-- @param DCS#Vec2 Vec2 The location of the zone.
|
||||||
|
-- @param DCS#Distance Radius The radius of the zone.
|
||||||
|
-- @return #ZONE_RADIUS self
|
||||||
|
function ZONE_RADIUS:UpdateFromVec2(Vec2, Radius)
|
||||||
|
|
||||||
|
-- New center of the zone.
|
||||||
|
self.Vec2=Vec2
|
||||||
|
|
||||||
|
if Radius then
|
||||||
|
self.Radius=Radius
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Update zone from a 2D vector.
|
||||||
|
-- @param #ZONE_RADIUS self
|
||||||
|
-- @param DCS#Vec3 Vec3 The location of the zone.
|
||||||
|
-- @param DCS#Distance Radius The radius of the zone.
|
||||||
|
-- @return #ZONE_RADIUS self
|
||||||
|
function ZONE_RADIUS:UpdateFromVec3(Vec3, Radius)
|
||||||
|
|
||||||
|
-- New center of the zone.
|
||||||
|
self.Vec2.x=Vec3.x
|
||||||
|
self.Vec2.y=Vec3.z
|
||||||
|
|
||||||
|
if Radius then
|
||||||
|
self.Radius=Radius
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Mark the zone with markers on the F10 map.
|
--- Mark the zone with markers on the F10 map.
|
||||||
@@ -469,6 +583,32 @@ function ZONE_RADIUS:MarkZone(Points)
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Draw the zone circle on the F10 map.
|
||||||
|
-- @param #ZONE_RADIUS self
|
||||||
|
-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
||||||
|
-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red.
|
||||||
|
-- @param #number Alpha Transparency [0,1]. Default 1.
|
||||||
|
-- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value.
|
||||||
|
-- @param #number FillAlpha Transparency [0,1]. Default 0.15.
|
||||||
|
-- @param #number LineType Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid.
|
||||||
|
-- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false.
|
||||||
|
-- @return #ZONE_RADIUS self
|
||||||
|
function ZONE_RADIUS:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly)
|
||||||
|
|
||||||
|
local coordinate=self:GetCoordinate()
|
||||||
|
|
||||||
|
local Radius=self:GetRadius()
|
||||||
|
|
||||||
|
Color=Color or self:GetColorRGB()
|
||||||
|
Alpha=Alpha or 1
|
||||||
|
FillColor=FillColor or Color
|
||||||
|
FillAlpha=FillAlpha or self:GetColorAlpha()
|
||||||
|
|
||||||
|
self.DrawID=coordinate:CircleToAll(Radius, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Bounds the zone with tires.
|
--- Bounds the zone with tires.
|
||||||
-- @param #ZONE_RADIUS self
|
-- @param #ZONE_RADIUS self
|
||||||
-- @param #number Points (optional) The amount of points in the circle. Default 360.
|
-- @param #number Points (optional) The amount of points in the circle. Default 360.
|
||||||
@@ -606,11 +746,11 @@ end
|
|||||||
-- @param #ZONE_RADIUS self
|
-- @param #ZONE_RADIUS self
|
||||||
-- @return DCS#Vec2 The location of the zone.
|
-- @return DCS#Vec2 The location of the zone.
|
||||||
function ZONE_RADIUS:GetVec2()
|
function ZONE_RADIUS:GetVec2()
|
||||||
self:F2( self.ZoneName )
|
self:F2( self.ZoneName )
|
||||||
|
|
||||||
self:T2( { self.Vec2 } )
|
self:T2( { self.Vec2 } )
|
||||||
|
|
||||||
return self.Vec2
|
return self.Vec2
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Sets the @{DCS#Vec2} of the zone.
|
--- Sets the @{DCS#Vec2} of the zone.
|
||||||
@@ -781,6 +921,32 @@ function ZONE_RADIUS:GetScannedSetUnit()
|
|||||||
return SetUnit
|
return SetUnit
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get a set of scanned units.
|
||||||
|
-- @param #ZONE_RADIUS self
|
||||||
|
-- @return Core.Set#SET_GROUP Set of groups.
|
||||||
|
function ZONE_RADIUS:GetScannedSetGroup()
|
||||||
|
|
||||||
|
self.ScanSetGroup=self.ScanSetGroup or SET_GROUP:New() --Core.Set#SET_GROUP
|
||||||
|
|
||||||
|
self.ScanSetGroup.Set={}
|
||||||
|
|
||||||
|
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
|
||||||
|
local group=FoundUnit:GetGroup()
|
||||||
|
self.ScanSetGroup:AddGroup(group)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return self.ScanSetGroup
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Count the number of different coalitions inside the zone.
|
--- Count the number of different coalitions inside the zone.
|
||||||
-- @param #ZONE_RADIUS self
|
-- @param #ZONE_RADIUS self
|
||||||
@@ -999,20 +1165,20 @@ end
|
|||||||
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
|
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
|
||||||
-- @return DCS#Vec2 The random location within the zone.
|
-- @return DCS#Vec2 The random location within the zone.
|
||||||
function ZONE_RADIUS:GetRandomVec2( inner, outer )
|
function ZONE_RADIUS:GetRandomVec2( inner, outer )
|
||||||
self:F( self.ZoneName, inner, outer )
|
self:F( self.ZoneName, inner, outer )
|
||||||
|
|
||||||
local Point = {}
|
local Point = {}
|
||||||
local Vec2 = self:GetVec2()
|
local Vec2 = self:GetVec2()
|
||||||
local _inner = inner or 0
|
local _inner = inner or 0
|
||||||
local _outer = outer or self:GetRadius()
|
local _outer = outer or self:GetRadius()
|
||||||
|
|
||||||
local angle = math.random() * math.pi * 2;
|
local angle = math.random() * math.pi * 2;
|
||||||
Point.x = Vec2.x + math.cos( angle ) * math.random(_inner, _outer);
|
Point.x = Vec2.x + math.cos( angle ) * math.random(_inner, _outer);
|
||||||
Point.y = Vec2.y + math.sin( angle ) * math.random(_inner, _outer);
|
Point.y = Vec2.y + math.sin( angle ) * math.random(_inner, _outer);
|
||||||
|
|
||||||
self:T( { Point } )
|
self:T( { Point } )
|
||||||
|
|
||||||
return Point
|
return Point
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone.
|
--- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone.
|
||||||
@@ -1116,22 +1282,37 @@ ZONE = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
--- Constructor of ZONE, taking the zone name.
|
--- Constructor of ZONE taking the zone name.
|
||||||
-- @param #ZONE self
|
-- @param #ZONE self
|
||||||
-- @param #string ZoneName The name of the zone as defined within the mission editor.
|
-- @param #string ZoneName The name of the zone as defined within the mission editor.
|
||||||
-- @return #ZONE
|
-- @return #ZONE self
|
||||||
function ZONE:New( ZoneName )
|
function ZONE:New( ZoneName )
|
||||||
|
|
||||||
|
-- First try to find the zone in the DB.
|
||||||
|
local zone=_DATABASE:FindZone(ZoneName)
|
||||||
|
|
||||||
|
if zone then
|
||||||
|
--env.info("FF found zone in DB")
|
||||||
|
return zone
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get zone from DCS trigger function.
|
||||||
local Zone = trigger.misc.getZone( ZoneName )
|
local Zone = trigger.misc.getZone( ZoneName )
|
||||||
|
|
||||||
|
-- Error!
|
||||||
if not Zone then
|
if not Zone then
|
||||||
error( "Zone " .. ZoneName .. " does not exist." )
|
error( "Zone " .. ZoneName .. " does not exist." )
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local self = BASE:Inherit( self, ZONE_RADIUS:New( ZoneName, { x = Zone.point.x, y = Zone.point.z }, Zone.radius ) )
|
-- Create a new ZONE_RADIUS.
|
||||||
self:F( ZoneName )
|
local self=BASE:Inherit( self, ZONE_RADIUS:New(ZoneName, {x=Zone.point.x, y=Zone.point.z}, Zone.radius))
|
||||||
|
self:F(ZoneName)
|
||||||
|
|
||||||
|
-- Color of zone.
|
||||||
|
self.Color={1, 0, 0, 0.15}
|
||||||
|
|
||||||
|
-- DCS zone.
|
||||||
self.Zone = Zone
|
self.Zone = Zone
|
||||||
|
|
||||||
return self
|
return self
|
||||||
@@ -1392,40 +1573,88 @@ end
|
|||||||
-- * @{#ZONE_POLYGON_BASE.GetRandomPointVec2}(): Return a @{Core.Point#POINT_VEC2} object representing a random 2D point within the zone.
|
-- * @{#ZONE_POLYGON_BASE.GetRandomPointVec2}(): Return a @{Core.Point#POINT_VEC2} object representing a random 2D point within the zone.
|
||||||
-- * @{#ZONE_POLYGON_BASE.GetRandomPointVec3}(): Return a @{Core.Point#POINT_VEC3} object representing a random 3D point at landheight within the zone.
|
-- * @{#ZONE_POLYGON_BASE.GetRandomPointVec3}(): Return a @{Core.Point#POINT_VEC3} object representing a random 3D point at landheight within the zone.
|
||||||
--
|
--
|
||||||
|
-- ## Draw zone
|
||||||
|
--
|
||||||
|
-- * @{#ZONE_POLYGON_BASE.DrawZone}(): Draws the zone on the F10 map.
|
||||||
|
-- * @{#ZONE_POLYGON_BASE.Boundary}(): Draw a frontier on the F10 map with small filled circles.
|
||||||
|
--
|
||||||
|
--
|
||||||
-- @field #ZONE_POLYGON_BASE
|
-- @field #ZONE_POLYGON_BASE
|
||||||
ZONE_POLYGON_BASE = {
|
ZONE_POLYGON_BASE = {
|
||||||
ClassName="ZONE_POLYGON_BASE",
|
ClassName="ZONE_POLYGON_BASE",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- A points array.
|
--- A 2D points array.
|
||||||
-- @type ZONE_POLYGON_BASE.ListVec2
|
-- @type ZONE_POLYGON_BASE.ListVec2
|
||||||
-- @list <DCS#Vec2>
|
-- @list <DCS#Vec2> Table of 2D vectors.
|
||||||
|
|
||||||
|
--- A 3D points array.
|
||||||
|
-- @type ZONE_POLYGON_BASE.ListVec3
|
||||||
|
-- @list <DCS#Vec3> Table of 3D vectors.
|
||||||
|
|
||||||
--- Constructor to create a ZONE_POLYGON_BASE instance, taking the zone name and an array of @{DCS#Vec2}, forming a polygon.
|
--- Constructor to create a ZONE_POLYGON_BASE instance, taking the zone name and an array of @{DCS#Vec2}, forming a polygon.
|
||||||
-- The @{Wrapper.Group#GROUP} waypoints define the polygon corners. The first and the last point are automatically connected.
|
-- The @{Wrapper.Group#GROUP} waypoints define the polygon corners. The first and the last point are automatically connected.
|
||||||
-- @param #ZONE_POLYGON_BASE self
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
-- @param #string ZoneName Name of the zone.
|
-- @param #string ZoneName Name of the zone.
|
||||||
-- @param #ZONE_POLYGON_BASE.ListVec2 PointsArray An array of @{DCS#Vec2}, forming a polygon..
|
-- @param #ZONE_POLYGON_BASE.ListVec2 PointsArray An array of @{DCS#Vec2}, forming a polygon.
|
||||||
-- @return #ZONE_POLYGON_BASE self
|
-- @return #ZONE_POLYGON_BASE self
|
||||||
function ZONE_POLYGON_BASE:New( ZoneName, PointsArray )
|
function ZONE_POLYGON_BASE:New( ZoneName, PointsArray )
|
||||||
|
|
||||||
|
-- Inherit ZONE_BASE.
|
||||||
local self = BASE:Inherit( self, ZONE_BASE:New( ZoneName ) )
|
local self = BASE:Inherit( self, ZONE_BASE:New( ZoneName ) )
|
||||||
self:F( { ZoneName, PointsArray } )
|
self:F( { ZoneName, PointsArray } )
|
||||||
|
|
||||||
local i = 0
|
if PointsArray then
|
||||||
|
|
||||||
|
self._.Polygon = {}
|
||||||
|
|
||||||
|
for i = 1, #PointsArray do
|
||||||
|
self._.Polygon[i] = {}
|
||||||
|
self._.Polygon[i].x = PointsArray[i].x
|
||||||
|
self._.Polygon[i].y = PointsArray[i].y
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Update polygon points with an array of @{DCS#Vec2}.
|
||||||
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
|
-- @param #ZONE_POLYGON_BASE.ListVec2 Vec2Array An array of @{DCS#Vec2}, forming a polygon.
|
||||||
|
-- @return #ZONE_POLYGON_BASE self
|
||||||
|
function ZONE_POLYGON_BASE:UpdateFromVec2(Vec2Array)
|
||||||
|
|
||||||
self._.Polygon = {}
|
self._.Polygon = {}
|
||||||
|
|
||||||
for i = 1, #PointsArray do
|
for i=1,#Vec2Array do
|
||||||
self._.Polygon[i] = {}
|
self._.Polygon[i] = {}
|
||||||
self._.Polygon[i].x = PointsArray[i].x
|
self._.Polygon[i].x=Vec2Array[i].x
|
||||||
self._.Polygon[i].y = PointsArray[i].y
|
self._.Polygon[i].y=Vec2Array[i].y
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Update polygon points with an array of @{DCS#Vec3}.
|
||||||
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
|
-- @param #ZONE_POLYGON_BASE.ListVec3 Vec2Array An array of @{DCS#Vec3}, forming a polygon.
|
||||||
|
-- @return #ZONE_POLYGON_BASE self
|
||||||
|
function ZONE_POLYGON_BASE:UpdateFromVec3(Vec3Array)
|
||||||
|
|
||||||
|
self._.Polygon = {}
|
||||||
|
|
||||||
|
for i=1,#Vec3Array do
|
||||||
|
self._.Polygon[i] = {}
|
||||||
|
self._.Polygon[i].x=Vec3Array[i].x
|
||||||
|
self._.Polygon[i].y=Vec3Array[i].z
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns the center location of the polygon.
|
--- Returns the center location of the polygon.
|
||||||
-- @param #ZONE_GROUP self
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
-- @return DCS#Vec2 The location of the zone based on the @{Wrapper.Group} location.
|
-- @return DCS#Vec2 The location of the zone based on the @{Wrapper.Group} location.
|
||||||
function ZONE_POLYGON_BASE:GetVec2()
|
function ZONE_POLYGON_BASE:GetVec2()
|
||||||
self:F( self.ZoneName )
|
self:F( self.ZoneName )
|
||||||
@@ -1435,6 +1664,78 @@ function ZONE_POLYGON_BASE:GetVec2()
|
|||||||
return { x = ( Bounds.x2 + Bounds.x1 ) / 2, y = ( Bounds.y2 + Bounds.y1 ) / 2 }
|
return { x = ( Bounds.x2 + Bounds.x1 ) / 2, y = ( Bounds.y2 + Bounds.y1 ) / 2 }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get a vertex of the polygon.
|
||||||
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
|
-- @param #number Index Index of the vertex. Default 1.
|
||||||
|
-- @return DCS#Vec2 Vertex of the polygon.
|
||||||
|
function ZONE_POLYGON_BASE:GetVertexVec2(Index)
|
||||||
|
return self._.Polygon[Index or 1]
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get a vertex of the polygon.
|
||||||
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
|
-- @param #number Index Index of the vertex. Default 1.
|
||||||
|
-- @return DCS#Vec3 Vertex of the polygon.
|
||||||
|
function ZONE_POLYGON_BASE:GetVertexVec3(Index)
|
||||||
|
local vec2=self:GetVertexVec2(Index)
|
||||||
|
if vec2 then
|
||||||
|
local vec3={x=vec2.x, y=land.getHeight(vec2), z=vec2.y}
|
||||||
|
return vec3
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get a vertex of the polygon.
|
||||||
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
|
-- @param #number Index Index of the vertex. Default 1.
|
||||||
|
-- @return Core.Point#COORDINATE Vertex of the polygon.
|
||||||
|
function ZONE_POLYGON_BASE:GetVertexCoordinate(Index)
|
||||||
|
local vec2=self:GetVertexVec2(Index)
|
||||||
|
if vec2 then
|
||||||
|
local coord=COORDINATE:NewFromVec2(vec2)
|
||||||
|
return coord
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Get a list of verticies of the polygon.
|
||||||
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
|
-- @return <DCS#Vec2> List of DCS#Vec2 verticies defining the edges of the polygon.
|
||||||
|
function ZONE_POLYGON_BASE:GetVerticiesVec2()
|
||||||
|
return self._.Polygon
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get a list of verticies of the polygon.
|
||||||
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
|
-- @return #table List of DCS#Vec3 verticies defining the edges of the polygon.
|
||||||
|
function ZONE_POLYGON_BASE:GetVerticiesVec3()
|
||||||
|
|
||||||
|
local coords={}
|
||||||
|
|
||||||
|
for i,vec2 in ipairs(self._.Polygon) do
|
||||||
|
local vec3={x=vec2.x, y=land.getHeight(vec2), z=vec2.y}
|
||||||
|
table.insert(coords, vec3)
|
||||||
|
end
|
||||||
|
|
||||||
|
return coords
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get a list of verticies of the polygon.
|
||||||
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
|
-- @return #table List of COORDINATES verticies defining the edges of the polygon.
|
||||||
|
function ZONE_POLYGON_BASE:GetVerticiesCoordinates()
|
||||||
|
|
||||||
|
local coords={}
|
||||||
|
|
||||||
|
for i,vec2 in ipairs(self._.Polygon) do
|
||||||
|
local coord=COORDINATE:NewFromVec2(vec2)
|
||||||
|
table.insert(coords, coord)
|
||||||
|
end
|
||||||
|
|
||||||
|
return coords
|
||||||
|
end
|
||||||
|
|
||||||
--- Flush polygon coordinates as a table in DCS.log.
|
--- Flush polygon coordinates as a table in DCS.log.
|
||||||
-- @param #ZONE_POLYGON_BASE self
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
-- @return #ZONE_POLYGON_BASE self
|
-- @return #ZONE_POLYGON_BASE self
|
||||||
@@ -1494,6 +1795,46 @@ function ZONE_POLYGON_BASE:BoundZone( UnBound )
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Draw the zone on the F10 map. **NOTE** Currently, only polygons with **exactly four points** are supported!
|
||||||
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
|
-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
||||||
|
-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red.
|
||||||
|
-- @param #number Alpha Transparency [0,1]. Default 1.
|
||||||
|
-- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value.
|
||||||
|
-- @param #number FillAlpha Transparency [0,1]. Default 0.15.
|
||||||
|
-- @param #number LineType Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid.
|
||||||
|
-- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false.
|
||||||
|
-- @return #ZONE_POLYGON_BASE self
|
||||||
|
function ZONE_POLYGON_BASE:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly)
|
||||||
|
|
||||||
|
local coordinate=COORDINATE:NewFromVec2(self._.Polygon[1])
|
||||||
|
|
||||||
|
Color=Color or self:GetColorRGB()
|
||||||
|
Alpha=Alpha or 1
|
||||||
|
FillColor=FillColor or Color
|
||||||
|
FillAlpha=FillAlpha or self:GetColorAlpha()
|
||||||
|
|
||||||
|
|
||||||
|
if #self._.Polygon==4 then
|
||||||
|
|
||||||
|
local Coord2=COORDINATE:NewFromVec2(self._.Polygon[2])
|
||||||
|
local Coord3=COORDINATE:NewFromVec2(self._.Polygon[3])
|
||||||
|
local Coord4=COORDINATE:NewFromVec2(self._.Polygon[4])
|
||||||
|
|
||||||
|
self.DrawID=coordinate:QuadToAll(Coord2, Coord3, Coord4, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly)
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
local Coordinates=self:GetVerticiesCoordinates()
|
||||||
|
table.remove(Coordinates, 1)
|
||||||
|
|
||||||
|
self.DrawID=coordinate:MarkupToAllFreeForm(Coordinates, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Smokes the zone boundaries in a color.
|
--- Smokes the zone boundaries in a color.
|
||||||
-- @param #ZONE_POLYGON_BASE self
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
@@ -1685,6 +2026,45 @@ function ZONE_POLYGON_BASE:GetBoundingSquare()
|
|||||||
return { x1 = x1, y1 = y1, x2 = x2, y2 = y2 }
|
return { x1 = x1, y1 = y1, x2 = x2, y2 = y2 }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Draw a frontier on the F10 map with small filled circles.
|
||||||
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
|
-- @param #number Coalition (Optional) Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1= All.
|
||||||
|
-- @param #table Color (Optional) RGB color table {r, g, b}, e.g. {1, 0, 0} for red. Default {1, 1, 1}= White.
|
||||||
|
-- @param #number Radius (Optional) Radius of the circles in meters. Default 1000.
|
||||||
|
-- @param #number Alpha (Optional) Alpha transparency [0,1]. Default 1.
|
||||||
|
-- @param #number Segments (Optional) Number of segments within boundary line. Default 10.
|
||||||
|
-- @param #boolean Closed (Optional) Link the last point with the first one to obtain a closed boundary. Default false
|
||||||
|
-- @return #ZONE_POLYGON_BASE self
|
||||||
|
function ZONE_POLYGON_BASE:Boundary(Coalition, Color, Radius, Alpha, Segments, Closed)
|
||||||
|
Coalition = Coalition or -1
|
||||||
|
Color = Color or {1, 1, 1}
|
||||||
|
Radius = Radius or 1000
|
||||||
|
Alpha = Alpha or 1
|
||||||
|
Segments = Segments or 10
|
||||||
|
Closed = Closed or false
|
||||||
|
local i = 1
|
||||||
|
local j = #self._.Polygon
|
||||||
|
if (Closed) then
|
||||||
|
Limit = #self._.Polygon + 1
|
||||||
|
else
|
||||||
|
Limit = #self._.Polygon
|
||||||
|
end
|
||||||
|
while i <= #self._.Polygon do
|
||||||
|
self:T( { i, j, self._.Polygon[i], self._.Polygon[j] } )
|
||||||
|
if j ~= Limit then
|
||||||
|
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
|
||||||
|
local PointX = self._.Polygon[i].x + ( Segment * DeltaX / Segments )
|
||||||
|
local PointY = self._.Polygon[i].y + ( Segment * DeltaY / Segments )
|
||||||
|
ZONE_RADIUS:New( "Zone", {x = PointX, y = PointY}, Radius ):DrawZone(Coalition, Color, 1, Color, Alpha, nil, true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
j = i
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- @type ZONE_POLYGON
|
--- @type ZONE_POLYGON
|
||||||
-- @extends #ZONE_POLYGON_BASE
|
-- @extends #ZONE_POLYGON_BASE
|
||||||
@@ -1711,7 +2091,7 @@ end
|
|||||||
-- This is especially handy if you want to quickly setup a SET_ZONE...
|
-- This is especially handy if you want to quickly setup a SET_ZONE...
|
||||||
-- So when you would declare `local SetZone = SET_ZONE:New():FilterPrefixes( "Defense" ):FilterStart()`,
|
-- So when you would declare `local SetZone = SET_ZONE:New():FilterPrefixes( "Defense" ):FilterStart()`,
|
||||||
-- then SetZone would contain the ZONE_POLYGON object `DefenseZone` as part of the zone collection,
|
-- then SetZone would contain the ZONE_POLYGON object `DefenseZone` as part of the zone collection,
|
||||||
-- without much scripting overhead!!!
|
-- without much scripting overhead!
|
||||||
--
|
--
|
||||||
-- @field #ZONE_POLYGON
|
-- @field #ZONE_POLYGON
|
||||||
ZONE_POLYGON = {
|
ZONE_POLYGON = {
|
||||||
|
|||||||
@@ -295,6 +295,17 @@ do -- country
|
|||||||
-- @field QATAR
|
-- @field QATAR
|
||||||
-- @field OMAN
|
-- @field OMAN
|
||||||
-- @field UNITED_ARAB_EMIRATES
|
-- @field UNITED_ARAB_EMIRATES
|
||||||
|
-- @field SOUTH_AFRICA
|
||||||
|
-- @field CUBA
|
||||||
|
-- @field PORTUGAL
|
||||||
|
-- @field GDR
|
||||||
|
-- @field LEBANON
|
||||||
|
-- @field CJTF_BLUE
|
||||||
|
-- @field CJTF_RED
|
||||||
|
-- @field UN_PEACEKEEPERS
|
||||||
|
-- @field Argentinia
|
||||||
|
-- @field Cyprus
|
||||||
|
-- @field Slovenia
|
||||||
|
|
||||||
country = {} --#country
|
country = {} --#country
|
||||||
|
|
||||||
@@ -736,7 +747,66 @@ do -- Airbase
|
|||||||
|
|
||||||
end -- Airbase
|
end -- Airbase
|
||||||
|
|
||||||
|
do -- Spot
|
||||||
|
|
||||||
|
--- [DCS Class Spot](https://wiki.hoggitworld.com/view/DCS_Class_Spot)
|
||||||
|
-- Represents a spot from laser or IR-pointer.
|
||||||
|
-- @type Spot
|
||||||
|
-- @field #Spot.Category Category enum that stores spot categories.
|
||||||
|
|
||||||
|
--- Enum that stores spot categories.
|
||||||
|
-- @type Spot.Category
|
||||||
|
-- @field #string INFRA_RED
|
||||||
|
-- @field #string LASER
|
||||||
|
|
||||||
|
|
||||||
|
--- Creates a laser ray emanating from the given object to a point in 3d space.
|
||||||
|
-- @function [parent=#Spot] createLaser
|
||||||
|
-- @param DCS#Object Source The source object of the laser.
|
||||||
|
-- @param DCS#Vec3 LocalRef An optional 3D offset for the source.
|
||||||
|
-- @param DCS#Vec3 Vec3 Target coordinate where the ray is pointing at.
|
||||||
|
-- @param #number LaserCode Any 4 digit number between 1111 and 1788.
|
||||||
|
-- @return #Spot
|
||||||
|
|
||||||
|
--- Creates an infrared ray emanating from the given object to a point in 3d space. Can be seen with night vision goggles.
|
||||||
|
-- @function [parent=#Spot] createInfraRed
|
||||||
|
-- @param DCS#Object Source Source position of the IR ray.
|
||||||
|
-- @param DCS#Vec3 LocalRef An optional 3D offset for the source.
|
||||||
|
-- @param DCS#Vec3 Vec3 Target coordinate where the ray is pointing at.
|
||||||
|
-- @return #Spot
|
||||||
|
|
||||||
|
--- Returns a vec3 table of the x, y, and z coordinates for the position of the given object in 3D space. Coordinates are dependent on the position of the maps origin.
|
||||||
|
-- @function [parent=#Spot] getPoint
|
||||||
|
-- @param #Spot self
|
||||||
|
-- @return DCS#Vec3 Point in 3D, where the beam is pointing at.
|
||||||
|
|
||||||
|
--- Sets the destination point from which the source of the spot is drawn toward.
|
||||||
|
-- @function [parent=#Spot] setPoint
|
||||||
|
-- @param #Spot self
|
||||||
|
-- @param DCS#Vec3 Vec3 Point in 3D, where the beam is pointing at.
|
||||||
|
|
||||||
|
--- Returns the number that is used to define the laser code for which laser designation can track.
|
||||||
|
-- @function [parent=#Spot] getCode
|
||||||
|
-- @param #Spot self
|
||||||
|
-- @return #number Code The laser code used.
|
||||||
|
|
||||||
|
--- Sets the number that is used to define the laser code for which laser designation can track.
|
||||||
|
-- @function [parent=#Spot] setCode
|
||||||
|
-- @param #Spot self
|
||||||
|
-- @param #number Code The laser code. Default value is 1688.
|
||||||
|
|
||||||
|
--- Destroys the spot.
|
||||||
|
-- @function [parent=#Spot] destroy
|
||||||
|
-- @param #Spot self
|
||||||
|
|
||||||
|
--- Gets the category of the spot (laser or IR).
|
||||||
|
-- @function [parent=#Spot] getCategory
|
||||||
|
-- @param #Spot self
|
||||||
|
-- @return #string Category.
|
||||||
|
|
||||||
|
Spot = {} --#Spot
|
||||||
|
|
||||||
|
end -- Spot
|
||||||
|
|
||||||
do -- Controller
|
do -- Controller
|
||||||
--- Controller is an object that performs A.I.-routines. Other words controller is an instance of A.I.. Controller stores current main task, active enroute tasks and behavior options. Controller performs commands. Please, read DCS A-10C GUI Manual EN.pdf chapter "Task Planning for Unit Groups", page 91 to understand A.I. system of DCS:A-10C.
|
--- Controller is an object that performs A.I.-routines. Other words controller is an instance of A.I.. Controller stores current main task, active enroute tasks and behavior options. Controller performs commands. Please, read DCS A-10C GUI Manual EN.pdf chapter "Task Planning for Unit Groups", page 91 to understand A.I. system of DCS:A-10C.
|
||||||
@@ -1093,6 +1163,10 @@ do -- Unit
|
|||||||
-- @param #Unit self
|
-- @param #Unit self
|
||||||
-- @return #Unit.Desc
|
-- @return #Unit.Desc
|
||||||
|
|
||||||
|
--- GROUND - Switch on/off radar emissions
|
||||||
|
-- @function [parent=#Unit] enableEmission
|
||||||
|
-- @param #Unit self
|
||||||
|
-- @param #boolean switch
|
||||||
|
|
||||||
Unit = {} --#Unit
|
Unit = {} --#Unit
|
||||||
|
|
||||||
@@ -1163,7 +1237,7 @@ do -- Group
|
|||||||
-- @param #Group self
|
-- @param #Group self
|
||||||
-- @return #number
|
-- @return #number
|
||||||
|
|
||||||
--- Returns initial size of the group. If some of the units will be destroyed, initial size of the group will not be changed. Initial size limits the unitNumber parameter for Group.getUnit() function.
|
--- Returns initial size of the group. If some of the units will be destroyed, initial size of the group will not be changed; Initial size limits the unitNumber parameter for Group.getUnit() function.
|
||||||
-- @function [parent=#Group] getInitialSize
|
-- @function [parent=#Group] getInitialSize
|
||||||
-- @param #Group self
|
-- @param #Group self
|
||||||
-- @return #number
|
-- @return #number
|
||||||
@@ -1178,6 +1252,11 @@ do -- Group
|
|||||||
-- @param #Group self
|
-- @param #Group self
|
||||||
-- @return #Controller
|
-- @return #Controller
|
||||||
|
|
||||||
|
--- GROUND - Switch on/off radar emissions
|
||||||
|
-- @function [parent=#Group] enableEmission
|
||||||
|
-- @param #Group self
|
||||||
|
-- @param #boolean switch
|
||||||
|
|
||||||
Group = {} --#Group
|
Group = {} --#Group
|
||||||
|
|
||||||
end -- Group
|
end -- Group
|
||||||
|
|||||||
@@ -693,7 +693,7 @@ ARTY.db={
|
|||||||
|
|
||||||
--- Arty script version.
|
--- Arty script version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
ARTY.version="1.1.7"
|
ARTY.version="1.2.0"
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -1146,12 +1146,11 @@ end
|
|||||||
-- @param alias (Optional) Alias name the group will be calling itself when sending messages. Default is the group name.
|
-- @param alias (Optional) Alias name the group will be calling itself when sending messages. Default is the group name.
|
||||||
-- @return #ARTY ARTY object or nil if group does not exist or is not a ground or naval group.
|
-- @return #ARTY ARTY object or nil if group does not exist or is not a ground or naval group.
|
||||||
function ARTY:NewFromCargoGroup(cargogroup, alias)
|
function ARTY:NewFromCargoGroup(cargogroup, alias)
|
||||||
BASE:F2({cargogroup=cargogroup, alias=alias})
|
|
||||||
|
|
||||||
if cargogroup then
|
if cargogroup then
|
||||||
BASE:T(self.lid..string.format("ARTY script version %s. Added CARGO group %s.", ARTY.version, cargogroup:GetName()))
|
BASE:T(string.format("ARTY script version %s. Added CARGO group %s.", ARTY.version, cargogroup:GetName()))
|
||||||
else
|
else
|
||||||
BASE:E(self.lid.."ERROR: Requested ARTY CARGO GROUP does not exist! (Has to be a MOOSE CARGO(!) group.)")
|
BASE:E("ERROR: Requested ARTY CARGO GROUP does not exist! (Has to be a MOOSE CARGO(!) group.)")
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -2766,38 +2765,32 @@ function ARTY:onafterStatus(Controllable, From, Event, To)
|
|||||||
self:_EventFromTo("onafterStatus", Event, From, To)
|
self:_EventFromTo("onafterStatus", Event, From, To)
|
||||||
|
|
||||||
-- Get ammo.
|
-- Get ammo.
|
||||||
local ntot, nshells, nrockets, nmissiles=self:GetAmmo()
|
local nammo, nshells, nrockets, nmissiles=self:GetAmmo()
|
||||||
|
|
||||||
|
-- We have a cargo group ==> check if group was loaded into a carrier.
|
||||||
|
if self.iscargo and self.cargogroup then
|
||||||
|
if self.cargogroup:IsLoaded() and not self:is("InTransit") then
|
||||||
|
-- Group is now InTransit state. Current target is canceled.
|
||||||
|
self:T(self.lid..string.format("Group %s has been loaded into a carrier and is now transported.", self.alias))
|
||||||
|
self:Loaded()
|
||||||
|
elseif self.cargogroup:IsUnLoaded() then
|
||||||
|
-- Group has been unloaded and is combat ready again.
|
||||||
|
self:T(self.lid..string.format("Group %s has been unloaded from the carrier.", self.alias))
|
||||||
|
self:UnLoaded()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- FSM state.
|
-- FSM state.
|
||||||
local fsmstate=self:GetState()
|
local fsmstate=self:GetState()
|
||||||
self:I(self.lid..string.format("Status %s, Ammo total=%d: shells=%d [smoke=%d, illu=%d, nukes=%d*%.3f kT], rockets=%d, missiles=%d", fsmstate, ntot, nshells, self.Nsmoke, self.Nillu, self.Nukes, self.nukewarhead/1000000, nrockets, nmissiles))
|
self:T(self.lid..string.format("Status %s, Ammo total=%d: shells=%d [smoke=%d, illu=%d, nukes=%d*%.3f kT], rockets=%d, missiles=%d", fsmstate, nammo, nshells, self.Nsmoke, self.Nillu, self.Nukes, self.nukewarhead/1000000, nrockets, nmissiles))
|
||||||
|
|
||||||
if self.Controllable and self.Controllable:IsAlive() then
|
if self.Controllable and self.Controllable:IsAlive() then
|
||||||
|
|
||||||
-- We have a cargo group ==> check if group was loaded into a carrier.
|
|
||||||
if self.cargogroup then
|
|
||||||
if self.cargogroup:IsLoaded() and not self:is("InTransit") then
|
|
||||||
-- Group is now InTransit state. Current target is canceled.
|
|
||||||
self:T(self.lid..string.format("Group %s has been loaded into a carrier and is now transported.", self.alias))
|
|
||||||
self:Loaded()
|
|
||||||
elseif self.cargogroup:IsUnLoaded() then
|
|
||||||
-- Group has been unloaded and is combat ready again.
|
|
||||||
self:T(self.lid..string.format("Group %s has been unloaded from the carrier.", self.alias))
|
|
||||||
self:UnLoaded()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Debug current status info.
|
-- Debug current status info.
|
||||||
if self.Debug then
|
if self.Debug then
|
||||||
self:_StatusReport()
|
self:_StatusReport()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Group is being transported as cargo ==> skip everything and check again in 5 seconds.
|
|
||||||
if self:is("InTransit") then
|
|
||||||
self:__Status(-5)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Group on the move.
|
-- Group on the move.
|
||||||
if self:is("Moving") then
|
if self:is("Moving") then
|
||||||
self:T2(self.lid..string.format("%s: Moving", Controllable:GetName()))
|
self:T2(self.lid..string.format("%s: Moving", Controllable:GetName()))
|
||||||
@@ -2884,7 +2877,7 @@ function ARTY:onafterStatus(Controllable, From, Event, To)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Get ammo.
|
-- Get ammo.
|
||||||
local nammo, nshells, nrockets, nmissiles=self:GetAmmo()
|
--local nammo, nshells, nrockets, nmissiles=self:GetAmmo()
|
||||||
|
|
||||||
-- Check if we have a target in the queue for which weapons are still available.
|
-- Check if we have a target in the queue for which weapons are still available.
|
||||||
local gotsome=false
|
local gotsome=false
|
||||||
@@ -2914,9 +2907,23 @@ function ARTY:onafterStatus(Controllable, From, Event, To)
|
|||||||
-- Call status again in ~10 sec.
|
-- Call status again in ~10 sec.
|
||||||
self:__Status(self.StatusInterval)
|
self:__Status(self.StatusInterval)
|
||||||
|
|
||||||
|
elseif self.iscargo then
|
||||||
|
|
||||||
|
-- We have a cargo group ==> check if group was loaded into a carrier.
|
||||||
|
if self.cargogroup and self.cargogroup:IsAlive() then
|
||||||
|
|
||||||
|
-- Group is being transported as cargo ==> skip everything and check again in 5 seconds.
|
||||||
|
if self:is("InTransit") then
|
||||||
|
self:__Status(-5)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
self:E(self.lid..string.format("Arty group %s is not alive!", self.groupname))
|
self:E(self.lid..string.format("Arty group %s is not alive!", self.groupname))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
@@ -4814,7 +4821,8 @@ function ARTY:_CheckShootingStarted()
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Check if we waited long enough and no shot was fired.
|
-- Check if we waited long enough and no shot was fired.
|
||||||
if dt > self.WaitForShotTime and self.Nshots==0 then
|
--if dt > self.WaitForShotTime and self.Nshots==0 then
|
||||||
|
if dt > self.WaitForShotTime and (self.Nshots==0 or self.currentTarget.nshells >= self.Nshots) then --https://github.com/FlightControl-Master/MOOSE/issues/1356
|
||||||
|
|
||||||
-- Debug info.
|
-- Debug info.
|
||||||
self:T(self.lid..string.format("%s, no shot event after %d seconds. Removing current target %s from list.", self.groupname, self.WaitForShotTime, name))
|
self:T(self.lid..string.format("%s, no shot event after %d seconds. Removing current target %s from list.", self.groupname, self.WaitForShotTime, name))
|
||||||
|
|||||||
@@ -1001,7 +1001,13 @@ do -- DESIGNATE
|
|||||||
local ID = self.Detection:GetDetectedItemID( DetectedItem )
|
local ID = self.Detection:GetDetectedItemID( DetectedItem )
|
||||||
local MenuText = ID --.. ", " .. Coord:ToStringA2G( AttackGroup )
|
local MenuText = ID --.. ", " .. Coord:ToStringA2G( AttackGroup )
|
||||||
|
|
||||||
MenuText = string.format( "(%3s) %s", Designating, MenuText )
|
-- Use injected MenuName from TaskA2GDispatcher if using same Detection Object
|
||||||
|
if DetectedItem.DesignateMenuName then
|
||||||
|
MenuText = string.format( "(%3s) %s", Designating, DetectedItem.DesignateMenuName )
|
||||||
|
else
|
||||||
|
MenuText = string.format( "(%3s) %s", Designating, MenuText )
|
||||||
|
end
|
||||||
|
|
||||||
local DetectedMenu = MENU_GROUP_DELAYED:New( AttackGroup, MenuText, MenuDesignate ):SetTime( MenuTime ):SetTag( self.DesignateName )
|
local DetectedMenu = MENU_GROUP_DELAYED:New( AttackGroup, MenuText, MenuDesignate ):SetTime( MenuTime ):SetTag( self.DesignateName )
|
||||||
|
|
||||||
-- Build the Lasing menu.
|
-- Build the Lasing menu.
|
||||||
|
|||||||
@@ -23,7 +23,6 @@
|
|||||||
-- @module Functional.FOX
|
-- @module Functional.FOX
|
||||||
-- @image Functional_FOX.png
|
-- @image Functional_FOX.png
|
||||||
|
|
||||||
|
|
||||||
--- FOX class.
|
--- FOX class.
|
||||||
-- @type FOX
|
-- @type FOX
|
||||||
-- @field #string ClassName Name of the class.
|
-- @field #string ClassName Name of the class.
|
||||||
@@ -48,7 +47,6 @@
|
|||||||
-- @field #number dt05 Time step [sec] for missile position updates if distance to target > 5 km and < 10 km. Default 0.5 sec.
|
-- @field #number dt05 Time step [sec] for missile position updates if distance to target > 5 km and < 10 km. Default 0.5 sec.
|
||||||
-- @field #number dt01 Time step [sec] for missile position updates if distance to target > 1 km and < 5 km. Default 0.1 sec.
|
-- @field #number dt01 Time step [sec] for missile position updates if distance to target > 1 km and < 5 km. Default 0.1 sec.
|
||||||
-- @field #number dt00 Time step [sec] for missile position updates if distance to target < 1 km. Default 0.01 sec.
|
-- @field #number dt00 Time step [sec] for missile position updates if distance to target < 1 km. Default 0.01 sec.
|
||||||
-- @field #boolean
|
|
||||||
-- @extends Core.Fsm#FSM
|
-- @extends Core.Fsm#FSM
|
||||||
|
|
||||||
--- Fox 3!
|
--- Fox 3!
|
||||||
@@ -1707,8 +1705,8 @@ end
|
|||||||
--- Returns the unit of a player and the player name. If the unit does not belong to a player, nil is returned.
|
--- Returns the unit of a player and the player name. If the unit does not belong to a player, nil is returned.
|
||||||
-- @param #FOX self
|
-- @param #FOX self
|
||||||
-- @param DCS#Weapon weapon The weapon.
|
-- @param DCS#Weapon weapon The weapon.
|
||||||
-- @return #number Notching heading right, i.e. missile heading +90<EFBFBD>
|
-- @return #number Notching heading right, i.e. missile heading +90°.
|
||||||
-- @return #number Notching heading left, i.e. missile heading -90<EFBFBD>.
|
-- @return #number Notching heading left, i.e. missile heading -90°.
|
||||||
function FOX:_GetNotchingHeadings(weapon)
|
function FOX:_GetNotchingHeadings(weapon)
|
||||||
|
|
||||||
if weapon then
|
if weapon then
|
||||||
|
|||||||
1376
Moose Development/Moose/Functional/Mantis.lua
Normal file
1376
Moose Development/Moose/Functional/Mantis.lua
Normal file
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,7 @@
|
|||||||
-- ## Features:
|
-- ## Features:
|
||||||
--
|
--
|
||||||
-- * Track the missiles fired at you and other players, providing bearing and range information of the missiles towards the airplanes.
|
-- * 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 <EFBFBD>
|
-- * Provide alerts of missile launches, including detailed information of the units launching, including bearing, range °
|
||||||
-- * Provide alerts when a missile would have killed your aircraft.
|
-- * Provide alerts when a missile would have killed your aircraft.
|
||||||
-- * Provide alerts when the missile self destructs.
|
-- * Provide alerts when the missile self destructs.
|
||||||
-- * Enable / Disable and Configure the Missile Trainer using the various menu options.
|
-- * Enable / Disable and Configure the Missile Trainer using the various menu options.
|
||||||
|
|||||||
@@ -742,7 +742,7 @@ function PSEUDOATC:ReportWeather(GID, UID, position, location)
|
|||||||
local T=position:GetTemperature()
|
local T=position:GetTemperature()
|
||||||
|
|
||||||
-- Correct unit system.
|
-- Correct unit system.
|
||||||
local _T=string.format('%d°F', UTILS.CelciusToFarenheit(T))
|
local _T=string.format('%d°F', UTILS.CelsiusToFahrenheit(T))
|
||||||
if settings:IsMetric() then
|
if settings:IsMetric() then
|
||||||
_T=string.format('%d°C', T)
|
_T=string.format('%d°C', T)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -5371,7 +5371,7 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take
|
|||||||
|
|
||||||
if spawnonground then
|
if spawnonground then
|
||||||
|
|
||||||
-- Sh<EFBFBD>ps and FARPS seem to have a build in queue.
|
-- Sh°ps and FARPS seem to have a build in queue.
|
||||||
if spawnonship or spawnonfarp or spawnonrunway or automatic then
|
if spawnonship or spawnonfarp or spawnonrunway or automatic then
|
||||||
self:T(RAT.id..string.format("RAT group %s spawning at farp, ship or runway %s.", self.alias, departure:GetName()))
|
self:T(RAT.id..string.format("RAT group %s spawning at farp, ship or runway %s.", self.alias, departure:GetName()))
|
||||||
|
|
||||||
@@ -5786,6 +5786,8 @@ end
|
|||||||
-- If desired, the @{#RATMANAGER} can be stopped by the @{#RATMANAGER.Stop}(stoptime) function. The parameter "stoptime" specifies the time delay in seconds after which the manager stops.
|
-- If desired, the @{#RATMANAGER} can be stopped by the @{#RATMANAGER.Stop}(stoptime) function. The parameter "stoptime" specifies the time delay in seconds after which the manager stops.
|
||||||
-- When this happens, no new aircraft will be spawned and the population will eventually decrease to zero.
|
-- When this happens, no new aircraft will be spawned and the population will eventually decrease to zero.
|
||||||
--
|
--
|
||||||
|
-- When you are using a time intervall like @{#RATMANAGER.dTspawn}(delay), @{#RATMANAGER} will ignore the amount set with @{#RATMANAGER.New}(). @{#RATMANAGER.dTspawn}(delay) will spawn infinite groups.
|
||||||
|
--
|
||||||
-- ## Example
|
-- ## Example
|
||||||
-- In this example, three different @{#RAT} objects are created (but not spawned manually). The @{#RATMANAGER} takes care that at least five aircraft of each type are alive and that the total number of aircraft
|
-- In this example, three different @{#RAT} objects are created (but not spawned manually). The @{#RATMANAGER} takes care that at least five aircraft of each type are alive and that the total number of aircraft
|
||||||
-- spawned is 25. The @{#RATMANAGER} is started after 30 seconds and stopped after two hours.
|
-- spawned is 25. The @{#RATMANAGER} is started after 30 seconds and stopped after two hours.
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,8 @@
|
|||||||
--
|
--
|
||||||
-- * 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 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.
|
-- * When SAM sites are being fired upon, the SAMs will take defensive action by shutting down their radars.
|
||||||
|
-- * SEAD calculates the time it takes for a HARM to reach the target - and will attempt to minimize the shut-down time.
|
||||||
|
-- * Detection and evasion of shots has a random component based on the skill level of the SAM groups.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
@@ -15,19 +17,26 @@
|
|||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ### Authors: **FlightControl**
|
-- ### Authors: **FlightControl**, **applevangelist**
|
||||||
|
--
|
||||||
|
-- Last Update: Nov 2021
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- @module Functional.Sead
|
-- @module Functional.Sead
|
||||||
-- @image SEAD.JPG
|
-- @image SEAD.JPG
|
||||||
|
|
||||||
--- @type SEAD
|
---
|
||||||
|
-- @type SEAD
|
||||||
-- @extends Core.Base#BASE
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
--- Make SAM sites execute evasive and defensive behaviour when being fired upon.
|
--- 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.
|
-- 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.
|
||||||
|
-- Once a HARM attack is detected, SEAD will shut down the radars of the attacked SAM site and take evasive action by moving the SAM
|
||||||
|
-- vehicles around (*if* they are drivable, that is). There's a component of randomness in detection and evasion, which is based on the
|
||||||
|
-- skill set of the SAM set (the higher the skill, the more likely). When a missile is fired from far away, the SAM will stay active for a
|
||||||
|
-- period of time to stay defensive, before it takes evasive actions.
|
||||||
--
|
--
|
||||||
-- # Constructor:
|
-- # Constructor:
|
||||||
--
|
--
|
||||||
@@ -37,165 +46,339 @@
|
|||||||
--
|
--
|
||||||
-- @field #SEAD
|
-- @field #SEAD
|
||||||
SEAD = {
|
SEAD = {
|
||||||
ClassName = "SEAD",
|
ClassName = "SEAD",
|
||||||
TargetSkill = {
|
TargetSkill = {
|
||||||
Average = { Evade = 50, DelayOff = { 10, 25 }, DelayOn = { 10, 30 } } ,
|
Average = { Evade = 30, DelayOn = { 40, 60 } } ,
|
||||||
Good = { Evade = 30, DelayOff = { 8, 20 }, DelayOn = { 20, 40 } } ,
|
Good = { Evade = 20, DelayOn = { 30, 50 } } ,
|
||||||
High = { Evade = 15, DelayOff = { 5, 17 }, DelayOn = { 30, 50 } } ,
|
High = { Evade = 15, DelayOn = { 20, 40 } } ,
|
||||||
Excellent = { Evade = 10, DelayOff = { 3, 10 }, DelayOn = { 30, 60 } }
|
Excellent = { Evade = 10, DelayOn = { 10, 30 } }
|
||||||
},
|
},
|
||||||
SEADGroupPrefixes = {}
|
SEADGroupPrefixes = {},
|
||||||
|
SuppressedGroups = {},
|
||||||
|
EngagementRange = 75, -- default 75% engagement range Feature Request #1355
|
||||||
|
Padding = 10,
|
||||||
|
CallBack = nil,
|
||||||
|
UseCallBack = false,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
--- Missile enumerators
|
||||||
|
-- @field Harms
|
||||||
|
SEAD.Harms = {
|
||||||
|
["AGM_88"] = "AGM_88",
|
||||||
|
["AGM_45"] = "AGM_45",
|
||||||
|
["AGM_122"] = "AGM_122",
|
||||||
|
["AGM_84"] = "AGM_84",
|
||||||
|
["AGM_45"] = "AGM_45",
|
||||||
|
["ALARM"] = "ALARM",
|
||||||
|
["LD-10"] = "LD-10",
|
||||||
|
["X_58"] = "X_58",
|
||||||
|
["X_28"] = "X_28",
|
||||||
|
["X_25"] = "X_25",
|
||||||
|
["X_31"] = "X_31",
|
||||||
|
["Kh25"] = "Kh25",
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Missile enumerators - from DCS ME and Wikipedia
|
||||||
|
-- @field HarmData
|
||||||
|
SEAD.HarmData = {
|
||||||
|
-- km and mach
|
||||||
|
["AGM_88"] = { 150, 3},
|
||||||
|
["AGM_45"] = { 12, 2},
|
||||||
|
["AGM_122"] = { 16.5, 2.3},
|
||||||
|
["AGM_84"] = { 280, 0.85},
|
||||||
|
["ALARM"] = { 45, 2},
|
||||||
|
["LD-10"] = { 60, 4},
|
||||||
|
["X_58"] = { 70, 4},
|
||||||
|
["X_28"] = { 80, 2.5},
|
||||||
|
["X_25"] = { 25, 0.76},
|
||||||
|
["X_31"] = {150, 3},
|
||||||
|
["Kh25"] = {25, 0.8},
|
||||||
|
}
|
||||||
|
|
||||||
--- Creates the main object which is handling defensive actions for SA sites or moving SA vehicles.
|
--- Creates the main object which is handling defensive actions for SA sites or moving SA vehicles.
|
||||||
-- When an anti radiation missile is fired (KH-58, KH-31P, KH-31A, KH-25MPU, HARM missiles), the SA will shut down their radars and will take evasive actions...
|
-- When an anti radiation missile is fired (KH-58, KH-31P, KH-31A, KH-25MPU, HARM missiles), the SA will shut down their radars and will take evasive actions...
|
||||||
-- Chances are big that the missile will miss.
|
-- Chances are big that the missile will miss.
|
||||||
-- @param table{string,...}|string SEADGroupPrefixes which is a table of Prefixes of the SA Groups in the DCSRTE on which evasive actions need to be taken.
|
-- @param #SEAD self
|
||||||
-- @return SEAD
|
-- @param #table SEADGroupPrefixes Table of #string entries or single #string, which is a table of Prefixes of the SA Groups in the DCS mission editor on which evasive actions need to be taken.
|
||||||
|
-- @param #number Padding (Optional) Extra number of seconds to add to radar switch-back-on time
|
||||||
|
-- @return #SEAD self
|
||||||
-- @usage
|
-- @usage
|
||||||
-- -- CCCP SEAD Defenses
|
-- -- CCCP SEAD Defenses
|
||||||
-- -- Defends the Russian SA installations from SEAD attacks.
|
-- -- 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' } )
|
-- 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 )
|
function SEAD:New( SEADGroupPrefixes, Padding )
|
||||||
|
|
||||||
local self = BASE:Inherit( self, BASE:New() )
|
local self = BASE:Inherit( self, BASE:New() )
|
||||||
self:F( SEADGroupPrefixes )
|
self:F( SEADGroupPrefixes )
|
||||||
|
|
||||||
if type( SEADGroupPrefixes ) == 'table' then
|
if type( SEADGroupPrefixes ) == 'table' then
|
||||||
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
|
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
|
||||||
self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix
|
self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
self.SEADGroupNames[SEADGroupPrefixes] = SEADGroupPrefixes
|
self.SEADGroupPrefixes[SEADGroupPrefixes] = SEADGroupPrefixes
|
||||||
end
|
end
|
||||||
|
|
||||||
self:HandleEvent( EVENTS.Shot )
|
local padding = Padding or 10
|
||||||
|
if padding < 10 then padding = 10 end
|
||||||
|
self.Padding = padding
|
||||||
|
self.UseEmissionsOnOff = false
|
||||||
|
|
||||||
return self
|
self.CallBack = nil
|
||||||
|
self.UseCallBack = false
|
||||||
|
|
||||||
|
self:HandleEvent( EVENTS.Shot, self.HandleEventShot )
|
||||||
|
|
||||||
|
self:I("*** SEAD - Started Version 0.3.3")
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Detects if an SA site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME.
|
--- Update the active SEAD Set (while running)
|
||||||
-- @see SEAD
|
-- @param #SEAD self
|
||||||
-- @param #SEAD
|
-- @param #table SEADGroupPrefixes The prefixes to add, note: can also be a single #string
|
||||||
|
-- @return #SEAD self
|
||||||
|
function SEAD:UpdateSet( SEADGroupPrefixes )
|
||||||
|
|
||||||
|
self:T( SEADGroupPrefixes )
|
||||||
|
|
||||||
|
if type( SEADGroupPrefixes ) == 'table' then
|
||||||
|
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
|
||||||
|
self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self.SEADGroupPrefixes[SEADGroupPrefixes] = SEADGroupPrefixes
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Sets the engagement range of the SAMs. Defaults to 75% to make it more deadly. Feature Request #1355
|
||||||
|
-- @param #SEAD self
|
||||||
|
-- @param #number range Set the engagement range in percent, e.g. 55 (default 75)
|
||||||
|
-- @return #SEAD self
|
||||||
|
function SEAD:SetEngagementRange(range)
|
||||||
|
self:T( { range } )
|
||||||
|
range = range or 75
|
||||||
|
if range < 0 or range > 100 then
|
||||||
|
range = 75
|
||||||
|
end
|
||||||
|
self.EngagementRange = range
|
||||||
|
self:T(string.format("*** SEAD - Engagement range set to %s",range))
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the padding in seconds, which extends the radar off time calculated by SEAD
|
||||||
|
-- @param #SEAD self
|
||||||
|
-- @param #number Padding Extra number of seconds to add for the switch-on (default 10 seconds)
|
||||||
|
-- @return #SEAD self
|
||||||
|
function SEAD:SetPadding(Padding)
|
||||||
|
self:T( { Padding } )
|
||||||
|
local padding = Padding or 10
|
||||||
|
if padding < 10 then padding = 10 end
|
||||||
|
self.Padding = padding
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set SEAD to use emissions on/off in addition to alarm state.
|
||||||
|
-- @param #SEAD self
|
||||||
|
-- @param #boolean Switch True for on, false for off.
|
||||||
|
-- @return #SEAD self
|
||||||
|
function SEAD:SwitchEmissions(Switch)
|
||||||
|
self:T({Switch})
|
||||||
|
self.UseEmissionsOnOff = Switch
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Add an object to call back when going evasive.
|
||||||
|
-- @param #SEAD self
|
||||||
|
-- @param #table Object The object to call. Needs to have object functions as follows:
|
||||||
|
-- `:SeadSuppressionPlanned(Group, Name, SuppressionStartTime, SuppressionEndTime)`
|
||||||
|
-- `:SeadSuppressionStart(Group, Name)`,
|
||||||
|
-- `:SeadSuppressionEnd(Group, Name)`,
|
||||||
|
-- @return #SEAD self
|
||||||
|
function SEAD:AddCallBack(Object)
|
||||||
|
self:T({Class=Object.ClassName})
|
||||||
|
self.CallBack = Object
|
||||||
|
self.UseCallBack = true
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- (Internal) Check if a known HARM was fired
|
||||||
|
-- @param #SEAD self
|
||||||
|
-- @param #string WeaponName
|
||||||
|
-- @return #boolean Returns true for a match
|
||||||
|
-- @return #string name Name of hit in table
|
||||||
|
function SEAD:_CheckHarms(WeaponName)
|
||||||
|
self:T( { WeaponName } )
|
||||||
|
local hit = false
|
||||||
|
local name = ""
|
||||||
|
for _,_name in pairs (SEAD.Harms) do
|
||||||
|
if string.find(WeaponName,_name,1) then
|
||||||
|
hit = true
|
||||||
|
name = _name
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return hit, name
|
||||||
|
end
|
||||||
|
|
||||||
|
--- (Internal) Return distance in meters between two coordinates or -1 on error.
|
||||||
|
-- @param #SEAD self
|
||||||
|
-- @param Core.Point#COORDINATE _point1 Coordinate one
|
||||||
|
-- @param Core.Point#COORDINATE _point2 Coordinate two
|
||||||
|
-- @return #number Distance in meters
|
||||||
|
function SEAD:_GetDistance(_point1, _point2)
|
||||||
|
self:T("_GetDistance")
|
||||||
|
if _point1 and _point2 then
|
||||||
|
local distance1 = _point1:Get2DDistance(_point2)
|
||||||
|
local distance2 = _point1:DistanceFromPointVec2(_point2)
|
||||||
|
--self:T({dist1=distance1, dist2=distance2})
|
||||||
|
if distance1 and type(distance1) == "number" then
|
||||||
|
return distance1
|
||||||
|
elseif distance2 and type(distance2) == "number" then
|
||||||
|
return distance2
|
||||||
|
else
|
||||||
|
self:E("*****Cannot calculate distance!")
|
||||||
|
self:E({_point1,_point2})
|
||||||
|
return -1
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self:E("******Cannot calculate distance!")
|
||||||
|
self:E({_point1,_point2})
|
||||||
|
return -1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- (Internal) Detects if an SAM site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME.
|
||||||
|
-- @param #SEAD self
|
||||||
-- @param Core.Event#EVENTDATA EventData
|
-- @param Core.Event#EVENTDATA EventData
|
||||||
function SEAD:OnEventShot( EventData )
|
-- @return #SEAD self
|
||||||
self:F( { EventData } )
|
function SEAD:HandleEventShot( EventData )
|
||||||
|
self:T( { EventData.id } )
|
||||||
|
local SEADPlane = EventData.IniUnit -- Wrapper.Unit#UNIT
|
||||||
|
local SEADPlanePos = SEADPlane:GetCoordinate() -- Core.Point#COORDINATE
|
||||||
|
local SEADUnit = EventData.IniDCSUnit
|
||||||
|
local SEADUnitName = EventData.IniDCSUnitName
|
||||||
|
local SEADWeapon = EventData.Weapon -- Identify the weapon fired
|
||||||
|
local SEADWeaponName = EventData.WeaponName -- return weapon type
|
||||||
|
|
||||||
local SEADUnit = EventData.IniDCSUnit
|
self:T( "*** SEAD - Missile Launched = " .. SEADWeaponName)
|
||||||
local SEADUnitName = EventData.IniDCSUnitName
|
--self:T({ SEADWeapon })
|
||||||
local SEADWeapon = EventData.Weapon -- Identify the weapon fired
|
|
||||||
local SEADWeaponName = EventData.WeaponName -- return weapon type
|
|
||||||
-- Start of the 2nd loop
|
|
||||||
self:T( "Missile Launched = " .. SEADWeaponName )
|
|
||||||
|
|
||||||
--if SEADWeaponName == "KH-58" or SEADWeaponName == "KH-25MPU" or SEADWeaponName == "AGM-88" or SEADWeaponName == "KH-31A" or SEADWeaponName == "KH-31P" then -- Check if the missile is a SEAD
|
if self:_CheckHarms(SEADWeaponName) then
|
||||||
if SEADWeaponName == "weapons.missiles.X_58" --Kh-58U anti-radiation missiles fired
|
self:T( '*** SEAD - Weapon Match' )
|
||||||
or
|
local _targetskill = "Random"
|
||||||
SEADWeaponName == "weapons.missiles.Kh25MP_PRGS1VP" --Kh-25MP anti-radiation missiles fired
|
local _targetgroupname = "none"
|
||||||
or
|
local _target = EventData.Weapon:getTarget() -- Identify target
|
||||||
SEADWeaponName == "weapons.missiles.X_25MP" --Kh-25MPU anti-radiation missiles fired
|
local _targetUnit = UNIT:Find(_target) -- Wrapper.Unit#UNIT
|
||||||
or
|
local _targetgroup = nil -- Wrapper.Group#GROUP
|
||||||
SEADWeaponName == "weapons.missiles.X_28" --Kh-28 anti-radiation missiles fired
|
if _targetUnit and _targetUnit:IsAlive() then
|
||||||
or
|
_targetgroup = _targetUnit:GetGroup()
|
||||||
SEADWeaponName == "weapons.missiles.X_31P" --Kh-31P anti-radiation missiles fired
|
_targetgroupname = _targetgroup:GetName() -- group name
|
||||||
or
|
local _targetUnitName = _targetUnit:GetName()
|
||||||
SEADWeaponName == "weapons.missiles.AGM_45A" --AGM-45A anti-radiation missiles fired
|
_targetUnit:GetSkill()
|
||||||
or
|
_targetskill = _targetUnit:GetSkill()
|
||||||
SEADWeaponName == "weapons.missiles.AGM_45" --AGM-45B anti-radiation missiles fired
|
end
|
||||||
or
|
-- see if we are shot at
|
||||||
SEADWeaponName == "weapons.missiles.AGM_88" --AGM-88C anti-radiation missiles fired
|
local SEADGroupFound = false
|
||||||
or
|
for SEADGroupPrefixID, SEADGroupPrefix in pairs( self.SEADGroupPrefixes ) do
|
||||||
SEADWeaponName == "weapons.missiles.AGM_122" --AGM-122 Sidearm anti-radiation missiles fired
|
self:T( _targetgroupname, SEADGroupPrefix )
|
||||||
or
|
if string.find( _targetgroupname, SEADGroupPrefix, 1, true ) then
|
||||||
SEADWeaponName == "weapons.missiles.ALARM" --ALARM anti-radiation missiles fired
|
SEADGroupFound = true
|
||||||
then
|
self:T( '*** SEAD - Group Match Found' )
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if SEADGroupFound == true then -- yes we are being attacked
|
||||||
|
if _targetskill == "Random" then -- when skill is random, choose a skill
|
||||||
|
local Skills = { "Average", "Good", "High", "Excellent" }
|
||||||
|
_targetskill = Skills[ math.random(1,4) ]
|
||||||
|
end
|
||||||
|
--self:T( _targetskill )
|
||||||
|
if self.TargetSkill[_targetskill] then
|
||||||
|
local _evade = math.random (1,100) -- random number for chance of evading action
|
||||||
|
if (_evade > self.TargetSkill[_targetskill].Evade) then
|
||||||
|
self:T("*** SEAD - Evading")
|
||||||
|
-- calculate distance of attacker
|
||||||
|
local _targetpos = _targetgroup:GetCoordinate()
|
||||||
|
local _distance = self:_GetDistance(SEADPlanePos, _targetpos)
|
||||||
|
-- weapon speed
|
||||||
|
local hit, data = self:_CheckHarms(SEADWeaponName)
|
||||||
|
local wpnspeed = 666 -- ;)
|
||||||
|
local reach = 10
|
||||||
|
if hit then
|
||||||
|
local wpndata = SEAD.HarmData[data]
|
||||||
|
reach = wpndata[1] * 1,1
|
||||||
|
local mach = wpndata[2]
|
||||||
|
wpnspeed = math.floor(mach * 340.29)
|
||||||
|
end
|
||||||
|
-- time to impact
|
||||||
|
local _tti = math.floor(_distance / wpnspeed) -- estimated impact time
|
||||||
|
if _distance > 0 then
|
||||||
|
_distance = math.floor(_distance / 1000) -- km
|
||||||
|
else
|
||||||
|
_distance = 0
|
||||||
|
end
|
||||||
|
|
||||||
local _evade = math.random (1,100) -- random number for chance of evading action
|
self:T( string.format("*** SEAD - target skill %s, distance %dkm, reach %dkm, tti %dsec", _targetskill, _distance,reach,_tti ))
|
||||||
local _targetMim = EventData.Weapon:getTarget() -- Identify target
|
|
||||||
local _targetMimname = Unit.getName(_targetMim)
|
|
||||||
local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon))
|
|
||||||
local _targetMimgroupName = _targetMimgroup:getName()
|
|
||||||
local _targetMimcont= _targetMimgroup:getController()
|
|
||||||
local _targetskill = _DATABASE.Templates.Units[_targetMimname].Template.skill
|
|
||||||
self:T( self.SEADGroupPrefixes )
|
|
||||||
self:T( _targetMimgroupName )
|
|
||||||
local SEADGroupFound = false
|
|
||||||
for SEADGroupPrefixID, SEADGroupPrefix in pairs( self.SEADGroupPrefixes ) do
|
|
||||||
if string.find( _targetMimgroupName, SEADGroupPrefix, 1, true ) then
|
|
||||||
SEADGroupFound = true
|
|
||||||
self:T( 'Group Found' )
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if SEADGroupFound == true then
|
|
||||||
if _targetskill == "Random" then -- when skill is random, choose a skill
|
|
||||||
local Skills = { "Average", "Good", "High", "Excellent" }
|
|
||||||
_targetskill = Skills[ math.random(1,4) ]
|
|
||||||
end
|
|
||||||
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)) )
|
if reach >= _distance then
|
||||||
|
self:T("*** SEAD - Shot in Reach")
|
||||||
|
|
||||||
local _targetMim = Weapon.getTarget(SEADWeapon)
|
local function SuppressionStart(args)
|
||||||
local _targetMimname = Unit.getName(_targetMim)
|
self:T(string.format("*** SEAD - %s Radar Off & Relocating",args[2]))
|
||||||
local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon))
|
local grp = args[1] -- Wrapper.Group#GROUP
|
||||||
local _targetMimcont= _targetMimgroup:getController()
|
local name = args[2] -- #string Group Name
|
||||||
|
if self.UseEmissionsOnOff then
|
||||||
|
grp:EnableEmission(false)
|
||||||
|
end
|
||||||
|
grp:OptionAlarmStateGreen() -- needed else we cannot move around
|
||||||
|
grp:RelocateGroundRandomInRadius(20,300,false,false,"Diamond")
|
||||||
|
if self.UseCallBack then
|
||||||
|
local object = self.CallBack
|
||||||
|
object:SeadSuppressionStart(grp,name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
routines.groupRandomDistSelf(_targetMimgroup,300,'Diamond',250,20) -- move randomly
|
local function SuppressionStop(args)
|
||||||
|
self:T(string.format("*** SEAD - %s Radar On",args[2]))
|
||||||
|
local grp = args[1] -- Wrapper.Group#GROUP
|
||||||
|
local name = args[2] -- #string Group Nam
|
||||||
|
if self.UseEmissionsOnOff then
|
||||||
|
grp:EnableEmission(true)
|
||||||
|
end
|
||||||
|
grp:OptionAlarmStateAuto()
|
||||||
|
grp:OptionEngageRange(self.EngagementRange)
|
||||||
|
self.SuppressedGroups[name] = false
|
||||||
|
if self.UseCallBack then
|
||||||
|
local object = self.CallBack
|
||||||
|
object:SeadSuppressionEnd(grp,name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local SuppressedGroups1 = {} -- unit suppressed radar off for a random time
|
-- randomize switch-on time
|
||||||
|
local delay = math.random(self.TargetSkill[_targetskill].DelayOn[1], self.TargetSkill[_targetskill].DelayOn[2])
|
||||||
|
if delay > _tti then delay = delay / 2 end -- speed up
|
||||||
|
if _tti > (3*delay) then delay = (_tti / 2) * 0.9 end -- shot from afar
|
||||||
|
|
||||||
local function SuppressionEnd1(id)
|
local SuppressionStartTime = timer.getTime() + delay
|
||||||
id.ctrl:setOption(AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.GREEN)
|
local SuppressionEndTime = timer.getTime() + _tti + self.Padding
|
||||||
SuppressedGroups1[id.groupName] = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local id = {
|
if not self.SuppressedGroups[_targetgroupname] then
|
||||||
groupName = _targetMimgroup,
|
self:T(string.format("*** SEAD - %s | Parameters TTI %ds | Switch-Off in %ds",_targetgroupname,_tti,delay))
|
||||||
ctrl = _targetMimcont
|
timer.scheduleFunction(SuppressionStart,{_targetgroup,_targetgroupname},SuppressionStartTime)
|
||||||
}
|
timer.scheduleFunction(SuppressionStop,{_targetgroup,_targetgroupname},SuppressionEndTime)
|
||||||
|
self.SuppressedGroups[_targetgroupname] = true
|
||||||
|
if self.UseCallBack then
|
||||||
|
local object = self.CallBack
|
||||||
|
object:SeadSuppressionPlanned(_targetgroup,_targetgroupname,SuppressionStartTime,SuppressionEndTime)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local delay1 = math.random(self.TargetSkill[_targetskill].DelayOff[1], self.TargetSkill[_targetskill].DelayOff[2])
|
end
|
||||||
|
end
|
||||||
if SuppressedGroups1[id.groupName] == nil then
|
end
|
||||||
|
end
|
||||||
SuppressedGroups1[id.groupName] = {
|
end
|
||||||
SuppressionEndTime1 = timer.getTime() + delay1,
|
return self
|
||||||
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
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
548
Moose Development/Moose/Functional/Shorad.lua
Normal file
548
Moose Development/Moose/Functional/Shorad.lua
Normal file
@@ -0,0 +1,548 @@
|
|||||||
|
--- **Functional** -- Short Range Air Defense System
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- **SHORAD** - Short Range Air Defense System
|
||||||
|
-- Controls a network of short range air/missile defense groups.
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ## Missions:
|
||||||
|
--
|
||||||
|
-- ### [SHORAD - Short Range Air Defense](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SRD%20-%20SHORAD%20Defense)
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ### Author : **applevangelist **
|
||||||
|
--
|
||||||
|
-- @module Functional.Shorad
|
||||||
|
-- @image Functional.Shorad.jpg
|
||||||
|
--
|
||||||
|
-- Date: July 2021
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------
|
||||||
|
--- **SHORAD** class, extends Core.Base#BASE
|
||||||
|
-- @type SHORAD
|
||||||
|
-- @field #string ClassName
|
||||||
|
-- @field #string name Name of this Shorad
|
||||||
|
-- @field #boolean debug Set the debug state
|
||||||
|
-- @field #string Prefixes String to be used to build the @{#Core.Set#SET_GROUP}
|
||||||
|
-- @field #number Radius Shorad defense radius in meters
|
||||||
|
-- @field Core.Set#SET_GROUP Groupset The set of Shorad groups
|
||||||
|
-- @field Core.Set#SET_GROUP Samset The set of SAM groups to defend
|
||||||
|
-- @field #string Coalition The coalition of this Shorad
|
||||||
|
-- @field #number ActiveTimer How long a Shorad stays active after wake-up in seconds
|
||||||
|
-- @field #table ActiveGroups Table for the timer function
|
||||||
|
-- @field #string lid The log ID for the dcs.log
|
||||||
|
-- @field #boolean DefendHarms Default true, intercept incoming HARMS
|
||||||
|
-- @field #boolean DefendMavs Default true, intercept incoming AG-Missiles
|
||||||
|
-- @field #number DefenseLowProb Default 70, minimum detection limit
|
||||||
|
-- @field #number DefenseHighProb Default 90, maximim detection limit
|
||||||
|
-- @field #boolean UseEmOnOff Decide if we are using Emission on/off (default) or AlarmState red/green.
|
||||||
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
|
--- *Good friends are worth defending.* Mr Tushman, Wonder (the Movie)
|
||||||
|
--
|
||||||
|
-- Simple Class for a more intelligent Short Range Air Defense System
|
||||||
|
--
|
||||||
|
-- #SHORAD
|
||||||
|
-- Moose derived missile intercepting short range defense system.
|
||||||
|
-- Protects a network of SAM sites. Uses events to switch on the defense groups closest to the enemy.
|
||||||
|
-- Easily integrated with @{Functional.Mantis#MANTIS} to complete the defensive system setup.
|
||||||
|
--
|
||||||
|
-- ## Usage
|
||||||
|
--
|
||||||
|
-- Set up a #SET_GROUP for the SAM sites to be protected:
|
||||||
|
--
|
||||||
|
-- `local SamSet = SET_GROUP:New():FilterPrefixes("Red SAM"):FilterCoalitions("red"):FilterStart()`
|
||||||
|
--
|
||||||
|
-- By default, SHORAD will defense against both HARMs and AG-Missiles with short to medium range. The default defense probability is 70-90%.
|
||||||
|
-- When a missile is detected, SHORAD will activate defense groups in the given radius around the target for 10 minutes. It will *not* react to friendly fire.
|
||||||
|
--
|
||||||
|
-- ### Start a new SHORAD system, parameters are:
|
||||||
|
--
|
||||||
|
-- * Name: Name of this SHORAD.
|
||||||
|
-- * ShoradPrefix: Filter for the Shorad #SET_GROUP.
|
||||||
|
-- * Samset: The #SET_GROUP of SAM sites to defend.
|
||||||
|
-- * Radius: Defense radius in meters.
|
||||||
|
-- * ActiveTimer: Determines how many seconds the systems stay on red alert after wake-up call.
|
||||||
|
-- * Coalition: Coalition, i.e. "blue", "red", or "neutral".*
|
||||||
|
--
|
||||||
|
-- `myshorad = SHORAD:New("RedShorad", "Red SHORAD", SamSet, 25000, 600, "red")`
|
||||||
|
--
|
||||||
|
-- ## Customize options
|
||||||
|
--
|
||||||
|
-- * SHORAD:SwitchDebug(debug)
|
||||||
|
-- * SHORAD:SwitchHARMDefense(onoff)
|
||||||
|
-- * SHORAD:SwitchAGMDefense(onoff)
|
||||||
|
-- * SHORAD:SetDefenseLimits(low,high)
|
||||||
|
-- * SHORAD:SetActiveTimer(seconds)
|
||||||
|
-- * SHORAD:SetDefenseRadius(meters)
|
||||||
|
--
|
||||||
|
-- @field #SHORAD
|
||||||
|
SHORAD = {
|
||||||
|
ClassName = "SHORAD",
|
||||||
|
name = "MyShorad",
|
||||||
|
debug = false,
|
||||||
|
Prefixes = "",
|
||||||
|
Radius = 20000,
|
||||||
|
Groupset = nil,
|
||||||
|
Samset = nil,
|
||||||
|
Coalition = nil,
|
||||||
|
ActiveTimer = 600, --stay on 10 mins
|
||||||
|
ActiveGroups = {},
|
||||||
|
lid = "",
|
||||||
|
DefendHarms = true,
|
||||||
|
DefendMavs = true,
|
||||||
|
DefenseLowProb = 70,
|
||||||
|
DefenseHighProb = 90,
|
||||||
|
UseEmOnOff = false,
|
||||||
|
}
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------
|
||||||
|
-- SHORAD System
|
||||||
|
-----------------------------------------------------------------------
|
||||||
|
|
||||||
|
do
|
||||||
|
-- TODO Complete list?
|
||||||
|
--- Missile enumerators
|
||||||
|
-- @field Harms
|
||||||
|
SHORAD.Harms = {
|
||||||
|
["AGM_88"] = "AGM_88",
|
||||||
|
["AGM_45"] = "AGM_45",
|
||||||
|
["AGM_122"] = "AGM_122",
|
||||||
|
["AGM_84"] = "AGM_84",
|
||||||
|
["AGM_45"] = "AGM_45",
|
||||||
|
["ALARM"] = "ALARM",
|
||||||
|
["LD-10"] = "LD-10",
|
||||||
|
["X_58"] = "X_58",
|
||||||
|
["X_28"] = "X_28",
|
||||||
|
["X_25"] = "X_25",
|
||||||
|
["X_31"] = "X_31",
|
||||||
|
["Kh25"] = "Kh25",
|
||||||
|
}
|
||||||
|
|
||||||
|
--- TODO complete list?
|
||||||
|
-- @field Mavs
|
||||||
|
SHORAD.Mavs = {
|
||||||
|
["AGM"] = "AGM",
|
||||||
|
["C-701"] = "C-701",
|
||||||
|
["Kh25"] = "Kh25",
|
||||||
|
["Kh29"] = "Kh29",
|
||||||
|
["Kh31"] = "Kh31",
|
||||||
|
["Kh66"] = "Kh66",
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Instantiates a new SHORAD object
|
||||||
|
-- @param #SHORAD self
|
||||||
|
-- @param #string Name Name of this SHORAD
|
||||||
|
-- @param #string ShoradPrefix Filter for the Shorad #SET_GROUP
|
||||||
|
-- @param Core.Set#SET_GROUP Samset The #SET_GROUP of SAM sites to defend
|
||||||
|
-- @param #number Radius Defense radius in meters, used to switch on groups
|
||||||
|
-- @param #number ActiveTimer Determines how many seconds the systems stay on red alert after wake-up call
|
||||||
|
-- @param #string Coalition Coalition, i.e. "blue", "red", or "neutral"
|
||||||
|
-- @param #boolean UseEmOnOff Use Emissions On/Off rather than Alarm State Red/Green (default: use Emissions switch)
|
||||||
|
-- @retunr #SHORAD self
|
||||||
|
function SHORAD:New(Name, ShoradPrefix, Samset, Radius, ActiveTimer, Coalition, UseEmOnOff)
|
||||||
|
local self = BASE:Inherit( self, BASE:New() )
|
||||||
|
self:T({Name, ShoradPrefix, Samset, Radius, ActiveTimer, Coalition})
|
||||||
|
|
||||||
|
local GroupSet = SET_GROUP:New():FilterPrefixes(ShoradPrefix):FilterCoalitions(Coalition):FilterCategoryGround():FilterStart()
|
||||||
|
|
||||||
|
self.name = Name or "MyShorad"
|
||||||
|
self.Prefixes = ShoradPrefix or "SAM SHORAD"
|
||||||
|
self.Radius = Radius or 20000
|
||||||
|
self.Coalition = Coalition or "blue"
|
||||||
|
self.Samset = Samset or GroupSet
|
||||||
|
self.ActiveTimer = ActiveTimer or 600
|
||||||
|
self.ActiveGroups = {}
|
||||||
|
self.Groupset = GroupSet
|
||||||
|
self.DefendHarms = true
|
||||||
|
self.DefendMavs = true
|
||||||
|
self.DefenseLowProb = 70 -- probability to detect a missile shot, low margin
|
||||||
|
self.DefenseHighProb = 90 -- probability to detect a missile shot, high margin
|
||||||
|
self.UseEmOnOff = UseEmOnOff or false -- Decide if we are using Emission on/off (default) or AlarmState red/green
|
||||||
|
self:I("*** SHORAD - Started Version 0.2.8")
|
||||||
|
-- Set the string id for output to DCS.log file.
|
||||||
|
self.lid=string.format("SHORAD %s | ", self.name)
|
||||||
|
self:_InitState()
|
||||||
|
self:HandleEvent(EVENTS.Shot, self.HandleEventShot)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Initially set all groups to alarm state GREEN
|
||||||
|
-- @param #SHORAD self
|
||||||
|
function SHORAD:_InitState()
|
||||||
|
self:T(self.lid .. " _InitState")
|
||||||
|
local table = {}
|
||||||
|
local set = self.Groupset
|
||||||
|
self:T({set = set})
|
||||||
|
local aliveset = set:GetAliveSet() --#table
|
||||||
|
for _,_group in pairs (aliveset) do
|
||||||
|
if self.UseEmOnOff then
|
||||||
|
--_group:SetAIOff()
|
||||||
|
_group:EnableEmission(false)
|
||||||
|
_group:OptionAlarmStateRed() --Wrapper.Group#GROUP
|
||||||
|
else
|
||||||
|
_group:OptionAlarmStateGreen() --Wrapper.Group#GROUP
|
||||||
|
end
|
||||||
|
_group:OptionDisperseOnAttack(30)
|
||||||
|
end
|
||||||
|
-- gather entropy
|
||||||
|
for i=1,100 do
|
||||||
|
math.random()
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Switch debug state on
|
||||||
|
-- @param #SHORAD self
|
||||||
|
-- @param #boolean debug Switch debug on (true) or off (false)
|
||||||
|
function SHORAD:SwitchDebug(onoff)
|
||||||
|
self:T( { onoff } )
|
||||||
|
if onoff then
|
||||||
|
self:SwitchDebugOn()
|
||||||
|
else
|
||||||
|
self.SwitchDebugOff()
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Switch debug state on
|
||||||
|
-- @param #SHORAD self
|
||||||
|
function SHORAD:SwitchDebugOn()
|
||||||
|
self.debug = true
|
||||||
|
--tracing
|
||||||
|
BASE:TraceOn()
|
||||||
|
BASE:TraceClass("SHORAD")
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Switch debug state off
|
||||||
|
-- @param #SHORAD self
|
||||||
|
function SHORAD:SwitchDebugOff()
|
||||||
|
self.debug = false
|
||||||
|
BASE:TraceOff()
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Switch defense for HARMs
|
||||||
|
-- @param #SHORAD self
|
||||||
|
-- @param #boolean onoff
|
||||||
|
function SHORAD:SwitchHARMDefense(onoff)
|
||||||
|
self:T( { onoff } )
|
||||||
|
local onoff = onoff or true
|
||||||
|
self.DefendHarms = onoff
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Switch defense for AGMs
|
||||||
|
-- @param #SHORAD self
|
||||||
|
-- @param #boolean onoff
|
||||||
|
function SHORAD:SwitchAGMDefense(onoff)
|
||||||
|
self:T( { onoff } )
|
||||||
|
local onoff = onoff or true
|
||||||
|
self.DefendMavs = onoff
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set defense probability limits
|
||||||
|
-- @param #SHORAD self
|
||||||
|
-- @param #number low Minimum detection limit, integer 1-100
|
||||||
|
-- @param #number high Maximum detection limit integer 1-100
|
||||||
|
function SHORAD:SetDefenseLimits(low,high)
|
||||||
|
self:T( { low, high } )
|
||||||
|
local low = low or 70
|
||||||
|
local high = high or 90
|
||||||
|
if (low < 0) or (low > 100) or (low > high) then
|
||||||
|
low = 70
|
||||||
|
end
|
||||||
|
if (high < 0) or (high > 100) or (high < low ) then
|
||||||
|
high = 90
|
||||||
|
end
|
||||||
|
self.DefenseLowProb = low
|
||||||
|
self.DefenseHighProb = high
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the number of seconds a SHORAD site will stay active
|
||||||
|
-- @param #SHORAD self
|
||||||
|
-- @param #number seconds Number of seconds systems stay active
|
||||||
|
function SHORAD:SetActiveTimer(seconds)
|
||||||
|
self:T(self.lid .. " SetActiveTimer")
|
||||||
|
local timer = seconds or 600
|
||||||
|
if timer < 0 then
|
||||||
|
timer = 600
|
||||||
|
end
|
||||||
|
self.ActiveTimer = timer
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the number of meters for the SHORAD defense zone
|
||||||
|
-- @param #SHORAD self
|
||||||
|
-- @param #number meters Radius of the defense search zone in meters. #SHORADs in this range around a targeted group will go active
|
||||||
|
function SHORAD:SetDefenseRadius(meters)
|
||||||
|
self:T(self.lid .. " SetDefenseRadius")
|
||||||
|
local radius = meters or 20000
|
||||||
|
if radius < 0 then
|
||||||
|
radius = 20000
|
||||||
|
end
|
||||||
|
self.Radius = radius
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set using Emission on/off instead of changing alarm state
|
||||||
|
-- @param #SHORAD self
|
||||||
|
-- @param #boolean switch Decide if we are changing alarm state or AI state
|
||||||
|
function SHORAD:SetUsingEmOnOff(switch)
|
||||||
|
self:T(self.lid .. " SetUsingEmOnOff")
|
||||||
|
self.UseEmOnOff = switch or false
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check if a HARM was fired
|
||||||
|
-- @param #SHORAD self
|
||||||
|
-- @param #string WeaponName
|
||||||
|
-- @return #boolean Returns true for a match
|
||||||
|
function SHORAD:_CheckHarms(WeaponName)
|
||||||
|
self:T(self.lid .. " _CheckHarms")
|
||||||
|
self:T( { WeaponName } )
|
||||||
|
local hit = false
|
||||||
|
if self.DefendHarms then
|
||||||
|
for _,_name in pairs (SHORAD.Harms) do
|
||||||
|
if string.find(WeaponName,_name,1) then hit = true end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return hit
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check if an AGM was fired
|
||||||
|
-- @param #SHORAD self
|
||||||
|
-- @param #string WeaponName
|
||||||
|
-- @return #boolean Returns true for a match
|
||||||
|
function SHORAD:_CheckMavs(WeaponName)
|
||||||
|
self:T(self.lid .. " _CheckMavs")
|
||||||
|
self:T( { WeaponName } )
|
||||||
|
local hit = false
|
||||||
|
if self.DefendMavs then
|
||||||
|
for _,_name in pairs (SHORAD.Mavs) do
|
||||||
|
if string.find(WeaponName,_name,1) then hit = true end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return hit
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check the coalition of the attacker
|
||||||
|
-- @param #SHORAD self
|
||||||
|
-- @param #string Coalition name
|
||||||
|
-- @return #boolean Returns false for a match
|
||||||
|
function SHORAD:_CheckCoalition(Coalition)
|
||||||
|
self:T(self.lid .. " _CheckCoalition")
|
||||||
|
local owncoalition = self.Coalition
|
||||||
|
local othercoalition = ""
|
||||||
|
if Coalition == 0 then
|
||||||
|
othercoalition = "neutral"
|
||||||
|
elseif Coalition == 1 then
|
||||||
|
othercoalition = "red"
|
||||||
|
else
|
||||||
|
othercoalition = "blue"
|
||||||
|
end
|
||||||
|
self:T({owncoalition = owncoalition, othercoalition = othercoalition})
|
||||||
|
if owncoalition ~= othercoalition then
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check if the missile is aimed at a SHORAD
|
||||||
|
-- @param #SHORAD self
|
||||||
|
-- @param #string TargetGroupName Name of the target group
|
||||||
|
-- @return #boolean Returns true for a match, else false
|
||||||
|
function SHORAD:_CheckShotAtShorad(TargetGroupName)
|
||||||
|
self:T(self.lid .. " _CheckShotAtShorad")
|
||||||
|
local tgtgrp = TargetGroupName
|
||||||
|
local shorad = self.Groupset
|
||||||
|
local shoradset = shorad:GetAliveSet() --#table
|
||||||
|
local returnname = false
|
||||||
|
for _,_groups in pairs (shoradset) do
|
||||||
|
local groupname = _groups:GetName()
|
||||||
|
if string.find(groupname, tgtgrp, 1) then
|
||||||
|
returnname = true
|
||||||
|
--_groups:RelocateGroundRandomInRadius(7,100,false,false) -- be a bit evasive
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return returnname
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check if the missile is aimed at a SAM site
|
||||||
|
-- @param #SHORAD self
|
||||||
|
-- @param #string TargetGroupName Name of the target group
|
||||||
|
-- @return #boolean Returns true for a match, else false
|
||||||
|
function SHORAD:_CheckShotAtSams(TargetGroupName)
|
||||||
|
self:T(self.lid .. " _CheckShotAtSams")
|
||||||
|
local tgtgrp = TargetGroupName
|
||||||
|
local shorad = self.Samset
|
||||||
|
--local shoradset = shorad:GetAliveSet() --#table
|
||||||
|
local shoradset = shorad:GetSet() --#table
|
||||||
|
local returnname = false
|
||||||
|
for _,_groups in pairs (shoradset) do
|
||||||
|
local groupname = _groups:GetName()
|
||||||
|
if string.find(groupname, tgtgrp, 1) then
|
||||||
|
returnname = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return returnname
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Calculate if the missile shot is detected
|
||||||
|
-- @param #SHORAD self
|
||||||
|
-- @return #boolean Returns true for a detection, else false
|
||||||
|
function SHORAD:_ShotIsDetected()
|
||||||
|
self:T(self.lid .. " _ShotIsDetected")
|
||||||
|
local IsDetected = false
|
||||||
|
local DetectionProb = math.random(self.DefenseLowProb, self.DefenseHighProb) -- reference value
|
||||||
|
local ActualDetection = math.random(1,100) -- value for this shot
|
||||||
|
if ActualDetection <= DetectionProb then
|
||||||
|
IsDetected = true
|
||||||
|
end
|
||||||
|
return IsDetected
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Wake up #SHORADs in a zone with diameter Radius for ActiveTimer seconds
|
||||||
|
-- @param #SHORAD self
|
||||||
|
-- @param #string TargetGroup Name of the target group used to build the #ZONE
|
||||||
|
-- @param #number Radius Radius of the #ZONE
|
||||||
|
-- @param #number ActiveTimer Number of seconds to stay active
|
||||||
|
-- @param #number TargetCat (optional) Category, i.e. Object.Category.UNIT or Object.Category.STATIC
|
||||||
|
-- @usage Use this function to integrate with other systems, example
|
||||||
|
--
|
||||||
|
-- local SamSet = SET_GROUP:New():FilterPrefixes("Blue SAM"):FilterCoalitions("blue"):FilterStart()
|
||||||
|
-- myshorad = SHORAD:New("BlueShorad", "Blue SHORAD", SamSet, 22000, 600, "blue")
|
||||||
|
-- myshorad:SwitchDebug(true)
|
||||||
|
-- mymantis = MANTIS:New("BlueMantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")
|
||||||
|
-- mymantis:AddShorad(myshorad,720)
|
||||||
|
-- mymantis:Start()
|
||||||
|
function SHORAD:WakeUpShorad(TargetGroup, Radius, ActiveTimer, TargetCat)
|
||||||
|
self:T(self.lid .. " WakeUpShorad")
|
||||||
|
self:T({TargetGroup, Radius, ActiveTimer, TargetCat})
|
||||||
|
local targetcat = TargetCat or Object.Category.UNIT
|
||||||
|
local targetgroup = TargetGroup
|
||||||
|
local targetvec2 = nil
|
||||||
|
if targetcat == Object.Category.UNIT then
|
||||||
|
targetvec2 = GROUP:FindByName(targetgroup):GetVec2()
|
||||||
|
elseif targetcat == Object.Category.STATIC then
|
||||||
|
targetvec2 = STATIC:FindByName(targetgroup,false):GetVec2()
|
||||||
|
else
|
||||||
|
local samset = self.Samset
|
||||||
|
local sam = samset:GetRandom()
|
||||||
|
targetvec2 = sam:GetVec2()
|
||||||
|
end
|
||||||
|
local targetzone = ZONE_RADIUS:New("Shorad",targetvec2,Radius) -- create a defense zone to check
|
||||||
|
local groupset = self.Groupset --Core.Set#SET_GROUP
|
||||||
|
local shoradset = groupset:GetAliveSet() --#table
|
||||||
|
-- local function to switch off shorad again
|
||||||
|
local function SleepShorad(group)
|
||||||
|
local groupname = group:GetName()
|
||||||
|
self.ActiveGroups[groupname] = nil
|
||||||
|
if self.UseEmOnOff then
|
||||||
|
group:EnableEmission(false)
|
||||||
|
--group:SetAIOff()
|
||||||
|
else
|
||||||
|
group:OptionAlarmStateGreen()
|
||||||
|
end
|
||||||
|
local text = string.format("Sleeping SHORAD %s", group:GetName())
|
||||||
|
self:T(text)
|
||||||
|
local m = MESSAGE:New(text,10,"SHORAD"):ToAllIf(self.debug)
|
||||||
|
end
|
||||||
|
-- go through set and find the one(s) to activate
|
||||||
|
for _,_group in pairs (shoradset) do
|
||||||
|
if _group:IsAnyInZone(targetzone) then
|
||||||
|
local text = string.format("Waking up SHORAD %s", _group:GetName())
|
||||||
|
self:T(text)
|
||||||
|
local m = MESSAGE:New(text,10,"SHORAD"):ToAllIf(self.debug)
|
||||||
|
if self.UseEmOnOff then
|
||||||
|
--_group:SetAIOn()
|
||||||
|
_group:EnableEmission(true)
|
||||||
|
end
|
||||||
|
_group:OptionAlarmStateRed()
|
||||||
|
local groupname = _group:GetName()
|
||||||
|
if self.ActiveGroups[groupname] == nil then -- no timer yet for this group
|
||||||
|
self.ActiveGroups[groupname] = { Timing = ActiveTimer }
|
||||||
|
local endtime = timer.getTime() + (ActiveTimer * math.random(75,100) / 100 ) -- randomize wakeup a bit
|
||||||
|
timer.scheduleFunction(SleepShorad, _group, endtime)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Main function - work on the EventData
|
||||||
|
-- @param #SHORAD self
|
||||||
|
-- @param Core.Event#EVENTDATA EventData The event details table data set
|
||||||
|
function SHORAD:HandleEventShot( EventData )
|
||||||
|
self:T( { EventData } )
|
||||||
|
self:T(self.lid .. " HandleEventShot")
|
||||||
|
--local ShootingUnit = EventData.IniDCSUnit
|
||||||
|
--local ShootingUnitName = EventData.IniDCSUnitName
|
||||||
|
local ShootingWeapon = EventData.Weapon -- Identify the weapon fired
|
||||||
|
local ShootingWeaponName = EventData.WeaponName -- return weapon type
|
||||||
|
-- get firing coalition
|
||||||
|
local weaponcoalition = EventData.IniGroup:GetCoalition()
|
||||||
|
-- get detection probability
|
||||||
|
if self:_CheckCoalition(weaponcoalition) then --avoid overhead on friendly fire
|
||||||
|
local IsDetected = self:_ShotIsDetected()
|
||||||
|
-- convert to text
|
||||||
|
local DetectedText = "false"
|
||||||
|
if IsDetected then
|
||||||
|
DetectedText = "true"
|
||||||
|
end
|
||||||
|
local text = string.format("%s Missile Launched = %s | Detected probability state is %s", self.lid, ShootingWeaponName, DetectedText)
|
||||||
|
self:T( text )
|
||||||
|
local m = MESSAGE:New(text,10,"Info"):ToAllIf(self.debug)
|
||||||
|
--
|
||||||
|
if (self:_CheckHarms(ShootingWeaponName) or self:_CheckMavs(ShootingWeaponName)) and IsDetected then
|
||||||
|
-- get target data
|
||||||
|
local targetdata = EventData.Weapon:getTarget() -- Identify target
|
||||||
|
local targetcat = targetdata:getCategory() -- Identify category
|
||||||
|
self:T(string.format("Target Category (3=STATIC, 1=UNIT)= %s",tostring(targetcat)))
|
||||||
|
local targetunit = nil
|
||||||
|
if targetcat == Object.Category.UNIT then -- UNIT
|
||||||
|
targetunit = UNIT:Find(targetdata)
|
||||||
|
elseif targetcat == Object.Category.STATIC then -- STATIC
|
||||||
|
targetunit = STATIC:Find(targetdata)
|
||||||
|
end
|
||||||
|
--local targetunitname = Unit.getName(targetdata) -- Unit name
|
||||||
|
if targetunit and targetunit:IsAlive() then
|
||||||
|
local targetunitname = targetunit:GetName()
|
||||||
|
--local targetgroup = Unit.getGroup(Weapon.getTarget(ShootingWeapon)) --targeted group
|
||||||
|
local targetgroup = nil
|
||||||
|
local targetgroupname = "none"
|
||||||
|
if targetcat == Object.Category.UNIT then
|
||||||
|
targetgroup = targetunit:GetGroup()
|
||||||
|
targetgroupname = targetgroup:GetName() -- group name
|
||||||
|
elseif targetcat == Object.Category.STATIC then
|
||||||
|
targetgroup = targetunit
|
||||||
|
targetgroupname = targetunitname
|
||||||
|
end
|
||||||
|
local text = string.format("%s Missile Target = %s", self.lid, tostring(targetgroupname))
|
||||||
|
self:T( text )
|
||||||
|
local m = MESSAGE:New(text,10,"Info"):ToAllIf(self.debug)
|
||||||
|
-- check if we or a SAM site are the target
|
||||||
|
--local TargetGroup = EventData.TgtGroup -- Wrapper.Group#GROUP
|
||||||
|
local shotatus = self:_CheckShotAtShorad(targetgroupname) --#boolean
|
||||||
|
local shotatsams = self:_CheckShotAtSams(targetgroupname) --#boolean
|
||||||
|
-- if being shot at, find closest SHORADs to activate
|
||||||
|
if shotatsams or shotatus then
|
||||||
|
self:T({shotatsams=shotatsams,shotatus=shotatus})
|
||||||
|
self:WakeUpShorad(targetgroupname, self.Radius, self.ActiveTimer, targetcat)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
--
|
||||||
|
end
|
||||||
|
-----------------------------------------------------------------------
|
||||||
|
-- SHORAD end
|
||||||
|
-----------------------------------------------------------------------
|
||||||
@@ -193,7 +193,13 @@
|
|||||||
-- warehouseBatumi:AddAsset("Huey", 5, WAREHOUSE.Attribute.AIR_TRANSPORTHELO)
|
-- warehouseBatumi:AddAsset("Huey", 5, WAREHOUSE.Attribute.AIR_TRANSPORTHELO)
|
||||||
--
|
--
|
||||||
-- This becomes important when assets are requested from other warehouses as described below. In this case, the five Hueys are now marked as transport helicopters and
|
-- This becomes important when assets are requested from other warehouses as described below. In this case, the five Hueys are now marked as transport helicopters and
|
||||||
-- not attack helicopters.
|
-- not attack helicopters. This is also particularly useful when adding assets to a warehouse with the intention of using them to transport other units that are part of
|
||||||
|
-- a subsequent request (see below). Setting the attribute will help to ensure that warehouse module can find the correct unit when attempting to service a request in its
|
||||||
|
-- queue. For example, if we want to add an Amphibious Landing Ship, even though most are indeed armed, it's recommended to do the following:
|
||||||
|
--
|
||||||
|
-- warehouseBatumi:AddAsset("Landing Ship", 1, WAREHOUSE.Attribute.NAVAL_UNARMEDSHIP)
|
||||||
|
--
|
||||||
|
-- Then when adding the request, you can simply specify WAREHOUSE.TransportType.SHIP (which corresponds to NAVAL_UNARMEDSHIP) as the TransportType.
|
||||||
--
|
--
|
||||||
-- ### Setting the Cargo Bay Weight Limit
|
-- ### Setting the Cargo Bay Weight Limit
|
||||||
-- You can ajust the cargo bay weight limit, in case it is not calculated correctly automatically. For example, the cargo bay of a C-17A is much smaller in DCS than that of a C-130, which is
|
-- You can ajust the cargo bay weight limit, in case it is not calculated correctly automatically. For example, the cargo bay of a C-17A is much smaller in DCS than that of a C-130, which is
|
||||||
@@ -1569,6 +1575,7 @@ WAREHOUSE = {
|
|||||||
delivered = {},
|
delivered = {},
|
||||||
defending = {},
|
defending = {},
|
||||||
portzone = nil,
|
portzone = nil,
|
||||||
|
harborzone = nil,
|
||||||
shippinglanes = {},
|
shippinglanes = {},
|
||||||
offroadpaths = {},
|
offroadpaths = {},
|
||||||
autodefence = false,
|
autodefence = false,
|
||||||
@@ -1594,7 +1601,7 @@ WAREHOUSE = {
|
|||||||
-- @field #number range Range of the unit in meters.
|
-- @field #number range Range of the unit in meters.
|
||||||
-- @field #number speedmax Maximum speed in km/h the group can do.
|
-- @field #number speedmax Maximum speed in km/h the group can do.
|
||||||
-- @field #number size Maximum size in length and with of the asset in meters.
|
-- @field #number size Maximum size in length and with of the asset in meters.
|
||||||
-- @field #number weight The weight of the whole asset group in kilo gramms.
|
-- @field #number weight The weight of the whole asset group in kilograms.
|
||||||
-- @field DCS#Object.Desc DCSdesc All DCS descriptors.
|
-- @field DCS#Object.Desc DCSdesc All DCS descriptors.
|
||||||
-- @field #WAREHOUSE.Attribute attribute Generalized attribute of the group.
|
-- @field #WAREHOUSE.Attribute attribute Generalized attribute of the group.
|
||||||
-- @field #table cargobay Array of cargo bays of all units in an asset group.
|
-- @field #table cargobay Array of cargo bays of all units in an asset group.
|
||||||
@@ -1726,12 +1733,15 @@ WAREHOUSE.Attribute = {
|
|||||||
-- @field #string TRAIN Transports are conducted by trains. Not implemented yet. Also trains are buggy in DCS.
|
-- @field #string TRAIN Transports are conducted by trains. Not implemented yet. Also trains are buggy in DCS.
|
||||||
-- @field #string SELFPROPELLED Assets go to their destination by themselves. No transport carrier needed.
|
-- @field #string SELFPROPELLED Assets go to their destination by themselves. No transport carrier needed.
|
||||||
WAREHOUSE.TransportType = {
|
WAREHOUSE.TransportType = {
|
||||||
AIRPLANE = "Air_TransportPlane",
|
AIRPLANE = "Air_TransportPlane",
|
||||||
HELICOPTER = "Air_TransportHelo",
|
HELICOPTER = "Air_TransportHelo",
|
||||||
APC = "Ground_APC",
|
APC = "Ground_APC",
|
||||||
TRAIN = "Ground_Train",
|
TRAIN = "Ground_Train",
|
||||||
SHIP = "Naval_UnarmedShip",
|
SHIP = "Naval_UnarmedShip",
|
||||||
SELFPROPELLED = "Selfpropelled",
|
AIRCRAFTCARRIER = "Naval_AircraftCarrier",
|
||||||
|
WARSHIP = "Naval_WarShip",
|
||||||
|
ARMEDSHIP = "Naval_ArmedShip",
|
||||||
|
SELFPROPELLED = "Selfpropelled",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Warehouse quantity enumerator for selecting number of assets, e.g. all, half etc. of what is in stock rather than an absolute number.
|
--- Warehouse quantity enumerator for selecting number of assets, e.g. all, half etc. of what is in stock rather than an absolute number.
|
||||||
@@ -1776,7 +1786,7 @@ WAREHOUSE.version="1.0.2"
|
|||||||
-- TODO: Make more examples: ARTY, CAP, ...
|
-- TODO: Make more examples: ARTY, CAP, ...
|
||||||
-- TODO: Check also general requests like all ground. Is this a problem for self propelled if immobile units are among the assets? Check if transport.
|
-- TODO: Check also general requests like all ground. Is this a problem for self propelled if immobile units are among the assets? Check if transport.
|
||||||
-- TODO: Handle the case when units of a group die during the transfer.
|
-- TODO: Handle the case when units of a group die during the transfer.
|
||||||
-- TODO: Added habours as interface for transport to from warehouses? Could make a rudimentary shipping dispatcher.
|
-- DONE: Added harbours as interface for transport to/from warehouses. Simplifies process of spawning units near the ship, especially if cargo not self-propelled.
|
||||||
-- DONE: Test capturing a neutral warehouse.
|
-- DONE: Test capturing a neutral warehouse.
|
||||||
-- DONE: Add save/load capability of warehouse <==> persistance after mission restart. Difficult in lua!
|
-- DONE: Add save/load capability of warehouse <==> persistance after mission restart. Difficult in lua!
|
||||||
-- DONE: Get cargo bay and weight from CARGO_GROUP and GROUP. No necessary any more!
|
-- DONE: Get cargo bay and weight from CARGO_GROUP and GROUP. No necessary any more!
|
||||||
@@ -1829,7 +1839,6 @@ WAREHOUSE.version="1.0.2"
|
|||||||
-- @param #string alias (Optional) Alias of the warehouse, i.e. the name it will be called when sending messages etc. Default is the name of the static
|
-- @param #string alias (Optional) Alias of the warehouse, i.e. the name it will be called when sending messages etc. Default is the name of the static
|
||||||
-- @return #WAREHOUSE self
|
-- @return #WAREHOUSE self
|
||||||
function WAREHOUSE:New(warehouse, alias)
|
function WAREHOUSE:New(warehouse, alias)
|
||||||
BASE:T({warehouse=warehouse})
|
|
||||||
|
|
||||||
-- Check if just a string was given and convert to static.
|
-- Check if just a string was given and convert to static.
|
||||||
if type(warehouse)=="string" then
|
if type(warehouse)=="string" then
|
||||||
@@ -2602,8 +2611,10 @@ end
|
|||||||
--- Check parking ID.
|
--- Check parking ID.
|
||||||
-- @param #WAREHOUSE self
|
-- @param #WAREHOUSE self
|
||||||
-- @param Wrapper.Airbase#AIRBASE.ParkingSpot spot Parking spot.
|
-- @param Wrapper.Airbase#AIRBASE.ParkingSpot spot Parking spot.
|
||||||
|
-- @param Wrapper.Airbase#AIRBASE airbase The airbase.
|
||||||
-- @return #boolean If true, parking is valid.
|
-- @return #boolean If true, parking is valid.
|
||||||
function WAREHOUSE:_CheckParkingValid(spot)
|
function WAREHOUSE:_CheckParkingValid(spot, airbase)
|
||||||
|
|
||||||
if self.parkingIDs==nil then
|
if self.parkingIDs==nil then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@@ -2709,6 +2720,18 @@ function WAREHOUSE:SetPortZone(zone)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Add a Harbor Zone for this warehouse where naval cargo units will spawn and be received.
|
||||||
|
-- Both warehouses must have the harbor zone defined for units to properly spawn on both the
|
||||||
|
-- sending and receiving side. The harbor zone should be within 3km of the port zone used for
|
||||||
|
-- warehouse in order to facilitate the boarding process.
|
||||||
|
-- @param #WAREHOUSE self
|
||||||
|
-- @param Core.Zone#ZONE zone The zone defining the naval embarcation/debarcation point for cargo units
|
||||||
|
-- @return #WAREHOUSE self
|
||||||
|
function WAREHOUSE:SetHarborZone(zone)
|
||||||
|
self.harborzone=zone
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Add a shipping lane from this warehouse to another remote warehouse.
|
--- Add a shipping lane from this warehouse to another remote warehouse.
|
||||||
-- Note that both warehouses must have a port zone defined before a shipping lane can be added!
|
-- Note that both warehouses must have a port zone defined before a shipping lane can be added!
|
||||||
-- Shipping lane is taken from the waypoints of a (late activated) template group. So set up a group, e.g. a ship or a helicopter, and place its
|
-- Shipping lane is taken from the waypoints of a (late activated) template group. So set up a group, e.g. a ship or a helicopter, and place its
|
||||||
@@ -4340,10 +4363,10 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request)
|
|||||||
self:_ErrorMessage("ERROR: Cargo transport by train not supported yet!")
|
self:_ErrorMessage("ERROR: Cargo transport by train not supported yet!")
|
||||||
return
|
return
|
||||||
|
|
||||||
elseif Request.transporttype==WAREHOUSE.TransportType.SHIP then
|
elseif Request.transporttype==WAREHOUSE.TransportType.SHIP or Request.transporttype==WAREHOUSE.TransportType.NAVALCARRIER then
|
||||||
|
|
||||||
self:_ErrorMessage("ERROR: Cargo transport by ship not supported yet!")
|
-- Spawn Ship in port zone
|
||||||
return
|
spawngroup=self:_SpawnAssetGroundNaval(_alias, _assetitem, Request, self.portzone)
|
||||||
|
|
||||||
elseif Request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then
|
elseif Request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then
|
||||||
|
|
||||||
@@ -4471,6 +4494,9 @@ function WAREHOUSE:onafterRequestSpawned(From, Event, To, Request, CargoGroupSet
|
|||||||
--_boardradius=nil
|
--_boardradius=nil
|
||||||
elseif Request.transporttype==WAREHOUSE.TransportType.APC then
|
elseif Request.transporttype==WAREHOUSE.TransportType.APC then
|
||||||
--_boardradius=nil
|
--_boardradius=nil
|
||||||
|
elseif Request.transporttype==WAREHOUSE.TransportType.SHIP or Request.transporttype==WAREHOUSE.TransportType.AIRCRAFTCARRIER
|
||||||
|
or Request.transporttype==WAREHOUSE.TransportType.ARMEDSHIP or Request.transporttype==WAREHOUSE.TransportType.WARSHIP then
|
||||||
|
_boardradius=6000
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Empty cargo group set.
|
-- Empty cargo group set.
|
||||||
@@ -4481,7 +4507,6 @@ function WAREHOUSE:onafterRequestSpawned(From, Event, To, Request, CargoGroupSet
|
|||||||
|
|
||||||
-- Find asset belonging to this group.
|
-- Find asset belonging to this group.
|
||||||
local asset=self:FindAssetInDB(_group)
|
local asset=self:FindAssetInDB(_group)
|
||||||
|
|
||||||
-- New cargo group object.
|
-- New cargo group object.
|
||||||
local cargogroup=CARGO_GROUP:New(_group, _cargotype,_group:GetName(),_boardradius, asset.loadradius)
|
local cargogroup=CARGO_GROUP:New(_group, _cargotype,_group:GetName(),_boardradius, asset.loadradius)
|
||||||
|
|
||||||
@@ -4490,6 +4515,7 @@ function WAREHOUSE:onafterRequestSpawned(From, Event, To, Request, CargoGroupSet
|
|||||||
|
|
||||||
-- Add group to group set.
|
-- Add group to group set.
|
||||||
CargoGroups:AddCargo(cargogroup)
|
CargoGroups:AddCargo(cargogroup)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
------------------------
|
------------------------
|
||||||
@@ -4535,23 +4561,54 @@ function WAREHOUSE:onafterRequestSpawned(From, Event, To, Request, CargoGroupSet
|
|||||||
-- Set home zone.
|
-- Set home zone.
|
||||||
CargoTransport:SetHomeZone(self.spawnzone)
|
CargoTransport:SetHomeZone(self.spawnzone)
|
||||||
|
|
||||||
|
elseif Request.transporttype==WAREHOUSE.TransportType.SHIP or Request.transporttype==WAREHOUSE.TransportType.AIRCRAFTCARRIER
|
||||||
|
or Request.transporttype==WAREHOUSE.TransportType.ARMEDSHIP or Request.transporttype==WAREHOUSE.TransportType.WARSHIP then
|
||||||
|
|
||||||
|
-- Pickup and deploy zones.
|
||||||
|
local PickupZoneSet = SET_ZONE:New():AddZone(self.portzone)
|
||||||
|
PickupZoneSet:AddZone(self.harborzone)
|
||||||
|
local DeployZoneSet = SET_ZONE:New():AddZone(Request.warehouse.harborzone)
|
||||||
|
|
||||||
|
|
||||||
|
-- Get the shipping lane to use and pass it to the Dispatcher
|
||||||
|
local remotename = Request.warehouse.warehouse:GetName()
|
||||||
|
local ShippingLane = self.shippinglanes[remotename][math.random(#self.shippinglanes[remotename])]
|
||||||
|
|
||||||
|
-- Define dispatcher for this task.
|
||||||
|
CargoTransport = AI_CARGO_DISPATCHER_SHIP:New(TransportGroupSet, CargoGroups, PickupZoneSet, DeployZoneSet, ShippingLane)
|
||||||
|
|
||||||
|
-- Set home zone
|
||||||
|
CargoTransport:SetHomeZone(self.portzone)
|
||||||
|
|
||||||
else
|
else
|
||||||
self:E(self.lid.."ERROR: Unknown transporttype!")
|
self:E(self.lid.."ERROR: Unknown transporttype!")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Set pickup and deploy radii.
|
-- Set pickup and deploy radii.
|
||||||
-- The 20 m inner radius are to ensure that the helo does not land on the warehouse itself in the middle of the default spawn zone.
|
-- The 20 m inner radius are to ensure that the helo does not land on the warehouse itself in the middle of the default spawn zone.
|
||||||
local pickupouter=200
|
local pickupouter = 200
|
||||||
local pickupinner=0
|
local pickupinner = 0
|
||||||
if self.spawnzone.Radius~=nil then
|
local deployouter = 200
|
||||||
pickupouter=self.spawnzone.Radius
|
local deployinner = 0
|
||||||
|
if Request.transporttype==WAREHOUSE.TransportType.SHIP or Request.transporttype==WAREHOUSE.TransportType.AIRCRAFTCARRIER
|
||||||
|
or Request.transporttype==WAREHOUSE.TransportType.ARMEDSHIP or Request.transporttype==WAREHOUSE.TransportType.WARSHIP then
|
||||||
|
pickupouter=1000
|
||||||
pickupinner=20
|
pickupinner=20
|
||||||
end
|
deployouter=1000
|
||||||
local deployouter=200
|
deployinner=0
|
||||||
local deployinner=0
|
else
|
||||||
if self.spawnzone.Radius~=nil then
|
pickupouter=200
|
||||||
deployouter=Request.warehouse.spawnzone.Radius
|
pickupinner=0
|
||||||
deployinner=20
|
if self.spawnzone.Radius~=nil then
|
||||||
|
pickupouter=self.spawnzone.Radius
|
||||||
|
pickupinner=20
|
||||||
|
end
|
||||||
|
deployouter=200
|
||||||
|
deployinner=0
|
||||||
|
if self.spawnzone.Radius~=nil then
|
||||||
|
deployouter=Request.warehouse.spawnzone.Radius
|
||||||
|
deployinner=20
|
||||||
|
end
|
||||||
end
|
end
|
||||||
CargoTransport:SetPickupRadius(pickupouter, pickupinner)
|
CargoTransport:SetPickupRadius(pickupouter, pickupinner)
|
||||||
CargoTransport:SetDeployRadius(deployouter, deployinner)
|
CargoTransport:SetDeployRadius(deployouter, deployinner)
|
||||||
@@ -4630,7 +4687,7 @@ function WAREHOUSE:onafterRequestSpawned(From, Event, To, Request, CargoGroupSet
|
|||||||
-- Get cargo group object.
|
-- Get cargo group object.
|
||||||
local group=Cargo:GetObject() --Wrapper.Group#GROUP
|
local group=Cargo:GetObject() --Wrapper.Group#GROUP
|
||||||
|
|
||||||
-- Get request.
|
-- Get request.
|
||||||
local request=warehouse:_GetRequestOfGroup(group, warehouse.pending)
|
local request=warehouse:_GetRequestOfGroup(group, warehouse.pending)
|
||||||
|
|
||||||
-- Add cargo group to this carrier.
|
-- Add cargo group to this carrier.
|
||||||
@@ -6925,11 +6982,16 @@ function WAREHOUSE:_CheckRequestValid(request)
|
|||||||
valid=false
|
valid=false
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif request.transporttype==WAREHOUSE.TransportType.SHIP then
|
elseif request.transporttype==WAREHOUSE.TransportType.SHIP or request.transporttype==WAREHOUSE.TransportType.AIRCRAFTCARRIER
|
||||||
|
or request.transporttype==WAREHOUSE.TransportType.ARMEDSHIP or request.transporttype==WAREHOUSE.TransportType.WARSHIP then
|
||||||
|
|
||||||
-- Transport by ship.
|
-- Transport by ship.
|
||||||
self:E("ERROR: Incorrect request. Transport by SHIP not implemented yet!")
|
local shippinglane=self:HasConnectionNaval(request.warehouse)
|
||||||
valid=false
|
|
||||||
|
if not shippinglane then
|
||||||
|
self:E("ERROR: Incorrect request. No shipping lane has been defined between warehouses!")
|
||||||
|
valid=false
|
||||||
|
end
|
||||||
|
|
||||||
elseif request.transporttype==WAREHOUSE.TransportType.TRAIN then
|
elseif request.transporttype==WAREHOUSE.TransportType.TRAIN then
|
||||||
|
|
||||||
@@ -7025,7 +7087,7 @@ function WAREHOUSE:_CheckRequestNow(request)
|
|||||||
|
|
||||||
-- If no transport is requested, assets need to be mobile unless it is a self request.
|
-- If no transport is requested, assets need to be mobile unless it is a self request.
|
||||||
local onlymobile=false
|
local onlymobile=false
|
||||||
if type(request.transport)=="number" and request.ntransport==0 and not request.toself then
|
if type(request.ntransport)=="number" and request.ntransport==0 and not request.toself then
|
||||||
onlymobile=true
|
onlymobile=true
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -7607,7 +7669,7 @@ function WAREHOUSE:_FindParkingForAssets(airbase, assets)
|
|||||||
local parkingspot=_parkingspot --Wrapper.Airbase#AIRBASE.ParkingSpot
|
local parkingspot=_parkingspot --Wrapper.Airbase#AIRBASE.ParkingSpot
|
||||||
|
|
||||||
-- Check correct terminal type for asset. We don't want helos in shelters etc.
|
-- Check correct terminal type for asset. We don't want helos in shelters etc.
|
||||||
if AIRBASE._CheckTerminalType(parkingspot.TerminalType, terminaltype) and self:_CheckParkingValid(parkingspot) then
|
if AIRBASE._CheckTerminalType(parkingspot.TerminalType, terminaltype) and self:_CheckParkingValid(parkingspot, airbase) and airbase:_CheckParkingLists(parkingspot.TerminalID) then
|
||||||
|
|
||||||
-- Coordinate of the parking spot.
|
-- Coordinate of the parking spot.
|
||||||
local _spot=parkingspot.Coordinate -- Core.Point#COORDINATE
|
local _spot=parkingspot.Coordinate -- Core.Point#COORDINATE
|
||||||
@@ -8052,10 +8114,9 @@ function WAREHOUSE:_GetAttribute(group)
|
|||||||
-- Ships
|
-- Ships
|
||||||
local aircraftcarrier=group:HasAttribute("Aircraft Carriers")
|
local aircraftcarrier=group:HasAttribute("Aircraft Carriers")
|
||||||
local warship=group:HasAttribute("Heavy armed ships")
|
local warship=group:HasAttribute("Heavy armed ships")
|
||||||
local armedship=group:HasAttribute("Armed ships")
|
local armedship=group:HasAttribute("Armed ships") or group:HasAttribute("Armed Ship")
|
||||||
local unarmedship=group:HasAttribute("Unarmed ships")
|
local unarmedship=group:HasAttribute("Unarmed ships")
|
||||||
|
|
||||||
|
|
||||||
-- Define attribute. Order is important.
|
-- Define attribute. Order is important.
|
||||||
if transportplane then
|
if transportplane then
|
||||||
attribute=WAREHOUSE.Attribute.AIR_TRANSPORTPLANE
|
attribute=WAREHOUSE.Attribute.AIR_TRANSPORTPLANE
|
||||||
|
|||||||
@@ -715,20 +715,21 @@ do -- ZONE_CAPTURE_COALITION
|
|||||||
|
|
||||||
local UnitHit = EventData.TgtUnit
|
local UnitHit = EventData.TgtUnit
|
||||||
|
|
||||||
|
if UnitHit.ClassName ~= "SCENERY" then
|
||||||
-- Check if unit is inside the capture zone and that it is of the defending coalition.
|
-- 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
|
if UnitHit and UnitHit:IsInZone(self) and UnitHit:GetCoalition()==self.Coalition then
|
||||||
|
|
||||||
-- Update last hit time.
|
-- Update last hit time.
|
||||||
self.HitTimeLast=timer.getTime()
|
self.HitTimeLast=timer.getTime()
|
||||||
|
|
||||||
-- Only trigger attacked event if not already in state "Attacked".
|
-- Only trigger attacked event if not already in state "Attacked".
|
||||||
if self:GetState()~="Attacked" then
|
if self:GetState()~="Attacked" then
|
||||||
self:F2("Hit ==> Attack")
|
self:F2("Hit ==> Attack")
|
||||||
self:Attack()
|
self:Attack()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -890,12 +891,14 @@ do -- ZONE_CAPTURE_COALITION
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Status text.
|
-- 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)
|
if false then
|
||||||
local NewState = self:GetState()
|
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)
|
||||||
if NewState~=State then
|
local NewState = self:GetState()
|
||||||
text=text..string.format(" --> %s", NewState)
|
if NewState~=State then
|
||||||
|
text=text..string.format(" --> %s", NewState)
|
||||||
|
end
|
||||||
|
self:I(text)
|
||||||
end
|
end
|
||||||
self:I(text)
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
-- The order of the declarations is important here. Don't touch it.
|
--- GLOBALS: The order of the declarations is important here. Don't touch it.
|
||||||
|
|
||||||
|
|
||||||
--- Declare the event dispatcher based on the EVENT class
|
--- Declare the event dispatcher based on the EVENT class
|
||||||
_EVENTDISPATCHER = EVENT:New() -- Core.Event#EVENT
|
_EVENTDISPATCHER = EVENT:New() -- Core.Event#EVENT
|
||||||
@@ -10,9 +9,38 @@ _SCHEDULEDISPATCHER = SCHEDULEDISPATCHER:New() -- Core.ScheduleDispatcher#SCHEDU
|
|||||||
--- Declare the main database object, which is used internally by the MOOSE classes.
|
--- Declare the main database object, which is used internally by the MOOSE classes.
|
||||||
_DATABASE = DATABASE:New() -- Core.Database#DATABASE
|
_DATABASE = DATABASE:New() -- Core.Database#DATABASE
|
||||||
|
|
||||||
|
--- Settings
|
||||||
_SETTINGS = SETTINGS:Set()
|
_SETTINGS = SETTINGS:Set()
|
||||||
_SETTINGS:SetPlayerMenuOn()
|
_SETTINGS:SetPlayerMenuOn()
|
||||||
|
|
||||||
|
--- Register cargos.
|
||||||
_DATABASE:_RegisterCargos()
|
_DATABASE:_RegisterCargos()
|
||||||
_DATABASE:_RegisterZones()
|
|
||||||
|
|
||||||
|
--- Register zones.
|
||||||
|
_DATABASE:_RegisterZones()
|
||||||
|
_DATABASE:_RegisterAirbases()
|
||||||
|
|
||||||
|
--- Check if os etc is available.
|
||||||
|
BASE:I("Checking de-sanitization of os, io and lfs:")
|
||||||
|
local __na=false
|
||||||
|
if os then
|
||||||
|
BASE:I("- os available")
|
||||||
|
else
|
||||||
|
BASE:I("- os NOT available! Some functions may not work.")
|
||||||
|
__na=true
|
||||||
|
end
|
||||||
|
if io then
|
||||||
|
BASE:I("- io available")
|
||||||
|
else
|
||||||
|
BASE:I("- io NOT available! Some functions may not work.")
|
||||||
|
__na=true
|
||||||
|
end
|
||||||
|
if lfs then
|
||||||
|
BASE:I("- lfs available")
|
||||||
|
else
|
||||||
|
BASE:I("- lfs NOT available! Some functions may not work.")
|
||||||
|
__na=true
|
||||||
|
end
|
||||||
|
if __na then
|
||||||
|
BASE:I("Check <DCS install folder>/Scripts/MissionScripting.lua and comment out the lines with sanitizeModule(''). Use at your own risk!)")
|
||||||
|
end
|
||||||
|
|||||||
@@ -2,10 +2,12 @@ __Moose.Include( 'Scripts/Moose/Utilities/Enums.lua' )
|
|||||||
__Moose.Include( 'Scripts/Moose/Utilities/Routines.lua' )
|
__Moose.Include( 'Scripts/Moose/Utilities/Routines.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Utilities/Utils.lua' )
|
__Moose.Include( 'Scripts/Moose/Utilities/Utils.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Utilities/Profiler.lua' )
|
__Moose.Include( 'Scripts/Moose/Utilities/Profiler.lua' )
|
||||||
|
__Moose.Include( 'Scripts/Moose/Utilities/Templates.lua' )
|
||||||
|
__Moose.Include( 'Scripts/Moose/Utilities/STTS.lua' )
|
||||||
|
|
||||||
__Moose.Include( 'Scripts/Moose/Core/Base.lua' )
|
__Moose.Include( 'Scripts/Moose/Core/Base.lua' )
|
||||||
|
__Moose.Include( 'Scripts/Moose/Core/Beacon.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Core/UserFlag.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/Report.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Core/Scheduler.lua' )
|
__Moose.Include( 'Scripts/Moose/Core/Scheduler.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Core/ScheduleDispatcher.lua' )
|
__Moose.Include( 'Scripts/Moose/Core/ScheduleDispatcher.lua' )
|
||||||
@@ -20,9 +22,6 @@ __Moose.Include( 'Scripts/Moose/Core/Point.lua' )
|
|||||||
__Moose.Include( 'Scripts/Moose/Core/Velocity.lua' )
|
__Moose.Include( 'Scripts/Moose/Core/Velocity.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Core/Message.lua' )
|
__Moose.Include( 'Scripts/Moose/Core/Message.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Core/Fsm.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/Spawn.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Core/SpawnStatic.lua' )
|
__Moose.Include( 'Scripts/Moose/Core/SpawnStatic.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Core/Timer.lua' )
|
__Moose.Include( 'Scripts/Moose/Core/Timer.lua' )
|
||||||
@@ -67,11 +66,15 @@ __Moose.Include( 'Scripts/Moose/Functional/Suppression.lua' )
|
|||||||
__Moose.Include( 'Scripts/Moose/Functional/PseudoATC.lua' )
|
__Moose.Include( 'Scripts/Moose/Functional/PseudoATC.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Functional/Warehouse.lua' )
|
__Moose.Include( 'Scripts/Moose/Functional/Warehouse.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Functional/Fox.lua' )
|
__Moose.Include( 'Scripts/Moose/Functional/Fox.lua' )
|
||||||
|
__Moose.Include( 'Scripts/Moose/Functional/Mantis.lua' )
|
||||||
|
__Moose.Include( 'Scripts/Moose/Functional/Shorad.lua' )
|
||||||
|
|
||||||
__Moose.Include( 'Scripts/Moose/Ops/Airboss.lua' )
|
__Moose.Include( 'Scripts/Moose/Ops/Airboss.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Ops/RecoveryTanker.lua' )
|
__Moose.Include( 'Scripts/Moose/Ops/RecoveryTanker.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Ops/RescueHelo.lua' )
|
__Moose.Include( 'Scripts/Moose/Ops/RescueHelo.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Ops/ATIS.lua' )
|
__Moose.Include( 'Scripts/Moose/Ops/ATIS.lua' )
|
||||||
|
__Moose.Include( 'Scripts/Moose/Ops/CTLD.lua' )
|
||||||
|
__Moose.Include( 'Scripts/Moose/Ops/CSAR.lua' )
|
||||||
|
|
||||||
__Moose.Include( 'Scripts/Moose/AI/AI_Balancer.lua' )
|
__Moose.Include( 'Scripts/Moose/AI/AI_Balancer.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/AI/AI_Air.lua' )
|
__Moose.Include( 'Scripts/Moose/AI/AI_Air.lua' )
|
||||||
@@ -98,16 +101,25 @@ __Moose.Include( 'Scripts/Moose/AI/AI_Cargo.lua' )
|
|||||||
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_APC.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_Helicopter.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Airplane.lua' )
|
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Airplane.lua' )
|
||||||
|
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Ship.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Dispatcher.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_APC.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Dispatcher_Helicopter.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/AI/AI_Cargo_Dispatcher_Airplane.lua' )
|
||||||
|
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Dispatcher_Ship.lua' )
|
||||||
|
|
||||||
__Moose.Include( 'Scripts/Moose/Actions/Act_Assign.lua' )
|
__Moose.Include( 'Scripts/Moose/Actions/Act_Assign.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Actions/Act_Route.lua' )
|
__Moose.Include( 'Scripts/Moose/Actions/Act_Route.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Actions/Act_Account.lua' )
|
__Moose.Include( 'Scripts/Moose/Actions/Act_Account.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Actions/Act_Assist.lua' )
|
__Moose.Include( 'Scripts/Moose/Actions/Act_Assist.lua' )
|
||||||
|
|
||||||
|
__Moose.Include( 'Scripts/Moose/Sound/UserSound.lua' )
|
||||||
|
__Moose.Include( 'Scripts/Moose/Sound/SoundOutput.lua' )
|
||||||
|
__Moose.Include( 'Scripts/Moose/Sound/Radio.lua' )
|
||||||
|
__Moose.Include( 'Scripts/Moose/Sound/RadioQueue.lua' )
|
||||||
|
__Moose.Include( 'Scripts/Moose/Sound/RadioSpeech.lua' )
|
||||||
|
__Moose.Include( 'Scripts/Moose/Sound/SRS.lua' )
|
||||||
|
|
||||||
__Moose.Include( 'Scripts/Moose/Tasking/CommandCenter.lua' )
|
__Moose.Include( 'Scripts/Moose/Tasking/CommandCenter.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Tasking/Mission.lua' )
|
__Moose.Include( 'Scripts/Moose/Tasking/Mission.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Tasking/Task.lua' )
|
__Moose.Include( 'Scripts/Moose/Tasking/Task.lua' )
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
2179
Moose Development/Moose/Ops/CSAR.lua
Normal file
2179
Moose Development/Moose/Ops/CSAR.lua
Normal file
File diff suppressed because it is too large
Load Diff
4548
Moose Development/Moose/Ops/CTLD.lua
Normal file
4548
Moose Development/Moose/Ops/CTLD.lua
Normal file
File diff suppressed because it is too large
Load Diff
@@ -802,7 +802,7 @@ function RESCUEHELO:_OnEventCrashOrEject(EventData)
|
|||||||
-- Debug.
|
-- Debug.
|
||||||
local text=string.format("Unit %s crashed or ejected.", unitname)
|
local text=string.format("Unit %s crashed or ejected.", unitname)
|
||||||
MESSAGE:New(text, 10, "DEBUG"):ToAllIf(self.Debug)
|
MESSAGE:New(text, 10, "DEBUG"):ToAllIf(self.Debug)
|
||||||
self:I(self.lid..text)
|
self:T(self.lid..text)
|
||||||
|
|
||||||
-- Get coordinate of unit.
|
-- Get coordinate of unit.
|
||||||
local coord=unit:GetCoordinate()
|
local coord=unit:GetCoordinate()
|
||||||
|
|||||||
394
Moose Development/Moose/Sound/Radio.lua
Normal file
394
Moose Development/Moose/Sound/Radio.lua
Normal file
@@ -0,0 +1,394 @@
|
|||||||
|
--- **Sound** - Radio transmissions.
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ## Features:
|
||||||
|
--
|
||||||
|
-- * Provide radio functionality to broadcast radio transmissions.
|
||||||
|
--
|
||||||
|
-- What are radio communications in DCS?
|
||||||
|
--
|
||||||
|
-- * Radio transmissions consist of **sound files** that are broadcasted on a specific **frequency** (e.g. 115MHz) and **modulation** (e.g. AM),
|
||||||
|
-- * They can be **subtitled** for a specific **duration**, the **power** in Watts of the transmiter's antenna can be set, and the transmission can be **looped**.
|
||||||
|
--
|
||||||
|
-- How to supply DCS my own Sound Files?
|
||||||
|
--
|
||||||
|
-- * Your sound files need to be encoded in **.ogg** or .wav,
|
||||||
|
-- * Your sound files should be **as tiny as possible**. It is suggested you encode in .ogg with low bitrate and sampling settings,
|
||||||
|
-- * They need to be added in .\l10n\DEFAULT\ in you .miz file (wich can be decompressed like a .zip file),
|
||||||
|
-- * For 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 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 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.
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ### Authors: Hugues "Grey_Echo" Bousquet, funkyfranky
|
||||||
|
--
|
||||||
|
-- @module Sound.Radio
|
||||||
|
-- @image Core_Radio.JPG
|
||||||
|
|
||||||
|
|
||||||
|
--- *It's not true I had nothing on, I had the radio on.* -- Marilyn Monroe
|
||||||
|
--
|
||||||
|
-- # RADIO usage
|
||||||
|
--
|
||||||
|
-- There are 3 steps to a successful radio transmission.
|
||||||
|
--
|
||||||
|
-- * First, you need to **"add a @{#RADIO} object** to your @{Wrapper.Positionable#POSITIONABLE}. This is done using the @{Wrapper.Positionable#POSITIONABLE.GetRadio}() function,
|
||||||
|
-- * Then, you will **set the relevant parameters** to the transmission (see below),
|
||||||
|
-- * When done, you can actually **broadcast the transmission** (i.e. play the sound) with the @{RADIO.Broadcast}() function.
|
||||||
|
--
|
||||||
|
-- Methods to set relevant parameters for both a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP} or any other @{Wrapper.Positionable#POSITIONABLE}
|
||||||
|
--
|
||||||
|
-- * @{#RADIO.SetFileName}() : Sets the file name of your sound file (e.g. "Noise.ogg"),
|
||||||
|
-- * @{#RADIO.SetFrequency}() : Sets the frequency of your transmission.
|
||||||
|
-- * @{#RADIO.SetModulation}() : Sets the modulation of your transmission.
|
||||||
|
-- * @{#RADIO.SetLoop}() : Choose if you want the transmission to be looped. If you need your transmission to be looped, you might need a @{#BEACON} instead...
|
||||||
|
--
|
||||||
|
-- Additional Methods to set relevant parameters if the 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 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?
|
||||||
|
--
|
||||||
|
-- * 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 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 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 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",
|
||||||
|
FileName = "",
|
||||||
|
Frequency = 0,
|
||||||
|
Modulation = radio.modulation.AM,
|
||||||
|
Subtitle = "",
|
||||||
|
SubtitleDuration = 0,
|
||||||
|
Power = 100,
|
||||||
|
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.
|
||||||
|
-- @param #RADIO self
|
||||||
|
-- @param Wrapper.Positionable#POSITIONABLE Positionable The @{Positionable} that will receive radio capabilities.
|
||||||
|
-- @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:F(Positionable)
|
||||||
|
|
||||||
|
if Positionable:GetPointVec2() then -- It's stupid, but the only way I found to make sure positionable is valid
|
||||||
|
self.Positionable = Positionable
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
self:E({error="The passed positionable is invalid, no RADIO created!", positionable=Positionable})
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- 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
|
||||||
|
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?", FileName})
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- 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 Frequency in MHz.
|
||||||
|
-- @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 <= 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
|
||||||
|
|
||||||
|
local commandSetFrequency={
|
||||||
|
id = "SetFrequency",
|
||||||
|
params = {
|
||||||
|
frequency = self.Frequency,
|
||||||
|
modulation = self.Modulation,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self:T2(commandSetFrequency)
|
||||||
|
self.Positionable:SetCommand(commandSetFrequency)
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
-- end
|
||||||
|
end
|
||||||
|
|
||||||
|
self:E({"Frequency is not a number. Frequency unchanged.", Frequency})
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set AM or FM modulation of the radio transmitter.
|
||||||
|
-- @param #RADIO self
|
||||||
|
-- @param #number Modulation Modulation is either radio.modulation.AM or radio.modulation.FM.
|
||||||
|
-- @return #RADIO self
|
||||||
|
function RADIO:SetModulation(Modulation)
|
||||||
|
self:F2(Modulation)
|
||||||
|
if type(Modulation) == "number" then
|
||||||
|
if Modulation == radio.modulation.AM or Modulation == radio.modulation.FM then --TODO Maybe make this future proof if ED decides to add an other modulation ?
|
||||||
|
self.Modulation = Modulation
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self:E({"Modulation is invalid. Use DCS's enum radio.modulation. Modulation unchanged.", self.Modulation})
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check validity of the power passed and sets RADIO.Power
|
||||||
|
-- @param #RADIO self
|
||||||
|
-- @param #number Power 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
|
||||||
|
else
|
||||||
|
self:E({"Power is invalid. Power unchanged.", self.Power})
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set message looping on or off.
|
||||||
|
-- @param #RADIO self
|
||||||
|
-- @param #boolean Loop If true, message is repeated indefinitely.
|
||||||
|
-- @return #RADIO self
|
||||||
|
function RADIO:SetLoop(Loop)
|
||||||
|
self:F2(Loop)
|
||||||
|
if type(Loop) == "boolean" then
|
||||||
|
self.Loop = Loop
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
self:E({"Loop is invalid. Loop unchanged.", self.Loop})
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check validity of the subtitle and the subtitleDuration passed and sets RADIO.subtitle and RADIO.subtitleDuration
|
||||||
|
-- Both parameters are mandatory, since it wouldn't make much sense to change the Subtitle and not its duration
|
||||||
|
-- @param #RADIO self
|
||||||
|
-- @param #string Subtitle
|
||||||
|
-- @param #number SubtitleDuration in s
|
||||||
|
-- @return #RADIO self
|
||||||
|
-- @usage
|
||||||
|
-- -- create the broadcaster and attaches it a RADIO
|
||||||
|
-- local MyUnit = UNIT:FindByName("MyUnit")
|
||||||
|
-- local MyUnitRadio = MyUnit:GetRadio()
|
||||||
|
--
|
||||||
|
-- -- add a subtitle for the next transmission, which will be up for 10s
|
||||||
|
-- MyUnitRadio:SetSubtitle("My Subtitle, 10)
|
||||||
|
function RADIO:SetSubtitle(Subtitle, SubtitleDuration)
|
||||||
|
self:F2({Subtitle, SubtitleDuration})
|
||||||
|
if type(Subtitle) == "string" then
|
||||||
|
self.Subtitle = Subtitle
|
||||||
|
else
|
||||||
|
self.Subtitle = ""
|
||||||
|
self:E({"Subtitle is invalid. Subtitle reset.", self.Subtitle})
|
||||||
|
end
|
||||||
|
if type(SubtitleDuration) == "number" then
|
||||||
|
self.SubtitleDuration = SubtitleDuration
|
||||||
|
else
|
||||||
|
self.SubtitleDuration = 0
|
||||||
|
self:E({"SubtitleDuration is invalid. SubtitleDuration reset.", self.SubtitleDuration})
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Create a new transmission, that is to say, populate the RADIO with relevant data
|
||||||
|
-- In this function the data is especially relevant if the broadcaster is anything but a UNIT or a GROUP,
|
||||||
|
-- but it will work with a UNIT or a GROUP anyway.
|
||||||
|
-- Only the #RADIO and the Filename are mandatory
|
||||||
|
-- @param #RADIO self
|
||||||
|
-- @param #string FileName 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})
|
||||||
|
|
||||||
|
self:SetFileName(FileName)
|
||||||
|
if Frequency then self:SetFrequency(Frequency) end
|
||||||
|
if Modulation then self:SetModulation(Modulation) end
|
||||||
|
if Power then self:SetPower(Power) end
|
||||||
|
if Loop then self:SetLoop(Loop) end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Create a new transmission, that is to say, populate the RADIO with relevant data
|
||||||
|
-- In this function the data is especially relevant if the broadcaster is a UNIT or a GROUP,
|
||||||
|
-- but it will work for any @{Wrapper.Positionable#POSITIONABLE}.
|
||||||
|
-- Only the RADIO and the Filename are mandatory.
|
||||||
|
-- @param #RADIO self
|
||||||
|
-- @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)
|
||||||
|
|
||||||
|
-- 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
|
||||||
|
|
||||||
|
--- 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
|
||||||
|
-- * If the POSITIONABLE is not a UNIT or a GROUP, we use the generic (but limited) trigger.action.radioTransmission()
|
||||||
|
-- * If the POSITIONABLE is a UNIT or a GROUP, we use the "TransmitMessage" Command
|
||||||
|
-- * If your POSITIONABLE is a UNIT or a GROUP, the Power is ignored.
|
||||||
|
-- * If your POSITIONABLE is not a UNIT or a GROUP, the Subtitle, SubtitleDuration are ignored
|
||||||
|
-- @param #RADIO self
|
||||||
|
-- @param #boolean viatrigger Use trigger.action.radioTransmission() in any case, i.e. also for UNITS and GROUPS.
|
||||||
|
-- @return #RADIO self
|
||||||
|
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") 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: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
|
||||||
|
-- @return #RADIO self
|
||||||
|
function RADIO:StopBroadcast()
|
||||||
|
self:F()
|
||||||
|
-- If the POSITIONABLE is a UNIT or a GROUP, stop the transmission with the DCS "StopTransmission" command
|
||||||
|
if self.Positionable.ClassName == "UNIT" or self.Positionable.ClassName == "GROUP" then
|
||||||
|
|
||||||
|
local commandStopTransmission={id="StopTransmission", params={}}
|
||||||
|
|
||||||
|
self.Positionable:SetCommand(commandStopTransmission)
|
||||||
|
else
|
||||||
|
-- Else, we use the appropriate singleton funciton
|
||||||
|
trigger.action.stopRadioTransmission(tostring(self.ID))
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
@@ -1,20 +1,24 @@
|
|||||||
--- **Core** - Queues Radio Transmissions.
|
--- **Sound** - Queues Radio Transmissions.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ## Features:
|
-- ## Features:
|
||||||
--
|
--
|
||||||
-- * Managed Radio Transmissions.
|
-- * Manage Radio Transmissions
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ### Authors: funkyfranky
|
-- ### Authors: funkyfranky
|
||||||
--
|
--
|
||||||
-- @module Core.RadioQueue
|
-- @module Sound.RadioQueue
|
||||||
-- @image Core_Radio.JPG
|
-- @image Core_Radio.JPG
|
||||||
|
|
||||||
--- Manages radio transmissions.
|
--- Manages radio transmissions.
|
||||||
--
|
--
|
||||||
|
-- The main goal of the RADIOQUEUE class is to string together multiple sound files to play a complete sentence.
|
||||||
|
-- The underlying problem is that radio transmissions in DCS are not queued but played "on top" of each other.
|
||||||
|
-- Therefore, to achive the goal, it is vital to know the precise duration how long it takes to play the sound file.
|
||||||
|
--
|
||||||
-- @type RADIOQUEUE
|
-- @type RADIOQUEUE
|
||||||
-- @field #string ClassName Name of the class "RADIOQUEUE".
|
-- @field #string ClassName Name of the class "RADIOQUEUE".
|
||||||
-- @field #boolean Debugmode Debug mode. More info.
|
-- @field #boolean Debugmode Debug mode. More info.
|
||||||
@@ -35,6 +39,7 @@
|
|||||||
-- @field #table numbers Table of number transmission parameters.
|
-- @field #table numbers Table of number transmission parameters.
|
||||||
-- @field #boolean checking Scheduler is checking the radio queue.
|
-- @field #boolean checking Scheduler is checking the radio queue.
|
||||||
-- @field #boolean schedonce Call ScheduleOnce instead of normal scheduler.
|
-- @field #boolean schedonce Call ScheduleOnce instead of normal scheduler.
|
||||||
|
-- @field Sound.SRS#MSRS msrs Moose SRS class.
|
||||||
-- @extends Core.Base#BASE
|
-- @extends Core.Base#BASE
|
||||||
RADIOQUEUE = {
|
RADIOQUEUE = {
|
||||||
ClassName = "RADIOQUEUE",
|
ClassName = "RADIOQUEUE",
|
||||||
@@ -69,12 +74,14 @@ RADIOQUEUE = {
|
|||||||
-- @field #boolean isplaying If true, transmission is currently playing.
|
-- @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 Tplay Mission time (abs) in seconds when the transmission should be played.
|
||||||
-- @field #number interval Interval in seconds before next transmission.
|
-- @field #number interval Interval in seconds before next transmission.
|
||||||
|
-- @field Sound.SoundOutput#SOUNDFILE soundfile Sound file object to play via SRS.
|
||||||
|
-- @field Sound.SoundOutput#SOUNDTEXT soundtext Sound TTS object to play via SRS.
|
||||||
|
|
||||||
|
|
||||||
--- Create a new RADIOQUEUE object for a given radio frequency/modulation.
|
--- Create a new RADIOQUEUE object for a given radio frequency/modulation.
|
||||||
-- @param #RADIOQUEUE self
|
-- @param #RADIOQUEUE self
|
||||||
-- @param #number frequency The radio frequency in MHz.
|
-- @param #number frequency The radio frequency in MHz.
|
||||||
-- @param #number modulation (Optional) The radio modulation. Default radio.modulation.AM.
|
-- @param #number modulation (Optional) The radio modulation. Default `radio.modulation.AM` (=0).
|
||||||
-- @param #string alias (Optional) Name of the radio queue.
|
-- @param #string alias (Optional) Name of the radio queue.
|
||||||
-- @return #RADIOQUEUE self The RADIOQUEUE object.
|
-- @return #RADIOQUEUE self The RADIOQUEUE object.
|
||||||
function RADIOQUEUE:New(frequency, modulation, alias)
|
function RADIOQUEUE:New(frequency, modulation, alias)
|
||||||
@@ -125,9 +132,9 @@ function RADIOQUEUE:Start(delay, dt)
|
|||||||
|
|
||||||
-- Start Scheduler.
|
-- Start Scheduler.
|
||||||
if self.schedonce then
|
if self.schedonce then
|
||||||
self:_CheckRadioQueueDelayed(delay)
|
self:_CheckRadioQueueDelayed(self.delay)
|
||||||
else
|
else
|
||||||
self.RQid=self.scheduler:Schedule(nil, RADIOQUEUE._CheckRadioQueue, {self}, delay, dt)
|
self.RQid=self.scheduler:Schedule(nil, RADIOQUEUE._CheckRadioQueue, {self}, self.delay, self.dt)
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
@@ -170,6 +177,17 @@ function RADIOQUEUE:SetRadioPower(power)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Set SRS.
|
||||||
|
-- @param #RADIOQUEUE self
|
||||||
|
-- @param #string PathToSRS Path to SRS.
|
||||||
|
-- @param #number Port SRS port. Default 5002.
|
||||||
|
-- @return #RADIOQUEUE self The RADIOQUEUE object.
|
||||||
|
function RADIOQUEUE:SetSRS(PathToSRS, Port)
|
||||||
|
self.msrs=MSRS:New(PathToSRS, self.frequency/1000000, self.modulation)
|
||||||
|
self.msrs:SetPort(Port)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Set parameters of a digit.
|
--- Set parameters of a digit.
|
||||||
-- @param #RADIOQUEUE self
|
-- @param #RADIOQUEUE self
|
||||||
-- @param #number digit The digit 0-9.
|
-- @param #number digit The digit 0-9.
|
||||||
@@ -202,7 +220,7 @@ end
|
|||||||
--- Add a transmission to the radio queue.
|
--- Add a transmission to the radio queue.
|
||||||
-- @param #RADIOQUEUE self
|
-- @param #RADIOQUEUE self
|
||||||
-- @param #RADIOQUEUE.Transmission transmission The transmission data table.
|
-- @param #RADIOQUEUE.Transmission transmission The transmission data table.
|
||||||
-- @return #RADIOQUEUE self The RADIOQUEUE object.
|
-- @return #RADIOQUEUE self
|
||||||
function RADIOQUEUE:AddTransmission(transmission)
|
function RADIOQUEUE:AddTransmission(transmission)
|
||||||
self:F({transmission=transmission})
|
self:F({transmission=transmission})
|
||||||
|
|
||||||
@@ -221,7 +239,7 @@ function RADIOQUEUE:AddTransmission(transmission)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Add a transmission to the radio queue.
|
--- Create a new transmission and add it to the radio queue.
|
||||||
-- @param #RADIOQUEUE self
|
-- @param #RADIOQUEUE self
|
||||||
-- @param #string filename Name of the sound file. Usually an ogg or wav file type.
|
-- @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 duration Duration in seconds the file lasts.
|
||||||
@@ -230,7 +248,7 @@ end
|
|||||||
-- @param #number interval Interval in seconds after the last transmission finished.
|
-- @param #number interval Interval in seconds after the last transmission finished.
|
||||||
-- @param #string subtitle Subtitle of the transmission.
|
-- @param #string subtitle Subtitle of the transmission.
|
||||||
-- @param #number subduration Duration [sec] of the subtitle being displayed. Default 5 sec.
|
-- @param #number subduration Duration [sec] of the subtitle being displayed. Default 5 sec.
|
||||||
-- @return #RADIOQUEUE self The RADIOQUEUE object.
|
-- @return #RADIOQUEUE.Transmission Radio transmission table.
|
||||||
function RADIOQUEUE:NewTransmission(filename, duration, path, tstart, interval, subtitle, subduration)
|
function RADIOQUEUE:NewTransmission(filename, duration, path, tstart, interval, subtitle, subduration)
|
||||||
|
|
||||||
-- Sanity checks.
|
-- Sanity checks.
|
||||||
@@ -269,9 +287,36 @@ function RADIOQUEUE:NewTransmission(filename, duration, path, tstart, interval,
|
|||||||
-- Add transmission to queue.
|
-- Add transmission to queue.
|
||||||
self:AddTransmission(transmission)
|
self:AddTransmission(transmission)
|
||||||
|
|
||||||
|
return transmission
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Add a SOUNDFILE to the radio queue.
|
||||||
|
-- @param #RADIOQUEUE self
|
||||||
|
-- @param Sound.SoundOutput#SOUNDFILE soundfile Sound file object to be added.
|
||||||
|
-- @param #number tstart Start time (abs) seconds. Default now.
|
||||||
|
-- @param #number interval Interval in seconds after the last transmission finished.
|
||||||
|
-- @return #RADIOQUEUE self
|
||||||
|
function RADIOQUEUE:AddSoundFile(soundfile, tstart, interval)
|
||||||
|
--env.info(string.format("FF add soundfile: name=%s%s", soundfile:GetPath(), soundfile:GetFileName()))
|
||||||
|
local transmission=self:NewTransmission(soundfile:GetFileName(), soundfile.duration, soundfile:GetPath(), tstart, interval, soundfile.subtitle, soundfile.subduration)
|
||||||
|
transmission.soundfile=soundfile
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Add a SOUNDTEXT to the radio queue.
|
||||||
|
-- @param #RADIOQUEUE self
|
||||||
|
-- @param Sound.SoundOutput#SOUNDTEXT soundtext Text-to-speech text.
|
||||||
|
-- @param #number tstart Start time (abs) seconds. Default now.
|
||||||
|
-- @param #number interval Interval in seconds after the last transmission finished.
|
||||||
|
-- @return #RADIOQUEUE self
|
||||||
|
function RADIOQUEUE:AddSoundText(soundtext, tstart, interval)
|
||||||
|
|
||||||
|
local transmission=self:NewTransmission("SoundText.ogg", soundtext.duration, nil, tstart, interval, soundtext.subtitle, soundtext.subduration)
|
||||||
|
transmission.soundtext=soundtext
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Convert a number (as string) into a radio transmission.
|
--- Convert a number (as string) into a radio transmission.
|
||||||
-- E.g. for board number or headings.
|
-- E.g. for board number or headings.
|
||||||
-- @param #RADIOQUEUE self
|
-- @param #RADIOQUEUE self
|
||||||
@@ -281,18 +326,8 @@ end
|
|||||||
-- @return #number Duration of the call in seconds.
|
-- @return #number Duration of the call in seconds.
|
||||||
function RADIOQUEUE:Number2Transmission(number, delay, interval)
|
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.
|
-- Split string into characters.
|
||||||
local numbers=_split(number)
|
local numbers=UTILS.GetCharacters(number)
|
||||||
|
|
||||||
local wait=0
|
local wait=0
|
||||||
for i=1,#numbers do
|
for i=1,#numbers do
|
||||||
@@ -325,6 +360,11 @@ end
|
|||||||
-- @param #RADIOQUEUE.Transmission transmission The transmission.
|
-- @param #RADIOQUEUE.Transmission transmission The transmission.
|
||||||
function RADIOQUEUE:Broadcast(transmission)
|
function RADIOQUEUE:Broadcast(transmission)
|
||||||
|
|
||||||
|
if ((transmission.soundfile and transmission.soundfile.useSRS) or transmission.soundtext) and self.msrs then
|
||||||
|
self:_BroadcastSRS(transmission)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
-- Get unit sending the transmission.
|
-- Get unit sending the transmission.
|
||||||
local sender=self:_GetRadioSender()
|
local sender=self:_GetRadioSender()
|
||||||
|
|
||||||
@@ -416,6 +456,19 @@ function RADIOQUEUE:Broadcast(transmission)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Broadcast radio message.
|
||||||
|
-- @param #RADIOQUEUE self
|
||||||
|
-- @param #RADIOQUEUE.Transmission transmission The transmission.
|
||||||
|
function RADIOQUEUE:_BroadcastSRS(transmission)
|
||||||
|
|
||||||
|
if transmission.soundfile and transmission.soundfile.useSRS then
|
||||||
|
self.msrs:PlaySoundFile(transmission.soundfile)
|
||||||
|
elseif transmission.soundtext then
|
||||||
|
self.msrs:PlaySoundText(transmission.soundtext)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
--- Start checking the radio queue.
|
--- Start checking the radio queue.
|
||||||
-- @param #RADIOQUEUE self
|
-- @param #RADIOQUEUE self
|
||||||
-- @param #number delay Delay in seconds before checking.
|
-- @param #number delay Delay in seconds before checking.
|
||||||
@@ -525,7 +578,7 @@ end
|
|||||||
|
|
||||||
--- Get unit from which we want to transmit a radio message. This has to be an aircraft for subtitles to work.
|
--- Get unit from which we want to transmit a radio message. This has to be an aircraft for subtitles to work.
|
||||||
-- @param #RADIOQUEUE self
|
-- @param #RADIOQUEUE self
|
||||||
-- @return Wrapper.Unit#UNIT Sending aircraft unit or nil if was not setup, is not an aircraft or is not alive.
|
-- @return Wrapper.Unit#UNIT Sending unit or nil if was not setup, is not an aircraft or ground unit or is not alive.
|
||||||
function RADIOQUEUE:_GetRadioSender()
|
function RADIOQUEUE:_GetRadioSender()
|
||||||
|
|
||||||
-- Check if we have a sending aircraft.
|
-- Check if we have a sending aircraft.
|
||||||
@@ -538,7 +591,7 @@ function RADIOQUEUE:_GetRadioSender()
|
|||||||
sender=UNIT:FindByName(self.sendername)
|
sender=UNIT:FindByName(self.sendername)
|
||||||
|
|
||||||
-- Check that sender is alive and an aircraft.
|
-- Check that sender is alive and an aircraft.
|
||||||
if sender and sender:IsAlive() and sender:IsAir() then
|
if sender and sender:IsAlive() and (sender:IsAir() or sender:IsGround()) then
|
||||||
return sender
|
return sender
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -547,7 +600,7 @@ function RADIOQUEUE:_GetRadioSender()
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get unit from which we want to transmit a radio message. This has to be an aircraft for subtitles to work.
|
--- Get unit from which we want to transmit a radio message. This has to be an aircraft or ground unit for subtitles to work.
|
||||||
-- @param #RADIOQUEUE self
|
-- @param #RADIOQUEUE self
|
||||||
-- @return DCS#Vec3 Vector 3D.
|
-- @return DCS#Vec3 Vector 3D.
|
||||||
function RADIOQUEUE:_GetRadioSenderCoord()
|
function RADIOQUEUE:_GetRadioSenderCoord()
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
--
|
--
|
||||||
-- ### Authors: FlightControl
|
-- ### Authors: FlightControl
|
||||||
--
|
--
|
||||||
-- @module Core.RadioSpeech
|
-- @module Sound.RadioSpeech
|
||||||
-- @image Core_Radio.JPG
|
-- @image Core_Radio.JPG
|
||||||
|
|
||||||
--- Makes the radio speak.
|
--- Makes the radio speak.
|
||||||
@@ -162,33 +162,32 @@ RADIOSPEECH.Vocabulary.RU = {
|
|||||||
["8000"] = { "8000", 0.92 },
|
["8000"] = { "8000", 0.92 },
|
||||||
["9000"] = { "9000", 0.87 },
|
["9000"] = { "9000", 0.87 },
|
||||||
|
|
||||||
["степени"] = { "degrees", 0.5 },
|
["градусы"] = { "degrees", 0.5 },
|
||||||
["километров"] = { "kilometers", 0.65 },
|
["километры"] = { "kilometers", 0.65 },
|
||||||
["km"] = { "kilometers", 0.65 },
|
["km"] = { "kilometers", 0.65 },
|
||||||
["миль"] = { "miles", 0.45 },
|
["мили"] = { "miles", 0.45 },
|
||||||
["mi"] = { "miles", 0.45 },
|
["mi"] = { "miles", 0.45 },
|
||||||
["метры"] = { "meters", 0.41 },
|
["метров"] = { "meters", 0.41 },
|
||||||
["m"] = { "meters", 0.41 },
|
["m"] = { "meters", 0.41 },
|
||||||
["ноги"] = { "feet", 0.37 },
|
["ноги"] = { "feet", 0.37 },
|
||||||
|
|
||||||
["br"] = { "br", 1.1 },
|
["br"] = { "br", 1.1 },
|
||||||
["bra"] = { "bra", 0.3 },
|
["bra"] = { "bra", 0.3 },
|
||||||
|
|
||||||
|
["возвращение на базу"] = { "returning_to_base", 1.40 },
|
||||||
["возвращаясь на базу"] = { "returning_to_base", 1.40 },
|
|
||||||
["на пути к наземной цели"] = { "on_route_to_ground_target", 1.45 },
|
["на пути к наземной цели"] = { "on_route_to_ground_target", 1.45 },
|
||||||
["перехват самолетов"] = { "intercepting_bogeys", 1.22 },
|
["перехват боги"] = { "intercepting_bogeys", 1.22 },
|
||||||
["поражение наземной цели"] = { "engaging_ground_target", 1.53 },
|
["поражение наземной цели"] = { "engaging_ground_target", 1.53 },
|
||||||
["захватывающие самолеты"] = { "engaging_bogeys", 1.68 },
|
["привлечение болотных птиц"] = { "engaging_bogeys", 1.68 },
|
||||||
["колеса вверх"] = { "wheels_up", 0.92 },
|
["колёса вверх..."] = { "wheels_up", 0.92 },
|
||||||
["посадка на базу"] = { "landing at base", 1.04 },
|
["посадка на базу"] = { "landing at base", 1.04 },
|
||||||
["патрулирующий"] = { "patrolling", 0.96 },
|
["патрулирование"] = { "patrolling", 0.96 },
|
||||||
|
|
||||||
["за"] = { "for", 0.27 },
|
["для"] = { "for", 0.27 },
|
||||||
["и"] = { "and", 0.17 },
|
["и"] = { "and", 0.17 },
|
||||||
["в"] = { "at", 0.19 },
|
["на сайте"] = { "at", 0.19 },
|
||||||
["dot"] = { "dot", 0.51 },
|
["точка"] = { "dot", 0.51 },
|
||||||
["defender"] = { "defender", 0.45 },
|
["защитник"] = { "defender", 0.45 },
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Create a new RADIOSPEECH object for a given radio frequency/modulation.
|
--- Create a new RADIOSPEECH object for a given radio frequency/modulation.
|
||||||
701
Moose Development/Moose/Sound/SRS.lua
Normal file
701
Moose Development/Moose/Sound/SRS.lua
Normal file
@@ -0,0 +1,701 @@
|
|||||||
|
--- **Sound** - Simple Radio Standalone (SRS) Integration.
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- **Main Features:**
|
||||||
|
--
|
||||||
|
-- * Play sound files via SRS
|
||||||
|
-- * Play text-to-speach via SRS
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ## Youtube Videos: None yet
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ## Missions: None yet
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ## Sound files: [MOOSE Sound Files](https://github.com/FlightControl-Master/MOOSE_SOUND/releases)
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- The goal of the [SRS](https://github.com/ciribob/DCS-SimpleRadioStandalone) project is to bring VoIP communication into DCS and to make communication as frictionless as possible.
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ### Author: **funkyfranky**
|
||||||
|
-- @module Sound.MSRS
|
||||||
|
-- @image Sound_MSRS.png
|
||||||
|
|
||||||
|
--- MSRS class.
|
||||||
|
-- @type MSRS
|
||||||
|
-- @field #string ClassName Name of the class.
|
||||||
|
-- @field #string lid Class id string for output to DCS log file.
|
||||||
|
-- @field #table frequencies Frequencies used in the transmissions.
|
||||||
|
-- @field #table modulations Modulations used in the transmissions.
|
||||||
|
-- @field #number coalition Coalition of the transmission.
|
||||||
|
-- @field #number port Port. Default 5002.
|
||||||
|
-- @field #string name Name. Default "DCS-STTS".
|
||||||
|
-- @field #number volume Volume between 0 (min) and 1 (max). Default 1.
|
||||||
|
-- @field #string culture Culture. Default "en-GB".
|
||||||
|
-- @field #string gender Gender. Default "female".
|
||||||
|
-- @field #string voice Specifc voce.
|
||||||
|
-- @field Core.Point#COORDINATE coordinate Coordinate from where the transmission is send.
|
||||||
|
-- @field #string path Path to the SRS exe. This includes the final slash "/".
|
||||||
|
-- @field #string google Full path google credentials JSON file, e.g. "C:\Users\username\Downloads\service-account-file.json".
|
||||||
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
|
--- *It is a very sad thing that nowadays there is so little useless information.* - Oscar Wilde
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- 
|
||||||
|
--
|
||||||
|
-- # The MSRS Concept
|
||||||
|
--
|
||||||
|
-- This class allows to broadcast sound files or text via Simple Radio Standalone (SRS).
|
||||||
|
--
|
||||||
|
-- ## Prerequisites
|
||||||
|
--
|
||||||
|
-- This script needs SRS version >= 1.9.6.
|
||||||
|
--
|
||||||
|
-- # Play Sound Files
|
||||||
|
--
|
||||||
|
-- local soundfile=SOUNDFILE:New("My Soundfile.ogg", "D:\\Sounds For DCS")
|
||||||
|
-- local msrs=MSRS:New("C:\\Path To SRS", 251, radio.modulation.AM)
|
||||||
|
-- msrs:PlaySoundFile(soundfile)
|
||||||
|
--
|
||||||
|
-- # Play Text-To-Speech
|
||||||
|
--
|
||||||
|
-- Basic example:
|
||||||
|
--
|
||||||
|
-- -- Create a SOUNDTEXT object.
|
||||||
|
-- local text=SOUNDTEXT:New("All Enemies destroyed")
|
||||||
|
--
|
||||||
|
-- -- MOOSE SRS
|
||||||
|
-- local msrs=MSRS:New("D:\\DCS\\_SRS\\", 305, radio.modulation.AM)
|
||||||
|
--
|
||||||
|
-- -- Text-to speech with default voice after 2 seconds.
|
||||||
|
-- msrs:PlaySoundText(text, 2)
|
||||||
|
--
|
||||||
|
-- ## Set Gender
|
||||||
|
--
|
||||||
|
-- Use a specific gender with the @{#MSRS.SetGender} function, e.g. `SetGender("male")` or `:SetGender("female")`.
|
||||||
|
--
|
||||||
|
-- ## Set Culture
|
||||||
|
--
|
||||||
|
-- Use a specific "culture" with the @{#MSRS.SetCulture} function, e.g. `:SetCulture("en-US")` or `:SetCulture("de-DE")`.
|
||||||
|
--
|
||||||
|
-- ## Set Google
|
||||||
|
--
|
||||||
|
-- Use Google's text-to-speech engine with the @{#MSRS.SetGoogle} function, e.g. ':SetGoogle()'.
|
||||||
|
-- By enabling this it also allows you to utilize SSML in your text for added flexibilty.
|
||||||
|
-- For more information on setting up a cloud account, visit: https://cloud.google.com/text-to-speech
|
||||||
|
-- Google's supported SSML reference: https://cloud.google.com/text-to-speech/docs/ssml
|
||||||
|
--
|
||||||
|
-- ## Set Voice
|
||||||
|
--
|
||||||
|
-- Use a specifc voice with the @{#MSRS.SetVoice} function, e.g, `:SetVoice("Microsoft Hedda Desktop")`.
|
||||||
|
-- Note that this must be installed on your windows system.
|
||||||
|
-- If enabling SetGoogle(), you can use voices provided by Google
|
||||||
|
-- Google's supported voices: https://cloud.google.com/text-to-speech/docs/voices
|
||||||
|
--
|
||||||
|
-- ## Set Coordinate
|
||||||
|
--
|
||||||
|
-- Use @{#MSRS.SetCoordinate} to define the origin from where the transmission is broadcasted.
|
||||||
|
--
|
||||||
|
-- @field #MSRS
|
||||||
|
MSRS = {
|
||||||
|
ClassName = "MSRS",
|
||||||
|
lid = nil,
|
||||||
|
port = 5002,
|
||||||
|
name = "MSRS",
|
||||||
|
frequencies = {},
|
||||||
|
modulations = {},
|
||||||
|
coalition = 0,
|
||||||
|
gender = "female",
|
||||||
|
culture = nil,
|
||||||
|
voice = nil,
|
||||||
|
volume = 1,
|
||||||
|
speed = 1,
|
||||||
|
coordinate = nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
--- MSRS class version.
|
||||||
|
-- @field #string version
|
||||||
|
MSRS.version="0.0.3"
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- TODO list
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- TODO: Add functions to add/remove freqs and modulations.
|
||||||
|
-- DONE: Add coordinate.
|
||||||
|
-- DONE: Add google.
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Constructor
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Create a new MSRS object.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param #string PathToSRS Path to the directory, where SRS is located.
|
||||||
|
-- @param #number Frequency Radio frequency in MHz. Default 143.00 MHz. Can also be given as a #table of multiple frequencies.
|
||||||
|
-- @param #number Modulation Radio modulation: 0=AM (default), 1=FM. See `radio.modulation.AM` and `radio.modulation.FM` enumerators. Can also be given as a #table of multiple modulations.
|
||||||
|
-- @return #MSRS self
|
||||||
|
function MSRS:New(PathToSRS, Frequency, Modulation)
|
||||||
|
|
||||||
|
-- Defaults.
|
||||||
|
Frequency =Frequency or 143
|
||||||
|
Modulation= Modulation or radio.modulation.AM
|
||||||
|
|
||||||
|
-- Inherit everything from FSM class.
|
||||||
|
local self=BASE:Inherit(self, BASE:New()) -- #MSRS
|
||||||
|
|
||||||
|
self:SetPath(PathToSRS)
|
||||||
|
self:SetPort()
|
||||||
|
self:SetFrequencies(Frequency)
|
||||||
|
self:SetModulations(Modulation)
|
||||||
|
self:SetGender()
|
||||||
|
self:SetCoalition()
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- User Functions
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Set path to SRS install directory. More precisely, path to where the DCS-
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param #string Path Path to the directory, where the sound file is located. This does **not** contain a final backslash or slash.
|
||||||
|
-- @return #MSRS self
|
||||||
|
function MSRS:SetPath(Path)
|
||||||
|
|
||||||
|
if Path==nil then
|
||||||
|
self:E("ERROR: No path to SRS directory specified!")
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set path.
|
||||||
|
self.path=Path
|
||||||
|
|
||||||
|
-- Remove (back)slashes.
|
||||||
|
local n=1 ; local nmax=1000
|
||||||
|
while (self.path:sub(-1)=="/" or self.path:sub(-1)==[[\]]) and n<=nmax do
|
||||||
|
self.path=self.path:sub(1,#self.path-1)
|
||||||
|
n=n+1
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Debug output.
|
||||||
|
self:T(string.format("SRS path=%s", self:GetPath()))
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get path to SRS directory.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @return #string Path to the directory. This includes the final slash "/".
|
||||||
|
function MSRS:GetPath()
|
||||||
|
return self.path
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set port.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param #number Port Port. Default 5002.
|
||||||
|
-- @return #MSRS self
|
||||||
|
function MSRS:SetPort(Port)
|
||||||
|
self.port=Port or 5002
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get port.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @return #number Port.
|
||||||
|
function MSRS:GetPort()
|
||||||
|
return self.port
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set coalition.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param #number Coalition Coalition. Default 0.
|
||||||
|
-- @return #MSRS self
|
||||||
|
function MSRS:SetCoalition(Coalition)
|
||||||
|
self.coalition=Coalition or 0
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get coalition.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @return #number Coalition.
|
||||||
|
function MSRS:GetCoalition()
|
||||||
|
return self.coalition
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Set frequencies.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param #table Frequencies Frequencies in MHz. Can also be given as a #number if only one frequency should be used.
|
||||||
|
-- @return #MSRS self
|
||||||
|
function MSRS:SetFrequencies(Frequencies)
|
||||||
|
|
||||||
|
-- Ensure table.
|
||||||
|
if type(Frequencies)~="table" then
|
||||||
|
Frequencies={Frequencies}
|
||||||
|
end
|
||||||
|
|
||||||
|
self.frequencies=Frequencies
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get frequencies.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param #table Frequencies in MHz.
|
||||||
|
function MSRS:GetFrequencies()
|
||||||
|
return self.frequencies
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Set modulations.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param #table Modulations Modulations. Can also be given as a #number if only one modulation should be used.
|
||||||
|
-- @return #MSRS self
|
||||||
|
function MSRS:SetModulations(Modulations)
|
||||||
|
|
||||||
|
-- Ensure table.
|
||||||
|
if type(Modulations)~="table" then
|
||||||
|
Modulations={Modulations}
|
||||||
|
end
|
||||||
|
|
||||||
|
self.modulations=Modulations
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get modulations.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param #table Modulations.
|
||||||
|
function MSRS:GetModulations()
|
||||||
|
return self.modulations
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set gender.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param #string Gender Gender: "male" or "female" (default).
|
||||||
|
-- @return #MSRS self
|
||||||
|
function MSRS:SetGender(Gender)
|
||||||
|
|
||||||
|
Gender=Gender or "female"
|
||||||
|
|
||||||
|
self.gender=Gender:lower()
|
||||||
|
|
||||||
|
-- Debug output.
|
||||||
|
self:T("Setting gender to "..tostring(self.gender))
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set culture.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param #string Culture Culture, e.g. "en-GB" (default).
|
||||||
|
-- @return #MSRS self
|
||||||
|
function MSRS:SetCulture(Culture)
|
||||||
|
|
||||||
|
self.culture=Culture
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set to use a specific voice. Will override gender and culture settings.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param #string Voice Voice.
|
||||||
|
-- @return #MSRS self
|
||||||
|
function MSRS:SetVoice(Voice)
|
||||||
|
|
||||||
|
self.voice=Voice
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the coordinate from which the transmissions will be broadcasted.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param Core.Point#COORDINATE Coordinate Origin of the transmission.
|
||||||
|
-- @return #MSRS self
|
||||||
|
function MSRS:SetCoordinate(Coordinate)
|
||||||
|
|
||||||
|
self.coordinate=Coordinate
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Use google text-to-speech.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param PathToCredentials Full path to the google credentials JSON file, e.g. "C:\Users\username\Downloads\service-account-file.json".
|
||||||
|
-- @return #MSRS self
|
||||||
|
function MSRS:SetGoogle(PathToCredentials)
|
||||||
|
|
||||||
|
self.google=PathToCredentials
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Print SRS STTS help to DCS log file.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @return #MSRS self
|
||||||
|
function MSRS:Help()
|
||||||
|
|
||||||
|
-- Path and exe.
|
||||||
|
local path=self:GetPath() or STTS.DIRECTORY
|
||||||
|
local exe=STTS.EXECUTABLE or "DCS-SR-ExternalAudio.exe"
|
||||||
|
|
||||||
|
-- Text file for output.
|
||||||
|
local filename = os.getenv('TMP') .. "\\MSRS-help-"..STTS.uuid()..".txt"
|
||||||
|
|
||||||
|
-- Print help.
|
||||||
|
local command=string.format("%s/%s --help > %s", path, exe, filename)
|
||||||
|
os.execute(command)
|
||||||
|
|
||||||
|
local f=assert(io.open(filename, "rb"))
|
||||||
|
local data=f:read("*all")
|
||||||
|
f:close()
|
||||||
|
|
||||||
|
-- Print to log file.
|
||||||
|
env.info("SRS STTS help output:")
|
||||||
|
env.info("======================================================================")
|
||||||
|
env.info(data)
|
||||||
|
env.info("======================================================================")
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Transmission Functions
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Play sound file (ogg or mp3) via SRS.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param Sound.SoundFile#SOUNDFILE Soundfile Sound file to play.
|
||||||
|
-- @param #number Delay Delay in seconds, before the sound file is played.
|
||||||
|
-- @return #MSRS self
|
||||||
|
function MSRS:PlaySoundFile(Soundfile, Delay)
|
||||||
|
|
||||||
|
if Delay and Delay>0 then
|
||||||
|
self:ScheduleOnce(Delay, MSRS.PlaySoundFile, self, Soundfile, 0)
|
||||||
|
else
|
||||||
|
|
||||||
|
-- Sound file name.
|
||||||
|
local soundfile=Soundfile:GetName()
|
||||||
|
|
||||||
|
-- Get command.
|
||||||
|
local command=self:_GetCommand()
|
||||||
|
|
||||||
|
-- Append file.
|
||||||
|
command=command.." --file="..tostring(soundfile)
|
||||||
|
|
||||||
|
self:_ExecCommand(command)
|
||||||
|
|
||||||
|
--[[
|
||||||
|
|
||||||
|
command=command.." > bla.txt"
|
||||||
|
|
||||||
|
-- Debug output.
|
||||||
|
self:I(string.format("MSRS PlaySoundfile command=%s", command))
|
||||||
|
|
||||||
|
-- Execute SRS command.
|
||||||
|
local x=os.execute(command)
|
||||||
|
|
||||||
|
]]
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Play a SOUNDTEXT text-to-speech object.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param Sound.SoundFile#SOUNDTEXT SoundText Sound text.
|
||||||
|
-- @param #number Delay Delay in seconds, before the sound file is played.
|
||||||
|
-- @return #MSRS self
|
||||||
|
function MSRS:PlaySoundText(SoundText, Delay)
|
||||||
|
|
||||||
|
if Delay and Delay>0 then
|
||||||
|
self:ScheduleOnce(Delay, MSRS.PlaySoundText, self, SoundText, 0)
|
||||||
|
else
|
||||||
|
|
||||||
|
-- Get command.
|
||||||
|
local command=self:_GetCommand(nil, nil, nil, SoundText.gender, SoundText.voice, SoundText.culture, SoundText.volume, SoundText.speed)
|
||||||
|
|
||||||
|
-- Append text.
|
||||||
|
command=command..string.format(" --text=\"%s\"", tostring(SoundText.text))
|
||||||
|
|
||||||
|
-- Execute command.
|
||||||
|
self:_ExecCommand(command)
|
||||||
|
|
||||||
|
--[[
|
||||||
|
command=command.." > bla.txt"
|
||||||
|
|
||||||
|
-- Debug putput.
|
||||||
|
self:I(string.format("MSRS PlaySoundfile command=%s", command))
|
||||||
|
|
||||||
|
-- Execute SRS command.
|
||||||
|
local x=os.execute(command)
|
||||||
|
]]
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Play text message via STTS.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param #string Text Text message.
|
||||||
|
-- @param #number Delay Delay in seconds, before the message is played.
|
||||||
|
-- @return #MSRS self
|
||||||
|
function MSRS:PlayText(Text, Delay)
|
||||||
|
|
||||||
|
if Delay and Delay>0 then
|
||||||
|
self:ScheduleOnce(Delay, MSRS.PlayText, self, Text, 0)
|
||||||
|
else
|
||||||
|
|
||||||
|
-- Get command line.
|
||||||
|
local command=self:_GetCommand()
|
||||||
|
|
||||||
|
-- Append text.
|
||||||
|
command=command..string.format(" --text=\"%s\"", tostring(Text))
|
||||||
|
|
||||||
|
-- Execute command.
|
||||||
|
self:_ExecCommand(command)
|
||||||
|
|
||||||
|
--[[
|
||||||
|
|
||||||
|
-- Check that length of command is max 255 chars or os.execute() will not work!
|
||||||
|
if string.len(command)>255 then
|
||||||
|
|
||||||
|
-- Create a tmp file.
|
||||||
|
local filename = os.getenv('TMP') .. "\\MSRS-"..STTS.uuid()..".bat"
|
||||||
|
|
||||||
|
local script = io.open(filename, "w+")
|
||||||
|
script:write(command.." && exit")
|
||||||
|
script:close()
|
||||||
|
|
||||||
|
-- Play command.
|
||||||
|
command=string.format("\"%s\"", filename)
|
||||||
|
|
||||||
|
-- Play file in 0.05 seconds
|
||||||
|
timer.scheduleFunction(os.execute, command, timer.getTime()+0.05)
|
||||||
|
|
||||||
|
-- Remove file in 1 second.
|
||||||
|
timer.scheduleFunction(os.remove, filename, timer.getTime()+1)
|
||||||
|
else
|
||||||
|
|
||||||
|
-- Debug output.
|
||||||
|
self:I(string.format("MSRS Text command=%s", command))
|
||||||
|
|
||||||
|
-- Execute SRS command.
|
||||||
|
local x=os.execute(command)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
]]
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Play text file via STTS.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param #string TextFile Full path to the file.
|
||||||
|
-- @param #number Delay Delay in seconds, before the message is played.
|
||||||
|
-- @return #MSRS self
|
||||||
|
function MSRS:PlayTextFile(TextFile, Delay)
|
||||||
|
|
||||||
|
if Delay and Delay>0 then
|
||||||
|
self:ScheduleOnce(Delay, MSRS.PlayTextFile, self, TextFile, 0)
|
||||||
|
else
|
||||||
|
|
||||||
|
-- First check if text file exists!
|
||||||
|
local exists=UTILS.FileExists(TextFile)
|
||||||
|
if not exists then
|
||||||
|
self:E("ERROR: MSRS Text file does not exist! File="..tostring(TextFile))
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get command line.
|
||||||
|
local command=self:_GetCommand()
|
||||||
|
|
||||||
|
-- Append text file.
|
||||||
|
command=command..string.format(" --textFile=\"%s\"", tostring(TextFile))
|
||||||
|
|
||||||
|
-- Debug output.
|
||||||
|
self:T(string.format("MSRS TextFile command=%s", command))
|
||||||
|
|
||||||
|
-- Count length of command.
|
||||||
|
local l=string.len(command)
|
||||||
|
|
||||||
|
-- Execute command.
|
||||||
|
self:_ExecCommand(command)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Misc Functions
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Execute SRS command to play sound using the `DCS-SR-ExternalAudio.exe`.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param #string command Command to executer
|
||||||
|
-- @return #number Return value of os.execute() command.
|
||||||
|
function MSRS:_ExecCommand(command)
|
||||||
|
|
||||||
|
-- Create a tmp file.
|
||||||
|
local filename=os.getenv('TMP').."\\MSRS-"..STTS.uuid()..".bat"
|
||||||
|
|
||||||
|
local script=io.open(filename, "w+")
|
||||||
|
script:write(command.." && exit")
|
||||||
|
script:close()
|
||||||
|
|
||||||
|
-- Play command.
|
||||||
|
command=string.format('start /b "" "%s"', filename)
|
||||||
|
|
||||||
|
local res=nil
|
||||||
|
if true then
|
||||||
|
|
||||||
|
-- Create a tmp file.
|
||||||
|
local filenvbs = os.getenv('TMP') .. "\\MSRS-"..STTS.uuid()..".vbs"
|
||||||
|
|
||||||
|
-- VBS script
|
||||||
|
local script = io.open(filenvbs, "w+")
|
||||||
|
script:write(string.format('Dim WinScriptHost\n'))
|
||||||
|
script:write(string.format('Set WinScriptHost = CreateObject("WScript.Shell")\n'))
|
||||||
|
script:write(string.format('WinScriptHost.Run Chr(34) & "%s" & Chr(34), 0\n', filename))
|
||||||
|
script:write(string.format('Set WinScriptHost = Nothing'))
|
||||||
|
script:close()
|
||||||
|
|
||||||
|
-- Run visual basic script. This still pops up a window but very briefly and does not put the DCS window out of focus.
|
||||||
|
local runvbs=string.format('cscript.exe //Nologo //B "%s"', filenvbs)
|
||||||
|
|
||||||
|
-- Debug output.
|
||||||
|
self:T("MSRS execute command="..command)
|
||||||
|
self:T("MSRS execute VBS command="..runvbs)
|
||||||
|
|
||||||
|
-- Play file in 0.01 seconds
|
||||||
|
res=os.execute(runvbs)
|
||||||
|
|
||||||
|
-- Remove file in 1 second.
|
||||||
|
timer.scheduleFunction(os.remove, filename, timer.getTime()+1)
|
||||||
|
timer.scheduleFunction(os.remove, filenvbs, timer.getTime()+1)
|
||||||
|
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
-- Debug output.
|
||||||
|
self:T("MSRS execute command="..command)
|
||||||
|
|
||||||
|
-- Execute command
|
||||||
|
res=os.execute(command)
|
||||||
|
|
||||||
|
-- Remove file in 1 second.
|
||||||
|
timer.scheduleFunction(os.remove, filename, timer.getTime()+1)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
return res
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get lat, long and alt from coordinate.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param Core.Point#Coordinate Coordinate Coordinate. Can also be a DCS#Vec3.
|
||||||
|
-- @return #number Latitude.
|
||||||
|
-- @return #number Longitude.
|
||||||
|
-- @return #number Altitude.
|
||||||
|
function MSRS:_GetLatLongAlt(Coordinate)
|
||||||
|
|
||||||
|
local lat, lon, alt=coord.LOtoLL(Coordinate)
|
||||||
|
|
||||||
|
return lat, lon, math.floor(alt)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Get SRS command to play sound using the `DCS-SR-ExternalAudio.exe`.
|
||||||
|
-- @param #MSRS self
|
||||||
|
-- @param #table freqs Frequencies in MHz.
|
||||||
|
-- @param #table modus Modulations.
|
||||||
|
-- @param #number coal Coalition.
|
||||||
|
-- @param #string gender Gender.
|
||||||
|
-- @param #string voice Voice.
|
||||||
|
-- @param #string culture Culture.
|
||||||
|
-- @param #number volume Volume.
|
||||||
|
-- @param #number speed Speed.
|
||||||
|
-- @param #number port Port.
|
||||||
|
-- @return #string Command.
|
||||||
|
function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, speed, port)
|
||||||
|
|
||||||
|
local path=self:GetPath() or STTS.DIRECTORY
|
||||||
|
local exe=STTS.EXECUTABLE or "DCS-SR-ExternalAudio.exe"
|
||||||
|
freqs=table.concat(freqs or self.frequencies, ",")
|
||||||
|
modus=table.concat(modus or self.modulations, ",")
|
||||||
|
coal=coal or self.coalition
|
||||||
|
gender=gender or self.gender
|
||||||
|
voice=voice or self.voice
|
||||||
|
culture=culture or self.culture
|
||||||
|
volume=volume or self.volume
|
||||||
|
speed=speed or self.speed
|
||||||
|
port=port or self.port
|
||||||
|
|
||||||
|
-- Replace modulation
|
||||||
|
modus=modus:gsub("0", "AM")
|
||||||
|
modus=modus:gsub("1", "FM")
|
||||||
|
|
||||||
|
-- This did not work well. Stopped if the transmission was a bit longer with no apparent error.
|
||||||
|
--local command=string.format("%s --freqs=%s --modulations=%s --coalition=%d --port=%d --volume=%.2f --speed=%d", exe, freqs, modus, coal, port, volume, speed)
|
||||||
|
|
||||||
|
-- Command from orig STTS script. Works better for some unknown reason!
|
||||||
|
local command=string.format("start /min \"\" /d \"%s\" /b \"%s\" -f %s -m %s -c %s -p %s -n \"%s\" -h", path, exe, freqs, modus, coal, port, "ROBOT")
|
||||||
|
|
||||||
|
--local command=string.format('start /b "" /d "%s" "%s" -f %s -m %s -c %s -p %s -n "%s" > bla.txt', path, exe, freqs, modus, coal, port, "ROBOT")
|
||||||
|
|
||||||
|
-- Command.
|
||||||
|
local command=string.format('%s/%s -f %s -m %s -c %s -p %s -n "%s"', path, exe, freqs, modus, coal, port, "ROBOT")
|
||||||
|
|
||||||
|
-- Set voice or gender/culture.
|
||||||
|
if voice then
|
||||||
|
-- Use a specific voice (no need for gender and/or culture.
|
||||||
|
command=command..string.format(" --voice=\"%s\"", tostring(voice))
|
||||||
|
else
|
||||||
|
-- Add gender.
|
||||||
|
if gender and gender~="female" then
|
||||||
|
command=command..string.format(" --gender=%s", tostring(gender))
|
||||||
|
end
|
||||||
|
-- Add culture.
|
||||||
|
if culture and culture~="en-GB" then
|
||||||
|
command=command..string.format(" -l %s", tostring(culture))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set coordinate.
|
||||||
|
if self.coordinate then
|
||||||
|
local lat,lon,alt=self:_GetLatLongAlt(self.coordinate)
|
||||||
|
command=command..string.format(" -L %.4f -O %.4f -A %d", lat, lon, alt)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set google.
|
||||||
|
if self.google then
|
||||||
|
command=command..string.format(' --ssml -G "%s"', self.google)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Debug output.
|
||||||
|
self:T("MSRS command="..command)
|
||||||
|
|
||||||
|
return command
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
408
Moose Development/Moose/Sound/SoundOutput.lua
Normal file
408
Moose Development/Moose/Sound/SoundOutput.lua
Normal file
@@ -0,0 +1,408 @@
|
|||||||
|
--- **Sound** - Sound output classes.
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ## Features:
|
||||||
|
--
|
||||||
|
-- * Create a SOUNDFILE object (mp3 or ogg) to be played via DCS or SRS transmissions
|
||||||
|
-- * Create a SOUNDTEXT object for text-to-speech output vis SRS Simple-Text-To-Speech (STTS)
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ### Author: **funkyfranky**
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- There are two classes, SOUNDFILE and SOUNDTEXT, defined in this section that deal with playing
|
||||||
|
-- sound files or arbitrary text (via SRS Simple-Text-To-Speech), respectively.
|
||||||
|
--
|
||||||
|
-- The SOUNDFILE and SOUNDTEXT objects can be defined and used in other MOOSE classes.
|
||||||
|
--
|
||||||
|
--
|
||||||
|
-- @module Sound.SoundOutput
|
||||||
|
-- @image Sound_SoundOutput.png
|
||||||
|
|
||||||
|
do -- Sound Base
|
||||||
|
|
||||||
|
--- @type SOUNDBASE
|
||||||
|
-- @field #string ClassName Name of the class.
|
||||||
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
|
|
||||||
|
--- Basic sound output inherited by other classes suche as SOUNDFILE and SOUNDTEXT.
|
||||||
|
--
|
||||||
|
-- This class is **not** meant to be used by "ordinary" users.
|
||||||
|
--
|
||||||
|
-- @field #SOUNDBASE
|
||||||
|
SOUNDBASE={
|
||||||
|
ClassName = "SOUNDBASE",
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Constructor to create a new SOUNDBASE object.
|
||||||
|
-- @param #SOUNDBASE self
|
||||||
|
-- @return #SOUNDBASE self
|
||||||
|
function SOUNDBASE:New()
|
||||||
|
|
||||||
|
-- Inherit BASE.
|
||||||
|
local self=BASE:Inherit(self, BASE:New()) -- #SOUNDBASE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Function returns estimated speech time in seconds.
|
||||||
|
-- Assumptions for time calc: 100 Words per min, avarage of 5 letters for english word so
|
||||||
|
--
|
||||||
|
-- * 5 chars * 100wpm = 500 characters per min = 8.3 chars per second
|
||||||
|
--
|
||||||
|
-- So lengh of msg / 8.3 = number of seconds needed to read it. rounded down to 8 chars per sec map function:
|
||||||
|
--
|
||||||
|
-- * (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
|
||||||
|
--
|
||||||
|
-- @param #string Text The text string to analyze.
|
||||||
|
-- @param #number Speed Speed factor. Default 1.
|
||||||
|
-- @param #boolean isGoogle If true, google text-to-speech is used.
|
||||||
|
function SOUNDBASE:GetSpeechTime(length,speed,isGoogle)
|
||||||
|
|
||||||
|
local maxRateRatio = 3
|
||||||
|
|
||||||
|
speed = speed or 1.0
|
||||||
|
isGoogle = isGoogle or false
|
||||||
|
|
||||||
|
local speedFactor = 1.0
|
||||||
|
if isGoogle then
|
||||||
|
speedFactor = speed
|
||||||
|
else
|
||||||
|
if speed ~= 0 then
|
||||||
|
speedFactor = math.abs(speed) * (maxRateRatio - 1) / 10 + 1
|
||||||
|
end
|
||||||
|
if speed < 0 then
|
||||||
|
speedFactor = 1/speedFactor
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Words per minute.
|
||||||
|
local wpm = math.ceil(100 * speedFactor)
|
||||||
|
|
||||||
|
-- Characters per second.
|
||||||
|
local cps = math.floor((wpm * 5)/60)
|
||||||
|
|
||||||
|
if type(length) == "string" then
|
||||||
|
length = string.len(length)
|
||||||
|
end
|
||||||
|
|
||||||
|
return math.ceil(length/cps)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
do -- Sound File
|
||||||
|
|
||||||
|
--- @type SOUNDFILE
|
||||||
|
-- @field #string ClassName Name of the class
|
||||||
|
-- @field #string filename Name of the flag.
|
||||||
|
-- @field #string path Directory path, where the sound file is located. This includes the final slash "/".
|
||||||
|
-- @field #string duration Duration of the sound file in seconds.
|
||||||
|
-- @field #string subtitle Subtitle of the transmission.
|
||||||
|
-- @field #number subduration Duration in seconds how long the subtitle is displayed.
|
||||||
|
-- @field #boolean useSRS If true, sound file is played via SRS. Sound file needs to be on local disk not inside the miz file!
|
||||||
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
|
|
||||||
|
--- Sound files used by other classes.
|
||||||
|
--
|
||||||
|
-- # The SOUNDFILE Concept
|
||||||
|
--
|
||||||
|
-- A SOUNDFILE object hold the important properties that are necessary to play the sound file, e.g. its file name, path, duration.
|
||||||
|
--
|
||||||
|
-- It can be created with the @{#SOUNDFILE.New}(*FileName*, *Path*, *Duration*) function:
|
||||||
|
--
|
||||||
|
-- local soundfile=SOUNDFILE:New("My Soundfile.ogg", "Sound File/", 3.5)
|
||||||
|
--
|
||||||
|
-- ## SRS
|
||||||
|
--
|
||||||
|
-- If sound files are supposed to be played via SRS, you need to use the @{#SOUNDFILE.SetPlayWithSRS}() function.
|
||||||
|
--
|
||||||
|
-- # Location/Path
|
||||||
|
--
|
||||||
|
-- ## DCS
|
||||||
|
--
|
||||||
|
-- DCS can only play sound files that are located inside the mission (.miz) file. In particular, DCS cannot make use of files that are stored on
|
||||||
|
-- your hard drive.
|
||||||
|
--
|
||||||
|
-- The default location where sound files are stored in DCS is the directory "l10n/DEFAULT/". This is where sound files are placed, if they are
|
||||||
|
-- added via the mission editor (TRIGGERS-->ACTIONS-->SOUND TO ALL). Note however, that sound files which are not added with a trigger command,
|
||||||
|
-- will be deleted each time the mission is saved! Therefore, this directory is not ideal to be used especially if many sound files are to
|
||||||
|
-- be included since for each file a trigger action needs to be created. Which is cumbersome, to say the least.
|
||||||
|
--
|
||||||
|
-- The recommended way is to create a new folder inside the mission (.miz) file (a miz file is essentially zip file and can be opened, e.g., with 7-Zip)
|
||||||
|
-- and to place the sound files in there. Sound files in these folders are not wiped out by DCS on the next save.
|
||||||
|
--
|
||||||
|
-- ## SRS
|
||||||
|
--
|
||||||
|
-- SRS sound files need to be located on your local drive (not inside the miz). Therefore, you need to specify the full path.
|
||||||
|
--
|
||||||
|
-- @field #SOUNDFILE
|
||||||
|
SOUNDFILE={
|
||||||
|
ClassName = "SOUNDFILE",
|
||||||
|
filename = nil,
|
||||||
|
path = "l10n/DEFAULT/",
|
||||||
|
duration = 3,
|
||||||
|
subtitle = nil,
|
||||||
|
subduration = 0,
|
||||||
|
useSRS = false,
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Constructor to create a new SOUNDFILE object.
|
||||||
|
-- @param #SOUNDFILE self
|
||||||
|
-- @param #string FileName The name of the sound file, e.g. "Hello World.ogg".
|
||||||
|
-- @param #string Path The path of the directory, where the sound file is located. Default is "l10n/DEFAULT/" within the miz file.
|
||||||
|
-- @param #number Duration Duration in seconds, how long it takes to play the sound file. Default is 3 seconds.
|
||||||
|
-- @return #SOUNDFILE self
|
||||||
|
function SOUNDFILE:New(FileName, Path, Duration)
|
||||||
|
|
||||||
|
-- Inherit BASE.
|
||||||
|
local self=BASE:Inherit(self, BASE:New()) -- #SOUNDFILE
|
||||||
|
|
||||||
|
-- Set file name.
|
||||||
|
self:SetFileName(FileName)
|
||||||
|
|
||||||
|
-- Set path.
|
||||||
|
self:SetPath(Path)
|
||||||
|
|
||||||
|
-- Set duration.
|
||||||
|
self:SetDuration(Duration)
|
||||||
|
|
||||||
|
-- Debug info:
|
||||||
|
self:T(string.format("New SOUNDFILE: file name=%s, path=%s", self.filename, self.path))
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set path, where the sound file is located.
|
||||||
|
-- @param #SOUNDFILE self
|
||||||
|
-- @param #string Path Path to the directory, where the sound file is located.
|
||||||
|
-- @return #SOUNDFILE self
|
||||||
|
function SOUNDFILE:SetPath(Path)
|
||||||
|
|
||||||
|
-- Init path.
|
||||||
|
self.path=Path or "l10n/DEFAULT/"
|
||||||
|
|
||||||
|
-- Remove (back)slashes.
|
||||||
|
local nmax=1000 ; local n=1
|
||||||
|
while (self.path:sub(-1)=="/" or self.path:sub(-1)==[[\]]) and n<=nmax do
|
||||||
|
self.path=self.path:sub(1,#self.path-1)
|
||||||
|
n=n+1
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Append slash.
|
||||||
|
self.path=self.path.."/"
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get path of the directory, where the sound file is located.
|
||||||
|
-- @param #SOUNDFILE self
|
||||||
|
-- @return #string Path.
|
||||||
|
function SOUNDFILE:GetPath()
|
||||||
|
local path=self.path or "l10n/DEFAULT/"
|
||||||
|
return path
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set sound file name. This must be a .ogg or .mp3 file!
|
||||||
|
-- @param #SOUNDFILE self
|
||||||
|
-- @param #string FileName Name of the file. Default is "Hello World.mp3".
|
||||||
|
-- @return #SOUNDFILE self
|
||||||
|
function SOUNDFILE:SetFileName(FileName)
|
||||||
|
--TODO: check that sound file is really .ogg or .mp3
|
||||||
|
self.filename=FileName or "Hello World.mp3"
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the sound file name.
|
||||||
|
-- @param #SOUNDFILE self
|
||||||
|
-- @return #string Name of the soud file. This does *not* include its path.
|
||||||
|
function SOUNDFILE:GetFileName()
|
||||||
|
return self.filename
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Set duration how long it takes to play the sound file.
|
||||||
|
-- @param #SOUNDFILE self
|
||||||
|
-- @param #string Duration Duration in seconds. Default 3 seconds.
|
||||||
|
-- @return #SOUNDFILE self
|
||||||
|
function SOUNDFILE:SetDuration(Duration)
|
||||||
|
self.duration=Duration or 3
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get duration how long the sound file takes to play.
|
||||||
|
-- @param #SOUNDFILE self
|
||||||
|
-- @return #number Duration in seconds.
|
||||||
|
function SOUNDFILE:GetDuration()
|
||||||
|
return self.duration or 3
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the complete sound file name inlcuding its path.
|
||||||
|
-- @param #SOUNDFILE self
|
||||||
|
-- @return #string Name of the sound file.
|
||||||
|
function SOUNDFILE:GetName()
|
||||||
|
local path=self:GetPath()
|
||||||
|
local filename=self:GetFileName()
|
||||||
|
local name=string.format("%s%s", path, filename)
|
||||||
|
return name
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set whether sound files should be played via SRS.
|
||||||
|
-- @param #SOUNDFILE self
|
||||||
|
-- @param #boolean Switch If true or nil, use SRS. If false, use DCS transmission.
|
||||||
|
-- @return #SOUNDFILE self
|
||||||
|
function SOUNDFILE:SetPlayWithSRS(Switch)
|
||||||
|
if Switch==true or Switch==nil then
|
||||||
|
self.useSRS=true
|
||||||
|
else
|
||||||
|
self.useSRS=false
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
do -- Text-To-Speech
|
||||||
|
|
||||||
|
--- @type SOUNDTEXT
|
||||||
|
-- @field #string ClassName Name of the class
|
||||||
|
-- @field #string text Text to speak.
|
||||||
|
-- @field #number duration Duration in seconds.
|
||||||
|
-- @field #string gender Gender: "male", "female".
|
||||||
|
-- @field #string culture Culture, e.g. "en-GB".
|
||||||
|
-- @field #string voice Specific voice to use. Overrules `gender` and `culture` settings.
|
||||||
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
|
|
||||||
|
--- Text-to-speech objects for other classes.
|
||||||
|
--
|
||||||
|
-- # The SOUNDTEXT Concept
|
||||||
|
--
|
||||||
|
-- A SOUNDTEXT object holds all necessary information to play a general text via SRS Simple-Text-To-Speech.
|
||||||
|
--
|
||||||
|
-- It can be created with the @{#SOUNDTEXT.New}(*Text*, *Duration*) function.
|
||||||
|
--
|
||||||
|
-- * @{#SOUNDTEXT.New}(*Text, Duration*): Creates a new SOUNDTEXT object.
|
||||||
|
--
|
||||||
|
-- # Options
|
||||||
|
--
|
||||||
|
-- ## Gender
|
||||||
|
--
|
||||||
|
-- You can choose a gender ("male" or "femal") with the @{#SOUNDTEXT.SetGender}(*Gender*) function.
|
||||||
|
-- Note that the gender voice needs to be installed on your windows machine for the used culture (see below).
|
||||||
|
--
|
||||||
|
-- ## Culture
|
||||||
|
--
|
||||||
|
-- You can choose a "culture" (accent) with the @{#SOUNDTEXT.SetCulture}(*Culture*) function, where the default (SRS) culture is "en-GB".
|
||||||
|
--
|
||||||
|
-- Other examples for culture are: "en-US" (US accent), "de-DE" (German), "it-IT" (Italian), "ru-RU" (Russian), "zh-CN" (Chinese).
|
||||||
|
--
|
||||||
|
-- Note that the chosen culture needs to be installed on your windows machine.
|
||||||
|
--
|
||||||
|
-- ## Specific Voice
|
||||||
|
--
|
||||||
|
-- You can use a specific voice for the transmission with the @{SOUNDTEXT.SetVoice}(*VoiceName*) function. Here are some examples
|
||||||
|
--
|
||||||
|
-- * Name: Microsoft Hazel Desktop, Culture: en-GB, Gender: Female, Age: Adult, Desc: Microsoft Hazel Desktop - English (Great Britain)
|
||||||
|
-- * Name: Microsoft David Desktop, Culture: en-US, Gender: Male, Age: Adult, Desc: Microsoft David Desktop - English (United States)
|
||||||
|
-- * Name: Microsoft Zira Desktop, Culture: en-US, Gender: Female, Age: Adult, Desc: Microsoft Zira Desktop - English (United States)
|
||||||
|
-- * Name: Microsoft Hedda Desktop, Culture: de-DE, Gender: Female, Age: Adult, Desc: Microsoft Hedda Desktop - German
|
||||||
|
-- * Name: Microsoft Helena Desktop, Culture: es-ES, Gender: Female, Age: Adult, Desc: Microsoft Helena Desktop - Spanish (Spain)
|
||||||
|
-- * Name: Microsoft Hortense Desktop, Culture: fr-FR, Gender: Female, Age: Adult, Desc: Microsoft Hortense Desktop - French
|
||||||
|
-- * Name: Microsoft Elsa Desktop, Culture: it-IT, Gender: Female, Age: Adult, Desc: Microsoft Elsa Desktop - Italian (Italy)
|
||||||
|
-- * Name: Microsoft Irina Desktop, Culture: ru-RU, Gender: Female, Age: Adult, Desc: Microsoft Irina Desktop - Russian
|
||||||
|
-- * Name: Microsoft Huihui Desktop, Culture: zh-CN, Gender: Female, Age: Adult, Desc: Microsoft Huihui Desktop - Chinese (Simplified)
|
||||||
|
--
|
||||||
|
-- Note that this must be installed on your windos machine. Also note that this overrides any culture and gender settings.
|
||||||
|
--
|
||||||
|
-- @field #SOUNDTEXT
|
||||||
|
SOUNDTEXT={
|
||||||
|
ClassName = "SOUNDTEXT",
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Constructor to create a new SOUNDTEXT object.
|
||||||
|
-- @param #SOUNDTEXT self
|
||||||
|
-- @param #string Text The text to speak.
|
||||||
|
-- @param #number Duration Duration in seconds, how long it takes to play the text. Default is 3 seconds.
|
||||||
|
-- @return #SOUNDTEXT self
|
||||||
|
function SOUNDTEXT:New(Text, Duration)
|
||||||
|
|
||||||
|
-- Inherit BASE.
|
||||||
|
local self=BASE:Inherit(self, BASE:New()) -- #SOUNDTEXT
|
||||||
|
|
||||||
|
self:SetText(Text)
|
||||||
|
self:SetDuration(Duration or STTS.getSpeechTime(Text))
|
||||||
|
--self:SetGender()
|
||||||
|
--self:SetCulture()
|
||||||
|
|
||||||
|
-- Debug info:
|
||||||
|
self:T(string.format("New SOUNDTEXT: text=%s, duration=%.1f sec", self.text, self.duration))
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set text.
|
||||||
|
-- @param #SOUNDTEXT self
|
||||||
|
-- @param #string Text Text to speak. Default "Hello World!".
|
||||||
|
-- @return #SOUNDTEXT self
|
||||||
|
function SOUNDTEXT:SetText(Text)
|
||||||
|
|
||||||
|
self.text=Text or "Hello World!"
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set duration, how long it takes to speak the text.
|
||||||
|
-- @param #SOUNDTEXT self
|
||||||
|
-- @param #number Duration Duration in seconds. Default 3 seconds.
|
||||||
|
-- @return #SOUNDTEXT self
|
||||||
|
function SOUNDTEXT:SetDuration(Duration)
|
||||||
|
|
||||||
|
self.duration=Duration or 3
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set gender.
|
||||||
|
-- @param #SOUNDTEXT self
|
||||||
|
-- @param #string Gender Gender: "male" or "female" (default).
|
||||||
|
-- @return #SOUNDTEXT self
|
||||||
|
function SOUNDTEXT:SetGender(Gender)
|
||||||
|
|
||||||
|
self.gender=Gender or "female"
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set TTS culture - local for the voice.
|
||||||
|
-- @param #SOUNDTEXT self
|
||||||
|
-- @param #string Culture TTS culture. Default "en-GB".
|
||||||
|
-- @return #SOUNDTEXT self
|
||||||
|
function SOUNDTEXT:SetCulture(Culture)
|
||||||
|
|
||||||
|
self.culture=Culture or "en-GB"
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set to use a specific voice name.
|
||||||
|
-- See the list from `DCS-SR-ExternalAudio.exe --help` or if using google see [google voices](https://cloud.google.com/text-to-speech/docs/voices).
|
||||||
|
-- @param #SOUNDTEXT self
|
||||||
|
-- @param #string VoiceName Voice name. Note that this will overrule `Gender` and `Culture`.
|
||||||
|
-- @return #SOUNDTEXT self
|
||||||
|
function SOUNDTEXT:SetVoice(VoiceName)
|
||||||
|
|
||||||
|
self.voice=VoiceName
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
--- **Core** - Manage user sound.
|
--- **Sound** - Manage user sound.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- @module Core.UserSound
|
-- @module Sound.UserSound
|
||||||
-- @image Core_Usersound.JPG
|
-- @image Core_Usersound.JPG
|
||||||
|
|
||||||
do -- UserSound
|
do -- UserSound
|
||||||
@@ -202,6 +202,7 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName )
|
|||||||
self:SetAutoAcceptTasks( true )
|
self:SetAutoAcceptTasks( true )
|
||||||
self:SetAutoAssignMethod( COMMANDCENTER.AutoAssignMethods.Distance )
|
self:SetAutoAssignMethod( COMMANDCENTER.AutoAssignMethods.Distance )
|
||||||
self:SetFlashStatus( false )
|
self:SetFlashStatus( false )
|
||||||
|
self:SetMessageDuration(10)
|
||||||
|
|
||||||
self:HandleEvent( EVENTS.Birth,
|
self:HandleEvent( EVENTS.Birth,
|
||||||
--- @param #COMMANDCENTER self
|
--- @param #COMMANDCENTER self
|
||||||
@@ -682,7 +683,7 @@ end
|
|||||||
-- @param #string Message The message text.
|
-- @param #string Message The message text.
|
||||||
function COMMANDCENTER:MessageToAll( Message )
|
function COMMANDCENTER:MessageToAll( Message )
|
||||||
|
|
||||||
self:GetPositionable():MessageToAll( Message, 20, self:GetName() )
|
self:GetPositionable():MessageToAll( Message, self.MessageDuration, self:GetName() )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -692,7 +693,7 @@ end
|
|||||||
-- @param Wrapper.Group#GROUP MessageGroup The group to receive the message.
|
-- @param Wrapper.Group#GROUP MessageGroup The group to receive the message.
|
||||||
function COMMANDCENTER:MessageToGroup( Message, MessageGroup )
|
function COMMANDCENTER:MessageToGroup( Message, MessageGroup )
|
||||||
|
|
||||||
self:GetPositionable():MessageToGroup( Message, 15, MessageGroup, self:GetShortText() )
|
self:GetPositionable():MessageToGroup( Message, self.MessageDuration, MessageGroup, self:GetShortText() )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -715,7 +716,7 @@ function COMMANDCENTER:MessageToCoalition( Message )
|
|||||||
local CCCoalition = self:GetPositionable():GetCoalition()
|
local CCCoalition = self:GetPositionable():GetCoalition()
|
||||||
--TODO: Fix coalition bug!
|
--TODO: Fix coalition bug!
|
||||||
|
|
||||||
self:GetPositionable():MessageToCoalition( Message, 15, CCCoalition, self:GetShortText() )
|
self:GetPositionable():MessageToCoalition( Message, self.MessageDuration, CCCoalition, self:GetShortText() )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -795,9 +796,18 @@ end
|
|||||||
|
|
||||||
--- Let the command center flash a report of the status of the subscribed task to a group.
|
--- Let the command center flash a report of the status of the subscribed task to a group.
|
||||||
-- @param #COMMANDCENTER self
|
-- @param #COMMANDCENTER self
|
||||||
|
-- @param Flash #boolean
|
||||||
function COMMANDCENTER:SetFlashStatus( Flash )
|
function COMMANDCENTER:SetFlashStatus( Flash )
|
||||||
self:F()
|
self:F()
|
||||||
|
|
||||||
self.FlashStatus = Flash or true
|
self.FlashStatus = Flash and true
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Duration a command center message is shown.
|
||||||
|
-- @param #COMMANDCENTER self
|
||||||
|
-- @param seconds #number
|
||||||
|
function COMMANDCENTER:SetMessageDuration(seconds)
|
||||||
|
self:F()
|
||||||
|
|
||||||
|
self.MessageDuration = 10 or seconds
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -881,6 +881,9 @@ do -- Group Assignment
|
|||||||
local Mission = self:GetMission()
|
local Mission = self:GetMission()
|
||||||
local CommandCenter = Mission:GetCommandCenter()
|
local CommandCenter = Mission:GetCommandCenter()
|
||||||
CommandCenter:SetMenu()
|
CommandCenter:SetMenu()
|
||||||
|
|
||||||
|
self:MenuFlashTaskStatus( TaskGroup, false ) -- stop message flashing, if any #1383 & #1312
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1252,7 +1255,7 @@ function TASK:MenuFlashTaskStatus( TaskGroup, Flash )
|
|||||||
self.FlashTaskStatus = Flash
|
self.FlashTaskStatus = Flash
|
||||||
|
|
||||||
if self.FlashTaskStatus then
|
if self.FlashTaskStatus then
|
||||||
self.FlashTaskScheduler, self.FlashTaskScheduleID = SCHEDULER:New( self, self.MenuTaskStatus, { TaskGroup }, 0, 60 )
|
self.FlashTaskScheduler, self.FlashTaskScheduleID = SCHEDULER:New( self, self.MenuTaskStatus, { TaskGroup }, 0, 60) --Issue #1383 never ending flash messages
|
||||||
else
|
else
|
||||||
if self.FlashTaskScheduler then
|
if self.FlashTaskScheduler then
|
||||||
self.FlashTaskScheduler:Stop( self.FlashTaskScheduleID )
|
self.FlashTaskScheduler:Stop( self.FlashTaskScheduleID )
|
||||||
|
|||||||
@@ -344,7 +344,9 @@ function TASKINFO:Report( Report, Detail, ReportGroup, Task )
|
|||||||
Text = DataText
|
Text = DataText
|
||||||
else
|
else
|
||||||
local DataText = Data.Data -- #string
|
local DataText = Data.Data -- #string
|
||||||
Text = DataText
|
if type(DataText) == "string" then --Issue #1388 - don't just assume this is a string
|
||||||
|
Text = DataText
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if Line < math.floor( Data.Order / 10 ) then
|
if Line < math.floor( Data.Order / 10 ) then
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ do -- TASK_A2A
|
|||||||
--
|
--
|
||||||
-- @field #TASK_A2A
|
-- @field #TASK_A2A
|
||||||
TASK_A2A = {
|
TASK_A2A = {
|
||||||
ClassName = "TASK_A2A",
|
ClassName = "TASK_A2A"
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Instantiates a new TASK_A2A.
|
--- Instantiates a new TASK_A2A.
|
||||||
@@ -60,29 +60,27 @@ do -- TASK_A2A
|
|||||||
|
|
||||||
local Fsm = self:GetUnitProcess()
|
local Fsm = self:GetUnitProcess()
|
||||||
|
|
||||||
|
|
||||||
Fsm:AddTransition( "Assigned", "RouteToRendezVous", "RoutingToRendezVous" )
|
Fsm:AddTransition( "Assigned", "RouteToRendezVous", "RoutingToRendezVous" )
|
||||||
Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtRendezVous" } )
|
Fsm:AddProcess( "RoutingToRendezVous", "RouteToRendezVousPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtRendezVous" } )
|
||||||
Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtRendezVous" } )
|
Fsm:AddProcess( "RoutingToRendezVous", "RouteToRendezVousZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtRendezVous" } )
|
||||||
|
|
||||||
Fsm:AddTransition( { "Arrived", "RoutingToRendezVous" }, "ArriveAtRendezVous", "ArrivedAtRendezVous" )
|
Fsm:AddTransition( { "Arrived", "RoutingToRendezVous" }, "ArriveAtRendezVous", "ArrivedAtRendezVous" )
|
||||||
|
|
||||||
Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "Engage", "Engaging" )
|
Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "Engage", "Engaging" )
|
||||||
Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "HoldAtRendezVous", "HoldingAtRendezVous" )
|
Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "HoldAtRendezVous", "HoldingAtRendezVous" )
|
||||||
|
|
||||||
Fsm:AddProcess ( "Engaging", "Account", ACT_ACCOUNT_DEADS:New(), {} )
|
Fsm:AddProcess( "Engaging", "Account", ACT_ACCOUNT_DEADS:New(), {} )
|
||||||
Fsm:AddTransition( "Engaging", "RouteToTarget", "Engaging" )
|
Fsm:AddTransition( "Engaging", "RouteToTarget", "Engaging" )
|
||||||
Fsm:AddProcess( "Engaging", "RouteToTargetZone", ACT_ROUTE_ZONE:New(), {} )
|
Fsm:AddProcess( "Engaging", "RouteToTargetZone", ACT_ROUTE_ZONE:New(), {} )
|
||||||
Fsm:AddProcess( "Engaging", "RouteToTargetPoint", ACT_ROUTE_POINT:New(), {} )
|
Fsm:AddProcess( "Engaging", "RouteToTargetPoint", ACT_ROUTE_POINT:New(), {} )
|
||||||
Fsm:AddTransition( "Engaging", "RouteToTargets", "Engaging" )
|
Fsm:AddTransition( "Engaging", "RouteToTargets", "Engaging" )
|
||||||
|
|
||||||
-- Fsm:AddTransition( "Accounted", "DestroyedAll", "Accounted" )
|
-- Fsm:AddTransition( "Accounted", "DestroyedAll", "Accounted" )
|
||||||
-- Fsm:AddTransition( "Accounted", "Success", "Success" )
|
-- Fsm:AddTransition( "Accounted", "Success", "Success" )
|
||||||
Fsm:AddTransition( "Rejected", "Reject", "Aborted" )
|
Fsm:AddTransition( "Rejected", "Reject", "Aborted" )
|
||||||
Fsm:AddTransition( "Failed", "Fail", "Failed" )
|
Fsm:AddTransition( "Failed", "Fail", "Failed" )
|
||||||
|
|
||||||
|
-- @param #FSM_PROCESS self
|
||||||
---- @param #FSM_PROCESS self
|
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
-- @param #TASK_CARGO Task
|
-- @param #TASK_CARGO Task
|
||||||
function Fsm:OnLeaveAssigned( TaskUnit, Task )
|
function Fsm:OnLeaveAssigned( TaskUnit, Task )
|
||||||
@@ -128,7 +126,7 @@ do -- TASK_A2A
|
|||||||
function Fsm:onafterEngage( TaskUnit, Task )
|
function Fsm:onafterEngage( TaskUnit, Task )
|
||||||
self:F( { self } )
|
self:F( { self } )
|
||||||
self:__Account( 0.1 )
|
self:__Account( 0.1 )
|
||||||
self:__RouteToTarget(0.1 )
|
self:__RouteToTarget( 0.1 )
|
||||||
self:__RouteToTargets( -10 )
|
self:__RouteToTargets( -10 )
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -177,8 +175,6 @@ do -- TASK_A2A
|
|||||||
self.TargetSetUnit = TargetSetUnit
|
self.TargetSetUnit = TargetSetUnit
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- @param #TASK_A2A self
|
--- @param #TASK_A2A self
|
||||||
function TASK_A2A:GetPlannedMenuText()
|
function TASK_A2A:GetPlannedMenuText()
|
||||||
return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )"
|
return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )"
|
||||||
@@ -188,7 +184,7 @@ do -- TASK_A2A
|
|||||||
-- @param Core.Point#COORDINATE RendezVousCoordinate The Coordinate object referencing to the 2D point where the RendezVous point is located on the map.
|
-- @param Core.Point#COORDINATE RendezVousCoordinate The Coordinate object referencing to the 2D point where the RendezVous point is located on the map.
|
||||||
-- @param #number RendezVousRange The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point.
|
-- @param #number RendezVousRange The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point.
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
function TASK_A2A:SetRendezVousCoordinate( RendezVousCoordinate, RendezVousRange, TaskUnit )
|
function TASK_A2A:SetRendezVousCoordinate( RendezVousCoordinate, RendezVousRange, TaskUnit )
|
||||||
|
|
||||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||||
|
|
||||||
@@ -209,8 +205,6 @@ do -- TASK_A2A
|
|||||||
return ActRouteRendezVous:GetCoordinate(), ActRouteRendezVous:GetRange()
|
return ActRouteRendezVous:GetCoordinate(), ActRouteRendezVous:GetRange()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- @param #TASK_A2A self
|
--- @param #TASK_A2A self
|
||||||
-- @param Core.Zone#ZONE_BASE RendezVousZone The Zone object where the RendezVous is located on the map.
|
-- @param Core.Zone#ZONE_BASE RendezVousZone The Zone object where the RendezVous is located on the map.
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
@@ -244,7 +238,6 @@ do -- TASK_A2A
|
|||||||
ActRouteTarget:SetCoordinate( TargetCoordinate )
|
ActRouteTarget:SetCoordinate( TargetCoordinate )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- @param #TASK_A2A self
|
--- @param #TASK_A2A self
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
-- @return Core.Point#COORDINATE The Coordinate object where the Target is located on the map.
|
-- @return Core.Point#COORDINATE The Coordinate object where the Target is located on the map.
|
||||||
@@ -256,7 +249,6 @@ do -- TASK_A2A
|
|||||||
return ActRouteTarget:GetCoordinate()
|
return ActRouteTarget:GetCoordinate()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- @param #TASK_A2A self
|
--- @param #TASK_A2A self
|
||||||
-- @param Core.Zone#ZONE_BASE TargetZone The Zone object where the Target is located on the map.
|
-- @param Core.Zone#ZONE_BASE TargetZone The Zone object where the Target is located on the map.
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
@@ -268,7 +260,6 @@ do -- TASK_A2A
|
|||||||
ActRouteTarget:SetZone( TargetZone, Altitude, Heading )
|
ActRouteTarget:SetZone( TargetZone, Altitude, Heading )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- @param #TASK_A2A self
|
--- @param #TASK_A2A self
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
-- @return Core.Zone#ZONE_BASE The Zone object where the Target is located on the map.
|
-- @return Core.Zone#ZONE_BASE The Zone object where the Target is located on the map.
|
||||||
@@ -293,7 +284,7 @@ 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.
|
--- 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
|
-- @param #TASK_A2A self
|
||||||
function TASK_A2A:ReportOrder( ReportGroup )
|
function TASK_A2A:ReportOrder( ReportGroup )
|
||||||
self:UpdateTaskInfo( self.DetectedItem )
|
self:UpdateTaskInfo( self.DetectedItem )
|
||||||
|
|
||||||
local Coordinate = self.TaskInfo:GetData( "Coordinate" )
|
local Coordinate = self.TaskInfo:GetData( "Coordinate" )
|
||||||
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
|
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
|
||||||
@@ -301,7 +292,6 @@ do -- TASK_A2A
|
|||||||
return Distance
|
return Distance
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- This method checks every 10 seconds if the goal has been reached of the task.
|
--- This method checks every 10 seconds if the goal has been reached of the task.
|
||||||
-- @param #TASK_A2A self
|
-- @param #TASK_A2A self
|
||||||
function TASK_A2A:onafterGoal( TaskUnit, From, Event, To )
|
function TASK_A2A:onafterGoal( TaskUnit, From, Event, To )
|
||||||
@@ -314,7 +304,6 @@ do -- TASK_A2A
|
|||||||
self:__Goal( -10 )
|
self:__Goal( -10 )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- @param #TASK_A2A self
|
--- @param #TASK_A2A self
|
||||||
function TASK_A2A:UpdateTaskInfo( DetectedItem )
|
function TASK_A2A:UpdateTaskInfo( DetectedItem )
|
||||||
|
|
||||||
@@ -360,7 +349,7 @@ do -- TASK_A2A
|
|||||||
-- @param Wrapper.Group#GROUP TaskGroup The player group.
|
-- @param Wrapper.Group#GROUP TaskGroup The player group.
|
||||||
function TASK_A2A:GetAutoAssignPriority( AutoAssignMethod, CommandCenter, TaskGroup )
|
function TASK_A2A:GetAutoAssignPriority( AutoAssignMethod, CommandCenter, TaskGroup )
|
||||||
|
|
||||||
if AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Random then
|
if AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Random then
|
||||||
return math.random( 1, 9 )
|
return math.random( 1, 9 )
|
||||||
elseif AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Distance then
|
elseif AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Distance then
|
||||||
local Coordinate = self.TaskInfo:GetData( "Coordinate" )
|
local Coordinate = self.TaskInfo:GetData( "Coordinate" )
|
||||||
@@ -375,7 +364,6 @@ do -- TASK_A2A
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
do -- TASK_A2A_INTERCEPT
|
do -- TASK_A2A_INTERCEPT
|
||||||
|
|
||||||
--- The TASK_A2A_INTERCEPT class
|
--- The TASK_A2A_INTERCEPT class
|
||||||
@@ -384,7 +372,7 @@ do -- TASK_A2A_INTERCEPT
|
|||||||
-- @extends Tasking.Task#TASK
|
-- @extends Tasking.Task#TASK
|
||||||
|
|
||||||
--- Defines an intercept task for a human player to be executed.
|
--- Defines an intercept task for a human player to be executed.
|
||||||
-- When enemy planes need to be intercepted by human players, use this task type to urgen the players to get out there!
|
-- When enemy planes need to be intercepted by human players, use this task type to urge the players to get out there!
|
||||||
--
|
--
|
||||||
-- The TASK_A2A_INTERCEPT is used by the @{Tasking.Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create intercept tasks
|
-- The TASK_A2A_INTERCEPT is used by the @{Tasking.Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create intercept tasks
|
||||||
-- based on detected airborne enemy targets intruding friendly airspace.
|
-- based on detected airborne enemy targets intruding friendly airspace.
|
||||||
@@ -394,11 +382,9 @@ do -- TASK_A2A_INTERCEPT
|
|||||||
--
|
--
|
||||||
-- @field #TASK_A2A_INTERCEPT
|
-- @field #TASK_A2A_INTERCEPT
|
||||||
TASK_A2A_INTERCEPT = {
|
TASK_A2A_INTERCEPT = {
|
||||||
ClassName = "TASK_A2A_INTERCEPT",
|
ClassName = "TASK_A2A_INTERCEPT"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- Instantiates a new TASK_A2A_INTERCEPT.
|
--- Instantiates a new TASK_A2A_INTERCEPT.
|
||||||
-- @param #TASK_A2A_INTERCEPT self
|
-- @param #TASK_A2A_INTERCEPT self
|
||||||
-- @param Tasking.Mission#MISSION Mission
|
-- @param Tasking.Mission#MISSION Mission
|
||||||
@@ -413,15 +399,12 @@ do -- TASK_A2A_INTERCEPT
|
|||||||
|
|
||||||
Mission:AddTask( self )
|
Mission:AddTask( self )
|
||||||
|
|
||||||
self:SetBriefing(
|
self:SetBriefing( TaskBriefing or "Intercept incoming intruders.\n" )
|
||||||
TaskBriefing or
|
|
||||||
"Intercept incoming intruders.\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set a score when a target in scope of the A2A attack, has been destroyed .
|
--- Set a score when a target in scope of the A2A attack, has been destroyed.
|
||||||
-- @param #TASK_A2A_INTERCEPT self
|
-- @param #TASK_A2A_INTERCEPT self
|
||||||
-- @param #string PlayerName The name of the player.
|
-- @param #string PlayerName The name of the player.
|
||||||
-- @param #number Score The score in points to be granted when task process has been achieved.
|
-- @param #number Score The score in points to be granted when task process has been achieved.
|
||||||
@@ -469,10 +452,8 @@ do -- TASK_A2A_INTERCEPT
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
do -- TASK_A2A_SWEEP
|
do -- TASK_A2A_SWEEP
|
||||||
|
|
||||||
--- The TASK_A2A_SWEEP class
|
--- The TASK_A2A_SWEEP class
|
||||||
@@ -493,11 +474,9 @@ do -- TASK_A2A_SWEEP
|
|||||||
--
|
--
|
||||||
-- @field #TASK_A2A_SWEEP
|
-- @field #TASK_A2A_SWEEP
|
||||||
TASK_A2A_SWEEP = {
|
TASK_A2A_SWEEP = {
|
||||||
ClassName = "TASK_A2A_SWEEP",
|
ClassName = "TASK_A2A_SWEEP"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- Instantiates a new TASK_A2A_SWEEP.
|
--- Instantiates a new TASK_A2A_SWEEP.
|
||||||
-- @param #TASK_A2A_SWEEP self
|
-- @param #TASK_A2A_SWEEP self
|
||||||
-- @param Tasking.Mission#MISSION Mission
|
-- @param Tasking.Mission#MISSION Mission
|
||||||
@@ -512,10 +491,7 @@ do -- TASK_A2A_SWEEP
|
|||||||
|
|
||||||
Mission:AddTask( self )
|
Mission:AddTask( self )
|
||||||
|
|
||||||
self:SetBriefing(
|
self:SetBriefing( TaskBriefing or "Perform a fighter sweep. Incoming intruders were detected and could be hiding at the location.\n" )
|
||||||
TaskBriefing or
|
|
||||||
"Perform a fighter sweep. Incoming intruders were detected and could be hiding at the location.\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@@ -531,7 +507,7 @@ do -- TASK_A2A_SWEEP
|
|||||||
self:__Goal( -10 )
|
self:__Goal( -10 )
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set a score when a target in scope of the A2A attack, has been destroyed .
|
--- Set a score when a target in scope of the A2A attack, has been destroyed.
|
||||||
-- @param #TASK_A2A_SWEEP self
|
-- @param #TASK_A2A_SWEEP self
|
||||||
-- @param #string PlayerName The name of the player.
|
-- @param #string PlayerName The name of the player.
|
||||||
-- @param #number Score The score in points to be granted when task process has been achieved.
|
-- @param #number Score The score in points to be granted when task process has been achieved.
|
||||||
@@ -581,7 +557,6 @@ do -- TASK_A2A_SWEEP
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
do -- TASK_A2A_ENGAGE
|
do -- TASK_A2A_ENGAGE
|
||||||
|
|
||||||
--- The TASK_A2A_ENGAGE class
|
--- The TASK_A2A_ENGAGE class
|
||||||
@@ -600,11 +575,9 @@ do -- TASK_A2A_ENGAGE
|
|||||||
--
|
--
|
||||||
-- @field #TASK_A2A_ENGAGE
|
-- @field #TASK_A2A_ENGAGE
|
||||||
TASK_A2A_ENGAGE = {
|
TASK_A2A_ENGAGE = {
|
||||||
ClassName = "TASK_A2A_ENGAGE",
|
ClassName = "TASK_A2A_ENGAGE"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- Instantiates a new TASK_A2A_ENGAGE.
|
--- Instantiates a new TASK_A2A_ENGAGE.
|
||||||
-- @param #TASK_A2A_ENGAGE self
|
-- @param #TASK_A2A_ENGAGE self
|
||||||
-- @param Tasking.Mission#MISSION Mission
|
-- @param Tasking.Mission#MISSION Mission
|
||||||
@@ -619,10 +592,7 @@ do -- TASK_A2A_ENGAGE
|
|||||||
|
|
||||||
Mission:AddTask( self )
|
Mission:AddTask( self )
|
||||||
|
|
||||||
self:SetBriefing(
|
self:SetBriefing( TaskBriefing or "Bogeys are nearby! Players close by are ordered to ENGAGE the intruders!\n" )
|
||||||
TaskBriefing or
|
|
||||||
"Bogeys are nearby! Players close by are ordered to ENGAGE the intruders!\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
-- The above example creates a SET_GROUP instance, and stores this in the variable (object) **EWRSet**.
|
-- The above example creates a SET_GROUP instance, and stores this in the variable (object) **EWRSet**.
|
||||||
-- **EWRSet** is then being configured to filter all active groups with a group name starting with **EWR** to be included in the Set.
|
-- **EWRSet** is then being configured to filter all active groups with a group name starting with **EWR** to be included in the Set.
|
||||||
-- **EWRSet** is then being ordered to start the dynamic filtering. Note that any destroy or new spawn of a group with the above names will be removed or added to the Set.
|
-- **EWRSet** is then being ordered to start the dynamic filtering. Note that any destroy or new spawn of a group with the above names will be removed or added to the Set.
|
||||||
-- Then a new **EWRDetection** object is created from the class DETECTION_AREAS. A grouping radius of 6000 is choosen, which is 6km.
|
-- Then a new **EWRDetection** object is created from the class DETECTION_AREAS. A grouping radius of 6000 is chosen, which is 6 km.
|
||||||
-- The **EWRDetection** object is then passed to the @{#TASK_A2A_DISPATCHER.New}() method to indicate the EWR network configuration and setup the A2A tasking and detection mechanism.
|
-- The **EWRDetection** object is then passed to the @{#TASK_A2A_DISPATCHER.New}() method to indicate the EWR network configuration and setup the A2A tasking and detection mechanism.
|
||||||
--
|
--
|
||||||
-- ### 2. Define the detected **target grouping radius**:
|
-- ### 2. Define the detected **target grouping radius**:
|
||||||
@@ -185,7 +185,6 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
SweepZones = {},
|
SweepZones = {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
--- TASK_A2A_DISPATCHER constructor.
|
--- TASK_A2A_DISPATCHER constructor.
|
||||||
-- @param #TASK_A2A_DISPATCHER self
|
-- @param #TASK_A2A_DISPATCHER self
|
||||||
-- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done.
|
-- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done.
|
||||||
@@ -199,7 +198,7 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
|
|
||||||
self.Detection = Detection
|
self.Detection = Detection
|
||||||
self.Mission = Mission
|
self.Mission = Mission
|
||||||
|
self.FlashNewTask = false
|
||||||
|
|
||||||
-- TODO: Check detection through radar.
|
-- TODO: Check detection through radar.
|
||||||
self.Detection:FilterCategories( Unit.Category.AIRPLANE, Unit.Category.HELICOPTER )
|
self.Detection:FilterCategories( Unit.Category.AIRPLANE, Unit.Category.HELICOPTER )
|
||||||
@@ -208,7 +207,6 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
|
|
||||||
self:AddTransition( "Started", "Assign", "Started" )
|
self:AddTransition( "Started", "Assign", "Started" )
|
||||||
|
|
||||||
|
|
||||||
--- OnAfter Transition Handler for Event Assign.
|
--- OnAfter Transition Handler for Event Assign.
|
||||||
-- @function [parent=#TASK_A2A_DISPATCHER] OnAfterAssign
|
-- @function [parent=#TASK_A2A_DISPATCHER] OnAfterAssign
|
||||||
-- @param #TASK_A2A_DISPATCHER self
|
-- @param #TASK_A2A_DISPATCHER self
|
||||||
@@ -224,7 +222,6 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Define the radius to when an ENGAGE task will be generated for any nearby by airborne friendlies, which are executing cap or returning from an intercept mission.
|
--- Define the radius to when an ENGAGE task will be generated for any nearby by airborne friendlies, which are executing cap or returning from an intercept mission.
|
||||||
-- So, if there is a target area detected and reported,
|
-- So, if there is a target area detected and reported,
|
||||||
-- then any friendlies that are airborne near this target area,
|
-- then any friendlies that are airborne near this target area,
|
||||||
@@ -253,7 +250,12 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Set flashing player messages on or off
|
||||||
|
-- @param #TASK_A2A_DISPATCHER self
|
||||||
|
-- @param #boolean onoff Set messages on (true) or off (false)
|
||||||
|
function TASK_A2A_DISPATCHER:SetSendMessages( onoff )
|
||||||
|
self.FlashNewTask = onoff
|
||||||
|
end
|
||||||
|
|
||||||
--- Creates an INTERCEPT task when there are targets for it.
|
--- Creates an INTERCEPT task when there are targets for it.
|
||||||
-- @param #TASK_A2A_DISPATCHER self
|
-- @param #TASK_A2A_DISPATCHER self
|
||||||
@@ -281,7 +283,6 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Creates an SWEEP task when there are targets for it.
|
--- Creates an SWEEP task when there are targets for it.
|
||||||
-- @param #TASK_A2A_DISPATCHER self
|
-- @param #TASK_A2A_DISPATCHER self
|
||||||
-- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem
|
-- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem
|
||||||
@@ -291,8 +292,7 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
self:F( { DetectedItem.ItemID } )
|
self:F( { DetectedItem.ItemID } )
|
||||||
|
|
||||||
local DetectedSet = DetectedItem.Set
|
local DetectedSet = DetectedItem.Set
|
||||||
local DetectedZone = DetectedItem.Zone
|
local DetectedZone = DetectedItem.Zone -- TODO: This seems unused, remove?
|
||||||
|
|
||||||
|
|
||||||
if DetectedItem.IsDetected == false then
|
if DetectedItem.IsDetected == false then
|
||||||
|
|
||||||
@@ -307,7 +307,6 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Creates an ENGAGE task when there are human friendlies airborne near the targets.
|
--- Creates an ENGAGE task when there are human friendlies airborne near the targets.
|
||||||
-- @param #TASK_A2A_DISPATCHER self
|
-- @param #TASK_A2A_DISPATCHER self
|
||||||
-- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem
|
-- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem
|
||||||
@@ -317,11 +316,10 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
self:F( { DetectedItem.ItemID } )
|
self:F( { DetectedItem.ItemID } )
|
||||||
|
|
||||||
local DetectedSet = DetectedItem.Set
|
local DetectedSet = DetectedItem.Set
|
||||||
local DetectedZone = DetectedItem.Zone
|
local DetectedZone = DetectedItem.Zone -- TODO: This seems unused, remove?
|
||||||
|
|
||||||
local PlayersCount, PlayersReport = self:GetPlayerFriendliesNearBy( DetectedItem )
|
local PlayersCount, PlayersReport = self:GetPlayerFriendliesNearBy( DetectedItem )
|
||||||
|
|
||||||
|
|
||||||
-- Only allow ENGAGE when there are Players near the zone, and when the Area has detected items since the last run in a 60 seconds time zone.
|
-- Only allow ENGAGE when there are Players near the zone, and when the Area has detected items since the last run in a 60 seconds time zone.
|
||||||
if PlayersCount > 0 and DetectedItem.IsDetected == true then
|
if PlayersCount > 0 and DetectedItem.IsDetected == true then
|
||||||
|
|
||||||
@@ -336,9 +334,6 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- Evaluates the removal of the Task from the Mission.
|
--- Evaluates the removal of the Task from the Mission.
|
||||||
-- Can only occur when the DetectedItem is Changed AND the state of the Task is "Planned".
|
-- Can only occur when the DetectedItem is Changed AND the state of the Task is "Planned".
|
||||||
-- @param #TASK_A2A_DISPATCHER self
|
-- @param #TASK_A2A_DISPATCHER self
|
||||||
@@ -383,8 +378,8 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
end
|
end
|
||||||
|
|
||||||
local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT
|
local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT
|
||||||
--DetectedSet:Flush( self )
|
-- DetectedSet:Flush( self )
|
||||||
--self:F( { DetectedSetCount = DetectedSet:Count() } )
|
-- self:F( { DetectedSetCount = DetectedSet:Count() } )
|
||||||
if DetectedSet:Count() == 0 then
|
if DetectedSet:Count() == 0 then
|
||||||
Remove = true
|
Remove = true
|
||||||
end
|
end
|
||||||
@@ -418,7 +413,7 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
local FriendlyUnitThreatLevel = FriendlyUnit:GetThreatLevel()
|
local FriendlyUnitThreatLevel = FriendlyUnit:GetThreatLevel()
|
||||||
FriendliesCount = FriendliesCount + 1
|
FriendliesCount = FriendliesCount + 1
|
||||||
local FriendlyType = FriendlyUnit:GetTypeName()
|
local FriendlyType = FriendlyUnit:GetTypeName()
|
||||||
FriendlyTypes[FriendlyType] = FriendlyTypes[FriendlyType] and ( FriendlyTypes[FriendlyType] + 1 ) or 1
|
FriendlyTypes[FriendlyType] = FriendlyTypes[FriendlyType] and (FriendlyTypes[FriendlyType] + 1) or 1
|
||||||
if DetectedTreatLevel < FriendlyUnitThreatLevel + 2 then
|
if DetectedTreatLevel < FriendlyUnitThreatLevel + 2 then
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -426,19 +421,18 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--self:F( { FriendliesCount = FriendliesCount } )
|
-- self:F( { FriendliesCount = FriendliesCount } )
|
||||||
|
|
||||||
local FriendlyTypesReport = REPORT:New()
|
local FriendlyTypesReport = REPORT:New()
|
||||||
|
|
||||||
if FriendliesCount > 0 then
|
if FriendliesCount > 0 then
|
||||||
for FriendlyType, FriendlyTypeCount in pairs( FriendlyTypes ) do
|
for FriendlyType, FriendlyTypeCount in pairs( FriendlyTypes ) do
|
||||||
FriendlyTypesReport:Add( string.format("%d of %s", FriendlyTypeCount, FriendlyType ) )
|
FriendlyTypesReport:Add( string.format( "%d of %s", FriendlyTypeCount, FriendlyType ) )
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
FriendlyTypesReport:Add( "-" )
|
FriendlyTypesReport:Add( "-" )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
return FriendliesCount, FriendlyTypesReport
|
return FriendliesCount, FriendlyTypesReport
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -459,7 +453,7 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
for PlayerUnitName, PlayerUnitData in pairs( PlayersNearBy ) do
|
for PlayerUnitName, PlayerUnitData in pairs( PlayersNearBy ) do
|
||||||
local PlayerUnit = PlayerUnitData -- Wrapper.Unit#UNIT
|
local PlayerUnit = PlayerUnitData -- Wrapper.Unit#UNIT
|
||||||
local PlayerName = PlayerUnit:GetPlayerName()
|
local PlayerName = PlayerUnit:GetPlayerName()
|
||||||
--self:F( { PlayerName = PlayerName, PlayerUnit = PlayerUnit } )
|
-- self:F( { PlayerName = PlayerName, PlayerUnit = PlayerUnit } )
|
||||||
if PlayerUnit:IsAirPlane() and PlayerName ~= nil then
|
if PlayerUnit:IsAirPlane() and PlayerName ~= nil then
|
||||||
local FriendlyUnitThreatLevel = PlayerUnit:GetThreatLevel()
|
local FriendlyUnitThreatLevel = PlayerUnit:GetThreatLevel()
|
||||||
PlayersCount = PlayersCount + 1
|
PlayersCount = PlayersCount + 1
|
||||||
@@ -476,13 +470,12 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
|
|
||||||
if PlayersCount > 0 then
|
if PlayersCount > 0 then
|
||||||
for PlayerName, PlayerType in pairs( PlayerTypes ) do
|
for PlayerName, PlayerType in pairs( PlayerTypes ) do
|
||||||
PlayerTypesReport:Add( string.format('"%s" in %s', PlayerName, PlayerType ) )
|
PlayerTypesReport:Add( string.format( '"%s" in %s', PlayerName, PlayerType ) )
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
PlayerTypesReport:Add( "-" )
|
PlayerTypesReport:Add( "-" )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
return PlayersCount, PlayerTypesReport
|
return PlayersCount, PlayerTypesReport
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -491,7 +484,6 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
self.Tasks[TaskIndex] = nil
|
self.Tasks[TaskIndex] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Assigns tasks in relation to the detected items to the @{Core.Set#SET_GROUP}.
|
--- Assigns tasks in relation to the detected items to the @{Core.Set#SET_GROUP}.
|
||||||
-- @param #TASK_A2A_DISPATCHER self
|
-- @param #TASK_A2A_DISPATCHER self
|
||||||
-- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Functional.Detection#DETECTION_BASE} derived object.
|
-- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Functional.Detection#DETECTION_BASE} derived object.
|
||||||
@@ -531,8 +523,8 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT
|
local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT
|
||||||
local DetectedCount = DetectedSet:Count()
|
local DetectedCount = DetectedSet:Count()
|
||||||
local DetectedZone = DetectedItem.Zone
|
local DetectedZone = DetectedItem.Zone
|
||||||
--self:F( { "Targets in DetectedItem", DetectedItem.ItemID, DetectedSet:Count(), tostring( DetectedItem ) } )
|
-- self:F( { "Targets in DetectedItem", DetectedItem.ItemID, DetectedSet:Count(), tostring( DetectedItem ) } )
|
||||||
--DetectedSet:Flush( self )
|
-- DetectedSet:Flush( self )
|
||||||
|
|
||||||
local DetectedID = DetectedItem.ID
|
local DetectedID = DetectedItem.ID
|
||||||
local TaskIndex = DetectedItem.Index
|
local TaskIndex = DetectedItem.Index
|
||||||
@@ -588,7 +580,7 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
|
|
||||||
TaskReport:Add( Task:GetName() )
|
TaskReport:Add( Task:GetName() )
|
||||||
else
|
else
|
||||||
self:F("This should not happen")
|
self:F( "This should not happen" )
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -607,10 +599,10 @@ do -- TASK_A2A_DISPATCHER
|
|||||||
-- TODO set menus using the HQ coordinator
|
-- TODO set menus using the HQ coordinator
|
||||||
Mission:GetCommandCenter():SetMenu()
|
Mission:GetCommandCenter():SetMenu()
|
||||||
|
|
||||||
local TaskText = TaskReport:Text(", ")
|
local TaskText = TaskReport:Text( ", " )
|
||||||
|
|
||||||
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
|
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
|
||||||
if ( not Mission:IsGroupAssigned(TaskGroup) ) and TaskText ~= "" then
|
if (not Mission:IsGroupAssigned( TaskGroup )) and TaskText ~= "" and (self.FlashNewTask) then
|
||||||
Mission:GetCommandCenter():MessageToGroup( string.format( "%s has tasks %s. Subscribe to a task using the radio menu.", Mission:GetShortText(), TaskText ), TaskGroup )
|
Mission:GetCommandCenter():MessageToGroup( string.format( "%s has tasks %s. Subscribe to a task using the radio menu.", Mission:GetShortText(), TaskText ), TaskGroup )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ do -- TASK_A2G
|
|||||||
--
|
--
|
||||||
-- @field #TASK_A2G
|
-- @field #TASK_A2G
|
||||||
TASK_A2G = {
|
TASK_A2G = {
|
||||||
ClassName = "TASK_A2G",
|
ClassName = "TASK_A2G"
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Instantiates a new TASK_A2G.
|
--- Instantiates a new TASK_A2G.
|
||||||
@@ -61,27 +61,25 @@ do -- TASK_A2G
|
|||||||
local Fsm = self:GetUnitProcess()
|
local Fsm = self:GetUnitProcess()
|
||||||
|
|
||||||
Fsm:AddTransition( "Assigned", "RouteToRendezVous", "RoutingToRendezVous" )
|
Fsm:AddTransition( "Assigned", "RouteToRendezVous", "RoutingToRendezVous" )
|
||||||
Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtRendezVous" } )
|
Fsm:AddProcess( "RoutingToRendezVous", "RouteToRendezVousPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtRendezVous" } )
|
||||||
Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtRendezVous" } )
|
Fsm:AddProcess( "RoutingToRendezVous", "RouteToRendezVousZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtRendezVous" } )
|
||||||
|
|
||||||
Fsm:AddTransition( { "Arrived", "RoutingToRendezVous" }, "ArriveAtRendezVous", "ArrivedAtRendezVous" )
|
Fsm:AddTransition( { "Arrived", "RoutingToRendezVous" }, "ArriveAtRendezVous", "ArrivedAtRendezVous" )
|
||||||
|
|
||||||
Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "Engage", "Engaging" )
|
Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "Engage", "Engaging" )
|
||||||
Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "HoldAtRendezVous", "HoldingAtRendezVous" )
|
Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "HoldAtRendezVous", "HoldingAtRendezVous" )
|
||||||
|
|
||||||
Fsm:AddProcess ( "Engaging", "Account", ACT_ACCOUNT_DEADS:New(), {} )
|
Fsm:AddProcess( "Engaging", "Account", ACT_ACCOUNT_DEADS:New(), {} )
|
||||||
Fsm:AddTransition( "Engaging", "RouteToTarget", "Engaging" )
|
Fsm:AddTransition( "Engaging", "RouteToTarget", "Engaging" )
|
||||||
Fsm:AddProcess( "Engaging", "RouteToTargetZone", ACT_ROUTE_ZONE:New(), {} )
|
Fsm:AddProcess( "Engaging", "RouteToTargetZone", ACT_ROUTE_ZONE:New(), {} )
|
||||||
Fsm:AddProcess( "Engaging", "RouteToTargetPoint", ACT_ROUTE_POINT:New(), {} )
|
Fsm:AddProcess( "Engaging", "RouteToTargetPoint", ACT_ROUTE_POINT:New(), {} )
|
||||||
Fsm:AddTransition( "Engaging", "RouteToTargets", "Engaging" )
|
Fsm:AddTransition( "Engaging", "RouteToTargets", "Engaging" )
|
||||||
|
|
||||||
--Fsm:AddTransition( "Accounted", "DestroyedAll", "Accounted" )
|
-- Fsm:AddTransition( "Accounted", "DestroyedAll", "Accounted" )
|
||||||
--Fsm:AddTransition( "Accounted", "Success", "Success" )
|
-- Fsm:AddTransition( "Accounted", "Success", "Success" )
|
||||||
Fsm:AddTransition( "Rejected", "Reject", "Aborted" )
|
Fsm:AddTransition( "Rejected", "Reject", "Aborted" )
|
||||||
Fsm:AddTransition( "Failed", "Fail", "Failed" )
|
Fsm:AddTransition( "Failed", "Fail", "Failed" )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- Test
|
--- Test
|
||||||
-- @param #FSM_PROCESS self
|
-- @param #FSM_PROCESS self
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
@@ -130,7 +128,7 @@ do -- TASK_A2G
|
|||||||
function Fsm:onafterEngage( TaskUnit, Task )
|
function Fsm:onafterEngage( TaskUnit, Task )
|
||||||
self:F( { self } )
|
self:F( { self } )
|
||||||
self:__Account( 0.1 )
|
self:__Account( 0.1 )
|
||||||
self:__RouteToTarget(0.1 )
|
self:__RouteToTarget( 0.1 )
|
||||||
self:__RouteToTargets( -10 )
|
self:__RouteToTargets( -10 )
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -179,8 +177,6 @@ do -- TASK_A2G
|
|||||||
self.TargetSetUnit = TargetSetUnit
|
self.TargetSetUnit = TargetSetUnit
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- @param #TASK_A2G self
|
--- @param #TASK_A2G self
|
||||||
function TASK_A2G:GetPlannedMenuText()
|
function TASK_A2G:GetPlannedMenuText()
|
||||||
return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )"
|
return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )"
|
||||||
@@ -190,7 +186,7 @@ do -- TASK_A2G
|
|||||||
-- @param Core.Point#COORDINATE RendezVousCoordinate The Coordinate object referencing to the 2D point where the RendezVous point is located on the map.
|
-- @param Core.Point#COORDINATE RendezVousCoordinate The Coordinate object referencing to the 2D point where the RendezVous point is located on the map.
|
||||||
-- @param #number RendezVousRange The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point.
|
-- @param #number RendezVousRange The RendezVousRange that defines when the player is considered to have arrived at the RendezVous point.
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
function TASK_A2G:SetRendezVousCoordinate( RendezVousCoordinate, RendezVousRange, TaskUnit )
|
function TASK_A2G:SetRendezVousCoordinate( RendezVousCoordinate, RendezVousRange, TaskUnit )
|
||||||
|
|
||||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||||
|
|
||||||
@@ -211,8 +207,6 @@ do -- TASK_A2G
|
|||||||
return ActRouteRendezVous:GetCoordinate(), ActRouteRendezVous:GetRange()
|
return ActRouteRendezVous:GetCoordinate(), ActRouteRendezVous:GetRange()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- @param #TASK_A2G self
|
--- @param #TASK_A2G self
|
||||||
-- @param Core.Zone#ZONE_BASE RendezVousZone The Zone object where the RendezVous is located on the map.
|
-- @param Core.Zone#ZONE_BASE RendezVousZone The Zone object where the RendezVous is located on the map.
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
@@ -246,7 +240,6 @@ do -- TASK_A2G
|
|||||||
ActRouteTarget:SetCoordinate( TargetCoordinate )
|
ActRouteTarget:SetCoordinate( TargetCoordinate )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- @param #TASK_A2G self
|
--- @param #TASK_A2G self
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
-- @return Core.Point#COORDINATE The Coordinate object where the Target is located on the map.
|
-- @return Core.Point#COORDINATE The Coordinate object where the Target is located on the map.
|
||||||
@@ -258,7 +251,6 @@ do -- TASK_A2G
|
|||||||
return ActRouteTarget:GetCoordinate()
|
return ActRouteTarget:GetCoordinate()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- @param #TASK_A2G self
|
--- @param #TASK_A2G self
|
||||||
-- @param Core.Zone#ZONE_BASE TargetZone The Zone object where the Target is located on the map.
|
-- @param Core.Zone#ZONE_BASE TargetZone The Zone object where the Target is located on the map.
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
@@ -270,7 +262,6 @@ do -- TASK_A2G
|
|||||||
ActRouteTarget:SetZone( TargetZone )
|
ActRouteTarget:SetZone( TargetZone )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- @param #TASK_A2G self
|
--- @param #TASK_A2G self
|
||||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||||
-- @return Core.Zone#ZONE_BASE The Zone object where the Target is located on the map.
|
-- @return Core.Zone#ZONE_BASE The Zone object where the Target is located on the map.
|
||||||
@@ -295,7 +286,7 @@ 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.
|
--- 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
|
-- @param #TASK_A2G self
|
||||||
function TASK_A2G:ReportOrder( ReportGroup )
|
function TASK_A2G:ReportOrder( ReportGroup )
|
||||||
self:UpdateTaskInfo( self.DetectedItem )
|
self:UpdateTaskInfo( self.DetectedItem )
|
||||||
|
|
||||||
local Coordinate = self.TaskInfo:GetData( "Coordinate" )
|
local Coordinate = self.TaskInfo:GetData( "Coordinate" )
|
||||||
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
|
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
|
||||||
@@ -303,7 +294,6 @@ do -- TASK_A2G
|
|||||||
return Distance
|
return Distance
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- This method checks every 10 seconds if the goal has been reached of the task.
|
--- This method checks every 10 seconds if the goal has been reached of the task.
|
||||||
-- @param #TASK_A2G self
|
-- @param #TASK_A2G self
|
||||||
function TASK_A2G:onafterGoal( TaskUnit, From, Event, To )
|
function TASK_A2G:onafterGoal( TaskUnit, From, Event, To )
|
||||||
@@ -365,12 +355,12 @@ do -- TASK_A2G
|
|||||||
-- @param Wrapper.Group#GROUP TaskGroup The player group.
|
-- @param Wrapper.Group#GROUP TaskGroup The player group.
|
||||||
function TASK_A2G:GetAutoAssignPriority( AutoAssignMethod, CommandCenter, TaskGroup )
|
function TASK_A2G:GetAutoAssignPriority( AutoAssignMethod, CommandCenter, TaskGroup )
|
||||||
|
|
||||||
if AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Random then
|
if AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Random then
|
||||||
return math.random( 1, 9 )
|
return math.random( 1, 9 )
|
||||||
elseif AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Distance then
|
elseif AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Distance then
|
||||||
local Coordinate = self.TaskInfo:GetData( "Coordinate" )
|
local Coordinate = self.TaskInfo:GetData( "Coordinate" )
|
||||||
local Distance = Coordinate:Get2DDistance( CommandCenter:GetPositionable():GetCoordinate() )
|
local Distance = Coordinate:Get2DDistance( CommandCenter:GetPositionable():GetCoordinate() )
|
||||||
self:F({Distance=Distance})
|
self:F( { Distance = Distance } )
|
||||||
return math.floor( Distance )
|
return math.floor( Distance )
|
||||||
elseif AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Priority then
|
elseif AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Priority then
|
||||||
return 1
|
return 1
|
||||||
@@ -381,7 +371,6 @@ do -- TASK_A2G
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
do -- TASK_A2G_SEAD
|
do -- TASK_A2G_SEAD
|
||||||
|
|
||||||
--- The TASK_A2G_SEAD class
|
--- The TASK_A2G_SEAD class
|
||||||
@@ -397,7 +386,7 @@ do -- TASK_A2G_SEAD
|
|||||||
--
|
--
|
||||||
-- @field #TASK_A2G_SEAD
|
-- @field #TASK_A2G_SEAD
|
||||||
TASK_A2G_SEAD = {
|
TASK_A2G_SEAD = {
|
||||||
ClassName = "TASK_A2G_SEAD",
|
ClassName = "TASK_A2G_SEAD"
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Instantiates a new TASK_A2G_SEAD.
|
--- Instantiates a new TASK_A2G_SEAD.
|
||||||
@@ -408,16 +397,13 @@ do -- TASK_A2G_SEAD
|
|||||||
-- @param Core.Set#SET_UNIT TargetSetUnit
|
-- @param Core.Set#SET_UNIT TargetSetUnit
|
||||||
-- @param #string TaskBriefing The briefing of the task.
|
-- @param #string TaskBriefing The briefing of the task.
|
||||||
-- @return #TASK_A2G_SEAD self
|
-- @return #TASK_A2G_SEAD self
|
||||||
function TASK_A2G_SEAD:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing)
|
function TASK_A2G_SEAD:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing )
|
||||||
local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "SEAD", TaskBriefing ) ) -- #TASK_A2G_SEAD
|
local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "SEAD", TaskBriefing ) ) -- #TASK_A2G_SEAD
|
||||||
self:F()
|
self:F()
|
||||||
|
|
||||||
Mission:AddTask( self )
|
Mission:AddTask( self )
|
||||||
|
|
||||||
self:SetBriefing(
|
self:SetBriefing( TaskBriefing or "Execute a Suppression of Enemy Air Defenses." )
|
||||||
TaskBriefing or
|
|
||||||
"Execute a Suppression of Enemy Air Defenses."
|
|
||||||
)
|
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@@ -470,7 +456,6 @@ do -- TASK_A2G_SEAD
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
do -- TASK_A2G_BAI
|
do -- TASK_A2G_BAI
|
||||||
@@ -488,9 +473,7 @@ do -- TASK_A2G_BAI
|
|||||||
-- based on detected enemy ground targets.
|
-- based on detected enemy ground targets.
|
||||||
--
|
--
|
||||||
-- @field #TASK_A2G_BAI
|
-- @field #TASK_A2G_BAI
|
||||||
TASK_A2G_BAI = {
|
TASK_A2G_BAI = { ClassName = "TASK_A2G_BAI" }
|
||||||
ClassName = "TASK_A2G_BAI",
|
|
||||||
}
|
|
||||||
|
|
||||||
--- Instantiates a new TASK_A2G_BAI.
|
--- Instantiates a new TASK_A2G_BAI.
|
||||||
-- @param #TASK_A2G_BAI self
|
-- @param #TASK_A2G_BAI self
|
||||||
@@ -506,10 +489,7 @@ do -- TASK_A2G_BAI
|
|||||||
|
|
||||||
Mission:AddTask( self )
|
Mission:AddTask( self )
|
||||||
|
|
||||||
self:SetBriefing(
|
self:SetBriefing( TaskBriefing or "Execute a Battlefield Air Interdiction of a group of enemy targets." )
|
||||||
TaskBriefing or
|
|
||||||
"Execute a Battlefield Air Interdiction of a group of enemy targets."
|
|
||||||
)
|
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@@ -564,9 +544,6 @@ do -- TASK_A2G_BAI
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
do -- TASK_A2G_CAS
|
do -- TASK_A2G_CAS
|
||||||
|
|
||||||
--- The TASK_A2G_CAS class
|
--- The TASK_A2G_CAS class
|
||||||
@@ -581,9 +558,7 @@ do -- TASK_A2G_CAS
|
|||||||
-- based on detected enemy ground targets.
|
-- based on detected enemy ground targets.
|
||||||
--
|
--
|
||||||
-- @field #TASK_A2G_CAS
|
-- @field #TASK_A2G_CAS
|
||||||
TASK_A2G_CAS = {
|
TASK_A2G_CAS = { ClassName = "TASK_A2G_CAS" }
|
||||||
ClassName = "TASK_A2G_CAS",
|
|
||||||
}
|
|
||||||
|
|
||||||
--- Instantiates a new TASK_A2G_CAS.
|
--- Instantiates a new TASK_A2G_CAS.
|
||||||
-- @param #TASK_A2G_CAS self
|
-- @param #TASK_A2G_CAS self
|
||||||
@@ -599,17 +574,11 @@ do -- TASK_A2G_CAS
|
|||||||
|
|
||||||
Mission:AddTask( self )
|
Mission:AddTask( self )
|
||||||
|
|
||||||
self:SetBriefing(
|
self:SetBriefing( TaskBriefing or ( "Execute a Close Air Support for a group of enemy targets. " .. "Beware of friendlies at the vicinity! " ) )
|
||||||
TaskBriefing or
|
|
||||||
"Execute a Close Air Support for a group of enemy targets. " ..
|
|
||||||
"Beware of friendlies at the vicinity! "
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Set a score when a target in scope of the A2G attack, has been destroyed .
|
--- Set a score when a target in scope of the A2G attack, has been destroyed .
|
||||||
-- @param #TASK_A2G_CAS self
|
-- @param #TASK_A2G_CAS self
|
||||||
-- @param #string PlayerName The name of the player.
|
-- @param #string PlayerName The name of the player.
|
||||||
@@ -658,5 +627,4 @@ do -- TASK_A2G_CAS
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -6,11 +6,12 @@
|
|||||||
-- * Dynamically change the tasks as the tactical situation evolves during the mission.
|
-- * Dynamically change the tasks as the tactical situation evolves during the mission.
|
||||||
-- * Dynamically assign (CAS) Close Air Support tasks for human players.
|
-- * Dynamically assign (CAS) Close Air Support tasks for human players.
|
||||||
-- * Dynamically assign (BAI) Battlefield Air Interdiction tasks for human players.
|
-- * Dynamically assign (BAI) Battlefield Air Interdiction tasks for human players.
|
||||||
-- * Dynamically assign (SEAD) Supression of Enemy Air Defense tasks for human players to eliminate G2A missile threats.
|
-- * Dynamically assign (SEAD) Suppression of Enemy Air Defense tasks for human players to eliminate G2A missile threats.
|
||||||
-- * Define and use an EWR (Early Warning Radar) network.
|
-- * Define and use an EWR (Early Warning Radar) network.
|
||||||
-- * Define different ranges to engage upon intruders.
|
-- * Define different ranges to engage upon intruders.
|
||||||
-- * Keep task achievements.
|
-- * Keep task achievements.
|
||||||
-- * Score task achievements.--
|
-- * Score task achievements.
|
||||||
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ### Author: **FlightControl**
|
-- ### Author: **FlightControl**
|
||||||
@@ -122,7 +123,7 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
-- F1. Command Center [Lima]
|
-- F1. Command Center [Lima]
|
||||||
-- F1. Mission "Overlord (High)"
|
-- F1. Mission "Overlord (High)"
|
||||||
--
|
--
|
||||||
-- Command Center [Gori] is controlling Mission "Alpha", "Beta", "Gamma". Alpha is the Primary mission, Beta the Secondary and there is a Tacical mission Gamma.
|
-- Command Center [Gori] is controlling Mission "Alpha", "Beta", "Gamma". Alpha is the Primary mission, Beta the Secondary and there is a Tactical mission Gamma.
|
||||||
-- Command Center [Lima] is controlling Missions "Overlord", which needs to be executed with High priority.
|
-- Command Center [Lima] is controlling Missions "Overlord", which needs to be executed with High priority.
|
||||||
--
|
--
|
||||||
-- ## 1.1. Mission Menu (Under the Command Center Menu)
|
-- ## 1.1. Mission Menu (Under the Command Center Menu)
|
||||||
@@ -196,7 +197,7 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
--
|
--
|
||||||
-- The Mission Reports Menu is a sub menu, that provides options to retrieve further information on the current Mission:
|
-- The Mission Reports Menu is a sub menu, that provides options to retrieve further information on the current Mission:
|
||||||
--
|
--
|
||||||
-- - **Report Mission Progress**: Shows the progress of the current Mission. Each Task has a %-tage of completion.
|
-- - **Report Mission Progress**: Shows the progress of the current Mission. Each Task has a % of completion.
|
||||||
-- - **Report Players per Task**: Show which players are engaged on which Task within the Mission.
|
-- - **Report Players per Task**: Show which players are engaged on which Task within the Mission.
|
||||||
--
|
--
|
||||||
-- For CC |Gori|, Mission "Alpha", the Mission Reports menu structure could look like this:
|
-- For CC |Gori|, Mission "Alpha", the Mission Reports menu structure could look like this:
|
||||||
@@ -264,7 +265,6 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
--
|
--
|
||||||
-- **The F5. Assigned Task __TaskName__ allows the player to control the current Assigned Task and take further actions.**
|
-- **The F5. Assigned Task __TaskName__ allows the player to control the current Assigned Task and take further actions.**
|
||||||
--
|
--
|
||||||
--
|
|
||||||
-- ## 1.3. Join Planned Task Menu
|
-- ## 1.3. Join Planned Task Menu
|
||||||
--
|
--
|
||||||
-- The Join Planned Task Menu contains the different Planned A2G Tasks **in a structured Menu Hierarchy**.
|
-- The Join Planned Task Menu contains the different Planned A2G Tasks **in a structured Menu Hierarchy**.
|
||||||
@@ -388,7 +388,7 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
-- - A @{Mission} object. Each task belongs to a Mission.
|
-- - A @{Mission} object. Each task belongs to a Mission.
|
||||||
-- - A @{Detection} object. There are several detection grouping methods to choose from.
|
-- - A @{Detection} object. There are several detection grouping methods to choose from.
|
||||||
-- - A @{Task_A2G_Dispatcher} object. The master A2G task dispatcher.
|
-- - A @{Task_A2G_Dispatcher} object. The master A2G task dispatcher.
|
||||||
-- - A @{Set} of @{Wrapper.Group} objects that will detect the emeny, the RecceSet. This is attached to the @{Detection} object.
|
-- - A @{Set} of @{Wrapper.Group} objects that will detect the enemy, the RecceSet. This is attached to the @{Detection} object.
|
||||||
-- - A @{Set} ob @{Wrapper.Group} objects that will attack the enemy, the AttackSet. This is attached to the @{Task_A2G_Dispatcher} object.
|
-- - A @{Set} ob @{Wrapper.Group} objects that will attack the enemy, the AttackSet. This is attached to the @{Task_A2G_Dispatcher} object.
|
||||||
--
|
--
|
||||||
-- Below an example mission declaration that is defines a Task A2G Dispatcher object.
|
-- Below an example mission declaration that is defines a Task A2G Dispatcher object.
|
||||||
@@ -425,7 +425,7 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
--
|
--
|
||||||
-- -- Now we have everything to setup the main A2G TaskDispatcher.
|
-- -- Now we have everything to setup the main A2G TaskDispatcher.
|
||||||
-- TaskDispatcher = TASK_A2G_DISPATCHER
|
-- TaskDispatcher = TASK_A2G_DISPATCHER
|
||||||
-- :New( Mission, AttackSet, DetectionAreas ) -- We assign the TaskDispatcher under Mission. The AttackSet will engage the enemy and will recieve the dispatched Tasks. The DetectionAreas will report any detected enemies to the TaskDispatcher.
|
-- :New( Mission, AttackSet, DetectionAreas ) -- We assign the TaskDispatcher under Mission. The AttackSet will engage the enemy and will receive the dispatched Tasks. The DetectionAreas will report any detected enemies to the TaskDispatcher.
|
||||||
--
|
--
|
||||||
--
|
--
|
||||||
--
|
--
|
||||||
@@ -434,10 +434,9 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
ClassName = "TASK_A2G_DISPATCHER",
|
ClassName = "TASK_A2G_DISPATCHER",
|
||||||
Mission = nil,
|
Mission = nil,
|
||||||
Detection = nil,
|
Detection = nil,
|
||||||
Tasks = {},
|
Tasks = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
--- TASK_A2G_DISPATCHER constructor.
|
--- TASK_A2G_DISPATCHER constructor.
|
||||||
-- @param #TASK_A2G_DISPATCHER self
|
-- @param #TASK_A2G_DISPATCHER self
|
||||||
-- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done.
|
-- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done.
|
||||||
@@ -451,6 +450,7 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
|
|
||||||
self.Detection = Detection
|
self.Detection = Detection
|
||||||
self.Mission = Mission
|
self.Mission = Mission
|
||||||
|
self.FlashNewTask = true -- set to false to suppress flash messages
|
||||||
|
|
||||||
self.Detection:FilterCategories( { Unit.Category.GROUND_UNIT } )
|
self.Detection:FilterCategories( { Unit.Category.GROUND_UNIT } )
|
||||||
|
|
||||||
@@ -471,6 +471,12 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Set flashing player messages on or off
|
||||||
|
-- @param #TASK_A2G_DISPATCHER self
|
||||||
|
-- @param #boolean onoff Set messages on (true) or off (false)
|
||||||
|
function TASK_A2G_DISPATCHER:SetSendMessages( onoff )
|
||||||
|
self.FlashNewTask = onoff
|
||||||
|
end
|
||||||
|
|
||||||
--- Creates a SEAD task when there are targets for it.
|
--- Creates a SEAD task when there are targets for it.
|
||||||
-- @param #TASK_A2G_DISPATCHER self
|
-- @param #TASK_A2G_DISPATCHER self
|
||||||
@@ -511,7 +517,6 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
local DetectedSet = DetectedItem.Set
|
local DetectedSet = DetectedItem.Set
|
||||||
local DetectedZone = DetectedItem.Zone
|
local DetectedZone = DetectedItem.Zone
|
||||||
|
|
||||||
|
|
||||||
-- Determine if the set has ground units.
|
-- Determine if the set has ground units.
|
||||||
-- There should be ground unit friendlies nearby. Airborne units are valid friendlies types.
|
-- There should be ground unit friendlies nearby. Airborne units are valid friendlies types.
|
||||||
-- And there shouldn't be any radar.
|
-- And there shouldn't be any radar.
|
||||||
@@ -543,7 +548,6 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
local DetectedSet = DetectedItem.Set
|
local DetectedSet = DetectedItem.Set
|
||||||
local DetectedZone = DetectedItem.Zone
|
local DetectedZone = DetectedItem.Zone
|
||||||
|
|
||||||
|
|
||||||
-- Determine if the set has ground units.
|
-- Determine if the set has ground units.
|
||||||
-- There shouldn't be any ground unit friendlies nearby.
|
-- There shouldn't be any ground unit friendlies nearby.
|
||||||
-- And there shouldn't be any radar.
|
-- And there shouldn't be any radar.
|
||||||
@@ -564,7 +568,6 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function TASK_A2G_DISPATCHER:RemoveTask( TaskIndex )
|
function TASK_A2G_DISPATCHER:RemoveTask( TaskIndex )
|
||||||
self.Mission:RemoveTask( self.Tasks[TaskIndex] )
|
self.Mission:RemoveTask( self.Tasks[TaskIndex] )
|
||||||
self.Tasks[TaskIndex] = nil
|
self.Tasks[TaskIndex] = nil
|
||||||
@@ -581,8 +584,8 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
function TASK_A2G_DISPATCHER:EvaluateRemoveTask( Mission, Task, TaskIndex, DetectedItemChanged )
|
function TASK_A2G_DISPATCHER:EvaluateRemoveTask( Mission, Task, TaskIndex, DetectedItemChanged )
|
||||||
|
|
||||||
if Task then
|
if Task then
|
||||||
if ( Task:IsStatePlanned() and DetectedItemChanged == true ) or Task:IsStateCancelled() then
|
if (Task:IsStatePlanned() and DetectedItemChanged == true) or Task:IsStateCancelled() then
|
||||||
--self:F( "Removing Tasking: " .. Task:GetTaskName() )
|
-- self:F( "Removing Tasking: " .. Task:GetTaskName() )
|
||||||
self:RemoveTask( TaskIndex )
|
self:RemoveTask( TaskIndex )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -590,7 +593,6 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
return Task
|
return Task
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Assigns tasks in relation to the detected items to the @{Core.Set#SET_GROUP}.
|
--- Assigns tasks in relation to the detected items to the @{Core.Set#SET_GROUP}.
|
||||||
-- @param #TASK_A2G_DISPATCHER self
|
-- @param #TASK_A2G_DISPATCHER self
|
||||||
-- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Functional.Detection#DETECTION_BASE} derived object.
|
-- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Functional.Detection#DETECTION_BASE} derived object.
|
||||||
@@ -616,7 +618,9 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
if not DetectedItem then
|
if not DetectedItem then
|
||||||
local TaskText = Task:GetName()
|
local TaskText = Task:GetName()
|
||||||
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
|
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
|
||||||
Mission:GetCommandCenter():MessageToGroup( string.format( "Obsolete A2G task %s for %s removed.", TaskText, Mission:GetShortText() ), TaskGroup )
|
if self.FlashNewTask then
|
||||||
|
Mission:GetCommandCenter():MessageToGroup( string.format( "Obsolete A2G task %s for %s removed.", TaskText, Mission:GetShortText() ), TaskGroup )
|
||||||
|
end
|
||||||
end
|
end
|
||||||
Task = self:RemoveTask( TaskIndex )
|
Task = self:RemoveTask( TaskIndex )
|
||||||
end
|
end
|
||||||
@@ -629,8 +633,8 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
local DetectedItem = DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem
|
local DetectedItem = DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem
|
||||||
local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT
|
local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT
|
||||||
local DetectedZone = DetectedItem.Zone
|
local DetectedZone = DetectedItem.Zone
|
||||||
--self:F( { "Targets in DetectedItem", DetectedItem.ItemID, DetectedSet:Count(), tostring( DetectedItem ) } )
|
-- self:F( { "Targets in DetectedItem", DetectedItem.ItemID, DetectedSet:Count(), tostring( DetectedItem ) } )
|
||||||
--DetectedSet:Flush( self )
|
-- DetectedSet:Flush( self )
|
||||||
|
|
||||||
local DetectedItemID = DetectedItem.ID
|
local DetectedItemID = DetectedItem.ID
|
||||||
local TaskIndex = DetectedItem.Index
|
local TaskIndex = DetectedItem.Index
|
||||||
@@ -651,7 +655,7 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
Task:SetTargetSetUnit( TargetSetUnit )
|
Task:SetTargetSetUnit( TargetSetUnit )
|
||||||
Task:SetDetection( Detection, DetectedItem )
|
Task:SetDetection( Detection, DetectedItem )
|
||||||
Task:UpdateTaskInfo( DetectedItem )
|
Task:UpdateTaskInfo( DetectedItem )
|
||||||
TargetsReport:Add( Detection:GetChangeText( DetectedItem ) )
|
TargetsReport:Add( Detection:GetChangeText( DetectedItem ) )
|
||||||
else
|
else
|
||||||
Task:Cancel()
|
Task:Cancel()
|
||||||
end
|
end
|
||||||
@@ -685,8 +689,8 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
|
|
||||||
-- Now we send to each group the changes, if any.
|
-- Now we send to each group the changes, if any.
|
||||||
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
|
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
|
||||||
local TargetsText = TargetsReport:Text(", ")
|
local TargetsText = TargetsReport:Text( ", " )
|
||||||
if ( Mission:IsGroupAssigned(TaskGroup) ) and TargetsText ~= "" then
|
if (Mission:IsGroupAssigned( TaskGroup )) and TargetsText ~= "" and self.FlashNewTask then
|
||||||
Mission:GetCommandCenter():MessageToGroup( string.format( "Task %s has change of targets:\n %s", Task:GetName(), TargetsText ), TaskGroup )
|
Mission:GetCommandCenter():MessageToGroup( string.format( "Task %s has change of targets:\n %s", Task:GetName(), TargetsText ), TaskGroup )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -744,6 +748,7 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
local TargetSetUnit = self:EvaluateSEAD( DetectedItem ) -- Returns a SetUnit if there are targets to be SEADed...
|
local TargetSetUnit = self:EvaluateSEAD( DetectedItem ) -- Returns a SetUnit if there are targets to be SEADed...
|
||||||
if TargetSetUnit then
|
if TargetSetUnit then
|
||||||
Task = TASK_A2G_SEAD:New( Mission, self.SetGroup, string.format( "SEAD.%03d", DetectedItemID ), TargetSetUnit )
|
Task = TASK_A2G_SEAD:New( Mission, self.SetGroup, string.format( "SEAD.%03d", DetectedItemID ), TargetSetUnit )
|
||||||
|
DetectedItem.DesignateMenuName = string.format( "SEAD.%03d", DetectedItemID ) -- inject a name for DESIGNATE, if using same DETECTION object
|
||||||
Task:SetDetection( Detection, DetectedItem )
|
Task:SetDetection( Detection, DetectedItem )
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -752,6 +757,7 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
local TargetSetUnit = self:EvaluateCAS( DetectedItem ) -- Returns a SetUnit if there are targets to be CASed...
|
local TargetSetUnit = self:EvaluateCAS( DetectedItem ) -- Returns a SetUnit if there are targets to be CASed...
|
||||||
if TargetSetUnit then
|
if TargetSetUnit then
|
||||||
Task = TASK_A2G_CAS:New( Mission, self.SetGroup, string.format( "CAS.%03d", DetectedItemID ), TargetSetUnit )
|
Task = TASK_A2G_CAS:New( Mission, self.SetGroup, string.format( "CAS.%03d", DetectedItemID ), TargetSetUnit )
|
||||||
|
DetectedItem.DesignateMenuName = string.format( "CAS.%03d", DetectedItemID ) -- inject a name for DESIGNATE, if using same DETECTION object
|
||||||
Task:SetDetection( Detection, DetectedItem )
|
Task:SetDetection( Detection, DetectedItem )
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -760,6 +766,7 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
local TargetSetUnit = self:EvaluateBAI( DetectedItem, self.Mission:GetCommandCenter():GetPositionable():GetCoalition() ) -- Returns a SetUnit if there are targets to be BAIed...
|
local TargetSetUnit = self:EvaluateBAI( DetectedItem, self.Mission:GetCommandCenter():GetPositionable():GetCoalition() ) -- Returns a SetUnit if there are targets to be BAIed...
|
||||||
if TargetSetUnit then
|
if TargetSetUnit then
|
||||||
Task = TASK_A2G_BAI:New( Mission, self.SetGroup, string.format( "BAI.%03d", DetectedItemID ), TargetSetUnit )
|
Task = TASK_A2G_BAI:New( Mission, self.SetGroup, string.format( "BAI.%03d", DetectedItemID ), TargetSetUnit )
|
||||||
|
DetectedItem.DesignateMenuName = string.format( "BAI.%03d", DetectedItemID ) -- inject a name for DESIGNATE, if using same DETECTION object
|
||||||
Task:SetDetection( Detection, DetectedItem )
|
Task:SetDetection( Detection, DetectedItem )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -788,14 +795,12 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
self:Aborted( Task )
|
self:Aborted( Task )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
TaskReport:Add( Task:GetName() )
|
TaskReport:Add( Task:GetName() )
|
||||||
else
|
else
|
||||||
self:F("This should not happen")
|
self:F( "This should not happen" )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- OK, so the tasking has been done, now delete the changes reported for the area.
|
-- OK, so the tasking has been done, now delete the changes reported for the area.
|
||||||
Detection:AcceptChanges( DetectedItem )
|
Detection:AcceptChanges( DetectedItem )
|
||||||
end
|
end
|
||||||
@@ -803,9 +808,9 @@ do -- TASK_A2G_DISPATCHER
|
|||||||
-- TODO set menus using the HQ coordinator
|
-- TODO set menus using the HQ coordinator
|
||||||
Mission:GetCommandCenter():SetMenu()
|
Mission:GetCommandCenter():SetMenu()
|
||||||
|
|
||||||
local TaskText = TaskReport:Text(", ")
|
local TaskText = TaskReport:Text( ", " )
|
||||||
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
|
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
|
||||||
if ( not Mission:IsGroupAssigned(TaskGroup) ) and TaskText ~= "" then
|
if (not Mission:IsGroupAssigned( TaskGroup )) and TaskText ~= "" and self.FlashNewTask then
|
||||||
Mission:GetCommandCenter():MessageToGroup( string.format( "%s has tasks %s. Subscribe to a task using the radio menu.", Mission:GetShortText(), TaskText ), TaskGroup )
|
Mission:GetCommandCenter():MessageToGroup( string.format( "%s has tasks %s. Subscribe to a task using the radio menu.", Mission:GetShortText(), TaskText ), TaskGroup )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ do -- TASK_CAPTURE_DISPATCHER
|
|||||||
-- ## 1.1. Create a command center.
|
-- ## 1.1. Create a command center.
|
||||||
--
|
--
|
||||||
-- First you need to create a command center using the @{Tasking.CommandCenter#COMMANDCENTER.New}() constructor.
|
-- 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.
|
-- 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.
|
-- This group will act as the command center object.
|
||||||
-- It is a good practice to mark this group as invisible and invulnerable.
|
-- It is a good practice to mark this group as invisible and invulnerable.
|
||||||
--
|
--
|
||||||
@@ -184,6 +184,7 @@ do -- TASK_CAPTURE_DISPATCHER
|
|||||||
local self = BASE:Inherit( self, TASK_MANAGER:New( SetGroup ) ) -- #TASK_CAPTURE_DISPATCHER
|
local self = BASE:Inherit( self, TASK_MANAGER:New( SetGroup ) ) -- #TASK_CAPTURE_DISPATCHER
|
||||||
|
|
||||||
self.Mission = Mission
|
self.Mission = Mission
|
||||||
|
self.FlashNewTask = false
|
||||||
|
|
||||||
self:AddTransition( "Started", "Assign", "Started" )
|
self:AddTransition( "Started", "Assign", "Started" )
|
||||||
self:AddTransition( "Started", "ZoneCaptured", "Started" )
|
self:AddTransition( "Started", "ZoneCaptured", "Started" )
|
||||||
@@ -383,7 +384,7 @@ do -- TASK_CAPTURE_DISPATCHER
|
|||||||
local TaskText = TaskReport:Text(", ")
|
local TaskText = TaskReport:Text(", ")
|
||||||
|
|
||||||
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
|
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
|
||||||
if ( not Mission:IsGroupAssigned(TaskGroup) ) and TaskText ~= "" then
|
if ( not Mission:IsGroupAssigned(TaskGroup) ) and TaskText ~= "" and ( not self.FlashNewTask ) then
|
||||||
Mission:GetCommandCenter():MessageToGroup( string.format( "%s has tasks %s. Subscribe to a task using the radio menu.", Mission:GetShortText(), TaskText ), TaskGroup )
|
Mission:GetCommandCenter():MessageToGroup( string.format( "%s has tasks %s. Subscribe to a task using the radio menu.", Mission:GetShortText(), TaskText ), TaskGroup )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
--
|
--
|
||||||
-- DCS itself provides a lot of enumerators for various things. See [Enumerators](https://wiki.hoggitworld.com/view/Category:Enumerators) on Hoggit.
|
-- 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.
|
-- Other Moose classes also have enumerators. For example, the AIRBASE class has enumerators for airbase names.
|
||||||
--
|
--
|
||||||
-- @module ENUMS
|
-- @module ENUMS
|
||||||
-- @image MOOSE.JPG
|
-- @image MOOSE.JPG
|
||||||
@@ -30,16 +30,16 @@ ENUMS = {}
|
|||||||
-- @type ENUMS.ROE
|
-- @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 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 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 OpenFire AI will engage only targets specified in its tasking.
|
||||||
-- @field #number ReturnFire AI will only engage threats that shoot first.
|
-- @field #number ReturnFire AI will only engage threats that shoot first.
|
||||||
-- @field #number WeaponHold AI will hold fire under all circumstances.
|
-- @field #number WeaponHold AI will hold fire under all circumstances.
|
||||||
ENUMS.ROE = {
|
ENUMS.ROE = {
|
||||||
WeaponFree=0,
|
WeaponFree = 0,
|
||||||
OpenFireWeaponFree=1,
|
OpenFireWeaponFree = 1,
|
||||||
OpenFire=2,
|
OpenFire = 2,
|
||||||
ReturnFire=3,
|
ReturnFire = 3,
|
||||||
WeaponHold=4,
|
WeaponHold = 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Reaction On Threat.
|
--- Reaction On Threat.
|
||||||
-- @type ENUMS.ROT
|
-- @type ENUMS.ROT
|
||||||
@@ -49,11 +49,11 @@ ENUMS.ROE = {
|
|||||||
-- @field #number BypassAndEscape AI will attempt to avoid enemy threat zones all together. This includes attempting to fly above or around threats.
|
-- @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.
|
-- @field #number AllowAbortMission If a threat is deemed severe enough the AI will abort its mission and return to base.
|
||||||
ENUMS.ROT = {
|
ENUMS.ROT = {
|
||||||
NoReaction=0,
|
NoReaction = 0,
|
||||||
PassiveDefense=1,
|
PassiveDefense = 1,
|
||||||
EvadeFire=2,
|
EvadeFire = 2,
|
||||||
BypassAndEscape=3,
|
BypassAndEscape = 3,
|
||||||
AllowAbortMission=4,
|
AllowAbortMission = 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Alarm state.
|
--- Alarm state.
|
||||||
@@ -62,12 +62,12 @@ ENUMS.ROT = {
|
|||||||
-- @field #number Green Group is not combat ready. Sensors are stowed if possible.
|
-- @field #number Green Group is not combat ready. Sensors are stowed if possible.
|
||||||
-- @field #number Red Group is combat ready and actively searching for targets. Some groups like infantry will not move in this state.
|
-- @field #number Red Group is combat ready and actively searching for targets. Some groups like infantry will not move in this state.
|
||||||
ENUMS.AlarmState = {
|
ENUMS.AlarmState = {
|
||||||
Auto=0,
|
Auto = 0,
|
||||||
Green=1,
|
Green = 1,
|
||||||
Red=2,
|
Red = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Weapon types. See the [Weapon Flag](https://wiki.hoggitworld.com/view/DCS_enum_weapon_flag) enumerotor on hoggit wiki.
|
--- Weapon types. See the [Weapon Flag](https://wiki.hoggitworld.com/view/DCS_enum_weapon_flag) enumerator on Hoggit wiki.
|
||||||
-- @type ENUMS.WeaponFlag
|
-- @type ENUMS.WeaponFlag
|
||||||
ENUMS.WeaponFlag={
|
ENUMS.WeaponFlag={
|
||||||
-- Bombs
|
-- Bombs
|
||||||
@@ -111,7 +111,7 @@ ENUMS.WeaponFlag={
|
|||||||
--
|
--
|
||||||
-- Bombs
|
-- Bombs
|
||||||
GuidedBomb = 14, -- (LGB + TvGB + SNSGB)
|
GuidedBomb = 14, -- (LGB + TvGB + SNSGB)
|
||||||
AnyUnguidedBomb = 2147485680, -- (HeBomb + Penetrator + NapalmBomb + FAEBomb + ClusterBomb + Dispencer + CandleBomb + ParachuteBomb)
|
AnyUnguidedBomb = 2147485680, -- (HeBomb + Penetrator + NapalmBomb + FAEBomb + ClusterBomb + Dispenser + CandleBomb + ParachuteBomb)
|
||||||
AnyBomb = 2147485694, -- (GuidedBomb + AnyUnguidedBomb)
|
AnyBomb = 2147485694, -- (GuidedBomb + AnyUnguidedBomb)
|
||||||
--- Rockets
|
--- Rockets
|
||||||
AnyRocket = 30720, -- LightRocket + MarkerRocket + CandleRocket + HeavyRocket
|
AnyRocket = 30720, -- LightRocket + MarkerRocket + CandleRocket + HeavyRocket
|
||||||
@@ -173,7 +173,7 @@ ENUMS.MissionTask={
|
|||||||
TRANSPORT="Transport",
|
TRANSPORT="Transport",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Formations (new). See the [Formations](https://wiki.hoggitworld.com/view/DCS_enum_formation) on hoggit wiki.
|
--- Formations (new). See the [Formations](https://wiki.hoggitworld.com/view/DCS_enum_formation) on Hoggit wiki.
|
||||||
-- @type ENUMS.Formation
|
-- @type ENUMS.Formation
|
||||||
ENUMS.Formation={}
|
ENUMS.Formation={}
|
||||||
ENUMS.Formation.FixedWing={}
|
ENUMS.Formation.FixedWing={}
|
||||||
@@ -216,23 +216,23 @@ ENUMS.Formation.FixedWing.FighterVic.Close = 917505
|
|||||||
ENUMS.Formation.FixedWing.FighterVic.Open = 917506
|
ENUMS.Formation.FixedWing.FighterVic.Open = 917506
|
||||||
ENUMS.Formation.RotaryWing={}
|
ENUMS.Formation.RotaryWing={}
|
||||||
ENUMS.Formation.RotaryWing.Column={}
|
ENUMS.Formation.RotaryWing.Column={}
|
||||||
ENUMS.Formation.RotaryWing.Column.D70=720896
|
ENUMS.Formation.RotaryWing.Column.D70 = 720896
|
||||||
ENUMS.Formation.RotaryWing.Wedge={}
|
ENUMS.Formation.RotaryWing.Wedge={}
|
||||||
ENUMS.Formation.RotaryWing.Wedge.D70=8
|
ENUMS.Formation.RotaryWing.Wedge.D70 = 8
|
||||||
ENUMS.Formation.RotaryWing.FrontRight={}
|
ENUMS.Formation.RotaryWing.FrontRight={}
|
||||||
ENUMS.Formation.RotaryWing.FrontRight.D300=655361
|
ENUMS.Formation.RotaryWing.FrontRight.D300 = 655361
|
||||||
ENUMS.Formation.RotaryWing.FrontRight.D600=655362
|
ENUMS.Formation.RotaryWing.FrontRight.D600 = 655362
|
||||||
ENUMS.Formation.RotaryWing.FrontLeft={}
|
ENUMS.Formation.RotaryWing.FrontLeft={}
|
||||||
ENUMS.Formation.RotaryWing.FrontLeft.D300=655617
|
ENUMS.Formation.RotaryWing.FrontLeft.D300 = 655617
|
||||||
ENUMS.Formation.RotaryWing.FrontLeft.D600=655618
|
ENUMS.Formation.RotaryWing.FrontLeft.D600 = 655618
|
||||||
ENUMS.Formation.RotaryWing.EchelonRight={}
|
ENUMS.Formation.RotaryWing.EchelonRight={}
|
||||||
ENUMS.Formation.RotaryWing.EchelonRight.D70 =589825
|
ENUMS.Formation.RotaryWing.EchelonRight.D70 = 589825
|
||||||
ENUMS.Formation.RotaryWing.EchelonRight.D300=589826
|
ENUMS.Formation.RotaryWing.EchelonRight.D300 = 589826
|
||||||
ENUMS.Formation.RotaryWing.EchelonRight.D600=589827
|
ENUMS.Formation.RotaryWing.EchelonRight.D600 = 589827
|
||||||
ENUMS.Formation.RotaryWing.EchelonLeft={}
|
ENUMS.Formation.RotaryWing.EchelonLeft={}
|
||||||
ENUMS.Formation.RotaryWing.EchelonLeft.D70 =590081
|
ENUMS.Formation.RotaryWing.EchelonLeft.D70 = 590081
|
||||||
ENUMS.Formation.RotaryWing.EchelonLeft.D300=590082
|
ENUMS.Formation.RotaryWing.EchelonLeft.D300 = 590082
|
||||||
ENUMS.Formation.RotaryWing.EchelonLeft.D600=590083
|
ENUMS.Formation.RotaryWing.EchelonLeft.D600 = 590083
|
||||||
ENUMS.Formation.Vehicle={}
|
ENUMS.Formation.Vehicle={}
|
||||||
ENUMS.Formation.Vehicle.Vee="Vee"
|
ENUMS.Formation.Vehicle.Vee="Vee"
|
||||||
ENUMS.Formation.Vehicle.EchelonRight="EchelonR"
|
ENUMS.Formation.Vehicle.EchelonRight="EchelonR"
|
||||||
@@ -244,25 +244,25 @@ ENUMS.Formation.Vehicle.Cone="Cone"
|
|||||||
ENUMS.Formation.Vehicle.Diamond="Diamond"
|
ENUMS.Formation.Vehicle.Diamond="Diamond"
|
||||||
|
|
||||||
--- Formations (old). The old format is a simplified version of the new formation enums, which allow more sophisticated settings.
|
--- 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.
|
-- See the [Formations](https://wiki.hoggitworld.com/view/DCS_enum_formation) on Hoggit wiki.
|
||||||
-- @type ENUMS.FormationOld
|
-- @type ENUMS.FormationOld
|
||||||
ENUMS.FormationOld={}
|
ENUMS.FormationOld={}
|
||||||
ENUMS.FormationOld.FixedWing={}
|
ENUMS.FormationOld.FixedWing={}
|
||||||
ENUMS.FormationOld.FixedWing.LineAbreast=1
|
ENUMS.FormationOld.FixedWing.LineAbreast = 1
|
||||||
ENUMS.FormationOld.FixedWing.Trail=2
|
ENUMS.FormationOld.FixedWing.Trail = 2
|
||||||
ENUMS.FormationOld.FixedWing.Wedge=3
|
ENUMS.FormationOld.FixedWing.Wedge = 3
|
||||||
ENUMS.FormationOld.FixedWing.EchelonRight=4
|
ENUMS.FormationOld.FixedWing.EchelonRight = 4
|
||||||
ENUMS.FormationOld.FixedWing.EchelonLeft=5
|
ENUMS.FormationOld.FixedWing.EchelonLeft = 5
|
||||||
ENUMS.FormationOld.FixedWing.FingerFour=6
|
ENUMS.FormationOld.FixedWing.FingerFour = 6
|
||||||
ENUMS.FormationOld.FixedWing.SpreadFour=7
|
ENUMS.FormationOld.FixedWing.SpreadFour = 7
|
||||||
ENUMS.FormationOld.FixedWing.BomberElement=12
|
ENUMS.FormationOld.FixedWing.BomberElement = 12
|
||||||
ENUMS.FormationOld.FixedWing.BomberElementHeight=13
|
ENUMS.FormationOld.FixedWing.BomberElementHeight = 13
|
||||||
ENUMS.FormationOld.FixedWing.FighterVic=14
|
ENUMS.FormationOld.FixedWing.FighterVic = 14
|
||||||
ENUMS.FormationOld.RotaryWing={}
|
ENUMS.FormationOld.RotaryWing={}
|
||||||
ENUMS.FormationOld.RotaryWing.Wedge=8
|
ENUMS.FormationOld.RotaryWing.Wedge = 8
|
||||||
ENUMS.FormationOld.RotaryWing.Echelon=9
|
ENUMS.FormationOld.RotaryWing.Echelon = 9
|
||||||
ENUMS.FormationOld.RotaryWing.Front=10
|
ENUMS.FormationOld.RotaryWing.Front = 10
|
||||||
ENUMS.FormationOld.RotaryWing.Column=11
|
ENUMS.FormationOld.RotaryWing.Column = 11
|
||||||
|
|
||||||
|
|
||||||
--- Morse Code. See the [Wikipedia](https://en.wikipedia.org/wiki/Morse_code).
|
--- Morse Code. See the [Wikipedia](https://en.wikipedia.org/wiki/Morse_code).
|
||||||
@@ -311,3 +311,52 @@ ENUMS.Morse.N8="- - - * *"
|
|||||||
ENUMS.Morse.N9="- - - - *"
|
ENUMS.Morse.N9="- - - - *"
|
||||||
ENUMS.Morse.N0="- - - - -"
|
ENUMS.Morse.N0="- - - - -"
|
||||||
ENUMS.Morse[" "]=" "
|
ENUMS.Morse[" "]=" "
|
||||||
|
|
||||||
|
--- ISO (639-1) 2-letter Language Codes. See the [Wikipedia](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes).
|
||||||
|
--
|
||||||
|
-- @type ENUMS.ISOLang
|
||||||
|
ENUMS.ISOLang =
|
||||||
|
{
|
||||||
|
Arabic = 'AR',
|
||||||
|
Chinese = 'ZH',
|
||||||
|
English = 'EN',
|
||||||
|
French = 'FR',
|
||||||
|
German = 'DE',
|
||||||
|
Russian = 'RU',
|
||||||
|
Spanish = 'ES',
|
||||||
|
Japanese = 'JA',
|
||||||
|
Italian = 'IT',
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Phonetic Alphabet (NATO). See the [Wikipedia](https://en.wikipedia.org/wiki/NATO_phonetic_alphabet).
|
||||||
|
--
|
||||||
|
-- @type ENUMS.Phonetic
|
||||||
|
ENUMS.Phonetic =
|
||||||
|
{
|
||||||
|
A = 'Alpha',
|
||||||
|
B = 'Bravo',
|
||||||
|
C = 'Charlie',
|
||||||
|
D = 'Delta',
|
||||||
|
E = 'Echo',
|
||||||
|
F = 'Foxtrot',
|
||||||
|
G = 'Golf',
|
||||||
|
H = 'Hotel',
|
||||||
|
I = 'India',
|
||||||
|
J = 'Juliett',
|
||||||
|
K = 'Kilo',
|
||||||
|
L = 'Lima',
|
||||||
|
M = 'Mike',
|
||||||
|
N = 'November',
|
||||||
|
O = 'Oscar',
|
||||||
|
P = 'Papa',
|
||||||
|
Q = 'Quebec',
|
||||||
|
R = 'Romeo',
|
||||||
|
S = 'Sierra',
|
||||||
|
T = 'Tango',
|
||||||
|
U = 'Uniform',
|
||||||
|
V = 'Victor',
|
||||||
|
W = 'Whiskey',
|
||||||
|
X = 'Xray',
|
||||||
|
Y = 'Yankee',
|
||||||
|
Z = 'Zulu',
|
||||||
|
}
|
||||||
@@ -9,7 +9,6 @@
|
|||||||
-- @module Utilities.PROFILER
|
-- @module Utilities.PROFILER
|
||||||
-- @image MOOSE.JPG
|
-- @image MOOSE.JPG
|
||||||
|
|
||||||
|
|
||||||
--- PROFILER class.
|
--- PROFILER class.
|
||||||
-- @type PROFILER
|
-- @type PROFILER
|
||||||
-- @field #string ClassName Name of the class.
|
-- @field #string ClassName Name of the class.
|
||||||
@@ -25,7 +24,6 @@
|
|||||||
-- @field #number ThreshTtot Total time threshold. Only write output if total function CPU time is more than this value.
|
-- @field #number ThreshTtot Total time threshold. Only write output if total function CPU time is more than this value.
|
||||||
-- @field #string fileNamePrefix Output file name prefix, e.g. "MooseProfiler".
|
-- @field #string fileNamePrefix Output file name prefix, e.g. "MooseProfiler".
|
||||||
-- @field #string fileNameSuffix Output file name prefix, e.g. "txt"
|
-- @field #string fileNameSuffix Output file name prefix, e.g. "txt"
|
||||||
|
|
||||||
--- *The emperor counsels simplicity. First principles. Of each particular thing, ask: What is it in itself, in its own constitution? What is its causal nature? *
|
--- *The emperor counsels simplicity. First principles. Of each particular thing, ask: What is it in itself, in its own constitution? What is its causal nature? *
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
@@ -35,12 +33,11 @@
|
|||||||
-- # The PROFILER Concept
|
-- # The PROFILER Concept
|
||||||
--
|
--
|
||||||
-- Profile your lua code. This tells you, which functions are called very often and which consume most real time.
|
-- Profile your lua code. This tells you, which functions are called very often and which consume most real time.
|
||||||
-- With this information you can optimize the perfomance of your code.
|
-- With this information you can optimize the performance of your code.
|
||||||
--
|
--
|
||||||
-- # Prerequisites
|
-- # Prerequisites
|
||||||
--
|
--
|
||||||
-- The modules **os** and **lfs** need to be desanizied.
|
-- The modules **os** and **lfs** need to be de-sanitized.
|
||||||
--
|
|
||||||
--
|
--
|
||||||
-- # Start
|
-- # Start
|
||||||
--
|
--
|
||||||
@@ -117,61 +114,60 @@ PROFILER = {
|
|||||||
--- Start profiler.
|
--- Start profiler.
|
||||||
-- @param #number Delay Delay in seconds before profiler is stated. Default is immediately.
|
-- @param #number Delay Delay in seconds before profiler is stated. Default is immediately.
|
||||||
-- @param #number Duration Duration in (game) seconds before the profiler is stopped. Default is when mission ends.
|
-- @param #number Duration Duration in (game) seconds before the profiler is stopped. Default is when mission ends.
|
||||||
function PROFILER.Start(Delay, Duration)
|
function PROFILER.Start( Delay, Duration )
|
||||||
|
|
||||||
-- Check if os, io and lfs are available.
|
-- Check if os, io and lfs are available.
|
||||||
local go=true
|
local go = true
|
||||||
if not os then
|
if not os then
|
||||||
env.error("ERROR: Profiler needs os to be desanitized!")
|
env.error( "ERROR: Profiler needs os to be de-sanitized!" )
|
||||||
go=false
|
go = false
|
||||||
end
|
end
|
||||||
if not io then
|
if not io then
|
||||||
env.error("ERROR: Profiler needs io to be desanitized!")
|
env.error( "ERROR: Profiler needs io to be de-sanitized!" )
|
||||||
go=false
|
go = false
|
||||||
end
|
end
|
||||||
if not lfs then
|
if not lfs then
|
||||||
env.error("ERROR: Profiler needs lfs to be desanitized!")
|
env.error( "ERROR: Profiler needs lfs to be de-sanitized!" )
|
||||||
go=false
|
go = false
|
||||||
end
|
end
|
||||||
if not go then
|
if not go then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if Delay and Delay>0 then
|
if Delay and Delay > 0 then
|
||||||
BASE:ScheduleOnce(Delay, PROFILER.Start, 0, Duration)
|
BASE:ScheduleOnce( Delay, PROFILER.Start, 0, Duration )
|
||||||
else
|
else
|
||||||
|
|
||||||
-- Set start time.
|
-- Set start time.
|
||||||
PROFILER.TstartGame=timer.getTime()
|
PROFILER.TstartGame = timer.getTime()
|
||||||
PROFILER.TstartOS=os.clock()
|
PROFILER.TstartOS = os.clock()
|
||||||
|
|
||||||
-- Add event handler.
|
-- Add event handler.
|
||||||
world.addEventHandler(PROFILER.eventHandler)
|
world.addEventHandler( PROFILER.eventHandler )
|
||||||
|
|
||||||
-- Info in log.
|
-- Info in log.
|
||||||
env.info('############################ Profiler Started ############################')
|
env.info( '############################ Profiler Started ############################' )
|
||||||
if Duration then
|
if Duration then
|
||||||
env.info(string.format("- Will be running for %d seconds", Duration))
|
env.info( string.format( "- Will be running for %d seconds", Duration ) )
|
||||||
else
|
else
|
||||||
env.info(string.format("- Will be stopped when mission ends"))
|
env.info( string.format( "- Will be stopped when mission ends" ) )
|
||||||
end
|
end
|
||||||
env.info(string.format("- Calls per second threshold %.3f/sec", PROFILER.ThreshCPS))
|
env.info( string.format( "- Calls per second threshold %.3f/sec", PROFILER.ThreshCPS ) )
|
||||||
env.info(string.format("- Total function time threshold %.3f sec", PROFILER.ThreshTtot))
|
env.info( string.format( "- Total function time threshold %.3f sec", PROFILER.ThreshTtot ) )
|
||||||
env.info(string.format("- Output file \"%s\" in your DCS log file folder", PROFILER.getfilename(PROFILER.fileNameSuffix)))
|
env.info( string.format( "- Output file \"%s\" in your DCS log file folder", PROFILER.getfilename( PROFILER.fileNameSuffix ) ) )
|
||||||
env.info(string.format("- Output file \"%s\" in CSV format", PROFILER.getfilename("csv")))
|
env.info( string.format( "- Output file \"%s\" in CSV format", PROFILER.getfilename( "csv" ) ) )
|
||||||
env.info('###############################################################################')
|
env.info( '###############################################################################' )
|
||||||
|
|
||||||
|
|
||||||
-- Message on screen
|
-- Message on screen
|
||||||
local duration=Duration or 600
|
local duration = Duration or 600
|
||||||
trigger.action.outText("### Profiler running ###", duration)
|
trigger.action.outText( "### Profiler running ###", duration )
|
||||||
|
|
||||||
-- Set hook.
|
-- Set hook.
|
||||||
debug.sethook(PROFILER.hook, "cr")
|
debug.sethook( PROFILER.hook, "cr" )
|
||||||
|
|
||||||
-- Auto stop profiler.
|
-- Auto stop profiler.
|
||||||
if Duration then
|
if Duration then
|
||||||
PROFILER.Stop(Duration)
|
PROFILER.Stop( Duration )
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -180,34 +176,33 @@ end
|
|||||||
|
|
||||||
--- Stop profiler.
|
--- Stop profiler.
|
||||||
-- @param #number Delay Delay before stop in seconds.
|
-- @param #number Delay Delay before stop in seconds.
|
||||||
function PROFILER.Stop(Delay)
|
function PROFILER.Stop( Delay )
|
||||||
|
|
||||||
if Delay and Delay>0 then
|
if Delay and Delay > 0 then
|
||||||
|
|
||||||
BASE:ScheduleOnce(Delay, PROFILER.Stop)
|
BASE:ScheduleOnce( Delay, PROFILER.Stop )
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
-- Remove hook.
|
-- Remove hook.
|
||||||
debug.sethook()
|
debug.sethook()
|
||||||
|
|
||||||
|
|
||||||
-- Run time game.
|
-- Run time game.
|
||||||
local runTimeGame=timer.getTime()-PROFILER.TstartGame
|
local runTimeGame = timer.getTime() - PROFILER.TstartGame
|
||||||
|
|
||||||
-- Run time real OS.
|
-- Run time real OS.
|
||||||
local runTimeOS=os.clock()-PROFILER.TstartOS
|
local runTimeOS = os.clock() - PROFILER.TstartOS
|
||||||
|
|
||||||
-- Show info.
|
-- Show info.
|
||||||
PROFILER.showInfo(runTimeGame, runTimeOS)
|
PROFILER.showInfo( runTimeGame, runTimeOS )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Event handler.
|
--- Event handler.
|
||||||
function PROFILER.eventHandler:onEvent(event)
|
function PROFILER.eventHandler:onEvent( event )
|
||||||
if event.id==world.event.S_EVENT_MISSION_END then
|
if event.id == world.event.S_EVENT_MISSION_END then
|
||||||
PROFILER.Stop()
|
PROFILER.Stop()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -218,34 +213,34 @@ end
|
|||||||
|
|
||||||
--- Debug hook.
|
--- Debug hook.
|
||||||
-- @param #table event Event.
|
-- @param #table event Event.
|
||||||
function PROFILER.hook(event)
|
function PROFILER.hook( event )
|
||||||
|
|
||||||
local f=debug.getinfo(2, "f").func
|
local f = debug.getinfo( 2, "f" ).func
|
||||||
|
|
||||||
if event=='call' then
|
if event == 'call' then
|
||||||
|
|
||||||
if PROFILER.Counters[f]==nil then
|
if PROFILER.Counters[f] == nil then
|
||||||
|
|
||||||
PROFILER.Counters[f]=1
|
PROFILER.Counters[f] = 1
|
||||||
PROFILER.dInfo[f]=debug.getinfo(2,"Sn")
|
PROFILER.dInfo[f] = debug.getinfo( 2, "Sn" )
|
||||||
|
|
||||||
if PROFILER.fTimeTotal[f]==nil then
|
if PROFILER.fTimeTotal[f] == nil then
|
||||||
PROFILER.fTimeTotal[f]=0
|
PROFILER.fTimeTotal[f] = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
PROFILER.Counters[f]=PROFILER.Counters[f]+1
|
PROFILER.Counters[f] = PROFILER.Counters[f] + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
if PROFILER.fTime[f]==nil then
|
if PROFILER.fTime[f] == nil then
|
||||||
PROFILER.fTime[f]=os.clock()
|
PROFILER.fTime[f] = os.clock()
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif (event=='return') then
|
elseif (event == 'return') then
|
||||||
|
|
||||||
if PROFILER.fTime[f]~=nil then
|
if PROFILER.fTime[f] ~= nil then
|
||||||
PROFILER.fTimeTotal[f]=PROFILER.fTimeTotal[f]+(os.clock()-PROFILER.fTime[f])
|
PROFILER.fTimeTotal[f] = PROFILER.fTimeTotal[f] + (os.clock() - PROFILER.fTime[f])
|
||||||
PROFILER.fTime[f]=nil
|
PROFILER.fTime[f] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -262,11 +257,11 @@ end
|
|||||||
-- @return #string Source file name.
|
-- @return #string Source file name.
|
||||||
-- @return #string Line number.
|
-- @return #string Line number.
|
||||||
-- @return #number Function time in seconds.
|
-- @return #number Function time in seconds.
|
||||||
function PROFILER.getData(func)
|
function PROFILER.getData( func )
|
||||||
|
|
||||||
local n=PROFILER.dInfo[func]
|
local n = PROFILER.dInfo[func]
|
||||||
|
|
||||||
if n.what=="C" then
|
if n.what == "C" then
|
||||||
return n.name, "?", "?", PROFILER.fTimeTotal[func]
|
return n.name, "?", "?", PROFILER.fTimeTotal[func]
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -276,31 +271,31 @@ end
|
|||||||
--- Write text to log file.
|
--- Write text to log file.
|
||||||
-- @param #function f The file.
|
-- @param #function f The file.
|
||||||
-- @param #string txt The text.
|
-- @param #string txt The text.
|
||||||
function PROFILER._flog(f, txt)
|
function PROFILER._flog( f, txt )
|
||||||
f:write(txt.."\r\n")
|
f:write( txt .. "\r\n" )
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Show table.
|
--- Show table.
|
||||||
-- @param #table data Data table.
|
-- @param #table data Data table.
|
||||||
-- @param #function f The file.
|
-- @param #function f The file.
|
||||||
-- @param #number runTimeGame Game run time in seconds.
|
-- @param #number runTimeGame Game run time in seconds.
|
||||||
function PROFILER.showTable(data, f, runTimeGame)
|
function PROFILER.showTable( data, f, runTimeGame )
|
||||||
|
|
||||||
-- Loop over data.
|
-- Loop over data.
|
||||||
for i=1, #data do
|
for i = 1, #data do
|
||||||
local t=data[i] --#PROFILER.Data
|
local t = data[i] -- #PROFILER.Data
|
||||||
|
|
||||||
-- Calls per second.
|
-- Calls per second.
|
||||||
local cps=t.count/runTimeGame
|
local cps = t.count / runTimeGame
|
||||||
|
|
||||||
local threshCPS=cps>=PROFILER.ThreshCPS
|
local threshCPS = cps >= PROFILER.ThreshCPS
|
||||||
local threshTot=t.tm>=PROFILER.ThreshTtot
|
local threshTot = t.tm >= PROFILER.ThreshTtot
|
||||||
|
|
||||||
if threshCPS and threshTot then
|
if threshCPS and threshTot then
|
||||||
|
|
||||||
-- Output
|
-- Output
|
||||||
local text=string.format("%30s: %8d calls %8.1f/sec - Time Total %8.3f sec (%.3f %%) %5.3f sec/call %s line %s", t.func, t.count, cps, t.tm, t.tm/runTimeGame*100, t.tm/t.count, tostring(t.src), tostring(t.line))
|
local text = string.format( "%30s: %8d calls %8.1f/sec - Time Total %8.3f sec (%.3f %%) %5.3f sec/call %s line %s", t.func, t.count, cps, t.tm, t.tm / runTimeGame * 100, t.tm / t.count, tostring( t.src ), tostring( t.line ) )
|
||||||
PROFILER._flog(f, text)
|
PROFILER._flog( f, text )
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -310,26 +305,26 @@ end
|
|||||||
--- Print csv file.
|
--- Print csv file.
|
||||||
-- @param #table data Data table.
|
-- @param #table data Data table.
|
||||||
-- @param #number runTimeGame Game run time in seconds.
|
-- @param #number runTimeGame Game run time in seconds.
|
||||||
function PROFILER.printCSV(data, runTimeGame)
|
function PROFILER.printCSV( data, runTimeGame )
|
||||||
|
|
||||||
-- Output file.
|
-- Output file.
|
||||||
local file=PROFILER.getfilename("csv")
|
local file = PROFILER.getfilename( "csv" )
|
||||||
local g=io.open(file, 'w')
|
local g = io.open( file, 'w' )
|
||||||
|
|
||||||
-- Header.
|
-- Header.
|
||||||
local text="Function,Total Calls,Calls per Sec,Total Time,Total in %,Sec per Call,Source File;Line Number,"
|
local text = "Function,Total Calls,Calls per Sec,Total Time,Total in %,Sec per Call,Source File;Line Number,"
|
||||||
g:write(text.."\r\n")
|
g:write( text .. "\r\n" )
|
||||||
|
|
||||||
-- Loop over data.
|
-- Loop over data.
|
||||||
for i=1, #data do
|
for i = 1, #data do
|
||||||
local t=data[i] --#PROFILER.Data
|
local t = data[i] -- #PROFILER.Data
|
||||||
|
|
||||||
-- Calls per second.
|
-- Calls per second.
|
||||||
local cps=t.count/runTimeGame
|
local cps = t.count / runTimeGame
|
||||||
|
|
||||||
-- Output
|
-- Output
|
||||||
local txt=string.format("%s,%d,%.1f,%.3f,%.3f,%.3f,%s,%s,", t.func, t.count, cps, t.tm, t.tm/runTimeGame*100, t.tm/t.count, tostring(t.src), tostring(t.line))
|
local txt = string.format( "%s,%d,%.1f,%.3f,%.3f,%.3f,%s,%s,", t.func, t.count, cps, t.tm, t.tm / runTimeGame * 100, t.tm / t.count, tostring( t.src ), tostring( t.line ) )
|
||||||
g:write(txt.."\r\n")
|
g:write( txt .. "\r\n" )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -337,27 +332,26 @@ function PROFILER.printCSV(data, runTimeGame)
|
|||||||
g:close()
|
g:close()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Write info to output file.
|
--- Write info to output file.
|
||||||
-- @param #string ext Extension.
|
-- @param #string ext Extension.
|
||||||
-- @return #string File name.
|
-- @return #string File name.
|
||||||
function PROFILER.getfilename(ext)
|
function PROFILER.getfilename( ext )
|
||||||
|
|
||||||
local dir=lfs.writedir()..[[Logs\]]
|
local dir = lfs.writedir() .. [[Logs\]]
|
||||||
|
|
||||||
ext=ext or PROFILER.fileNameSuffix
|
ext = ext or PROFILER.fileNameSuffix
|
||||||
|
|
||||||
local file=dir..PROFILER.fileNamePrefix.."."..ext
|
local file = dir .. PROFILER.fileNamePrefix .. "." .. ext
|
||||||
|
|
||||||
if not UTILS.FileExists(file) then
|
if not UTILS.FileExists( file ) then
|
||||||
return file
|
return file
|
||||||
end
|
end
|
||||||
|
|
||||||
for i=1,999 do
|
for i = 1, 999 do
|
||||||
|
|
||||||
local file=string.format("%s%s-%03d.%s", dir,PROFILER.fileNamePrefix, i, ext)
|
local file = string.format( "%s%s-%03d.%s", dir, PROFILER.fileNamePrefix, i, ext )
|
||||||
|
|
||||||
if not UTILS.FileExists(file) then
|
if not UTILS.FileExists( file ) then
|
||||||
return file
|
return file
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -368,81 +362,76 @@ end
|
|||||||
--- Write info to output file.
|
--- Write info to output file.
|
||||||
-- @param #number runTimeGame Game time in seconds.
|
-- @param #number runTimeGame Game time in seconds.
|
||||||
-- @param #number runTimeOS OS time in seconds.
|
-- @param #number runTimeOS OS time in seconds.
|
||||||
function PROFILER.showInfo(runTimeGame, runTimeOS)
|
function PROFILER.showInfo( runTimeGame, runTimeOS )
|
||||||
|
|
||||||
-- Output file.
|
-- Output file.
|
||||||
local file=PROFILER.getfilename(PROFILER.fileNameSuffix)
|
local file = PROFILER.getfilename( PROFILER.fileNameSuffix )
|
||||||
local f=io.open(file, 'w')
|
local f = io.open( file, 'w' )
|
||||||
|
|
||||||
-- Gather data.
|
-- Gather data.
|
||||||
local Ttot=0
|
local Ttot = 0
|
||||||
local Calls=0
|
local Calls = 0
|
||||||
|
|
||||||
local t={}
|
local t = {}
|
||||||
|
|
||||||
local tcopy=nil --#PROFILER.Data
|
local tcopy = nil -- #PROFILER.Data
|
||||||
local tserialize=nil --#PROFILER.Data
|
local tserialize = nil -- #PROFILER.Data
|
||||||
local tforgen=nil --#PROFILER.Data
|
local tforgen = nil -- #PROFILER.Data
|
||||||
local tpairs=nil --#PROFILER.Data
|
local tpairs = nil -- #PROFILER.Data
|
||||||
|
|
||||||
|
for func, count in pairs( PROFILER.Counters ) do
|
||||||
|
|
||||||
for func, count in pairs(PROFILER.Counters) do
|
local s, src, line, tm = PROFILER.getData( func )
|
||||||
|
|
||||||
local s,src,line,tm=PROFILER.getData(func)
|
if PROFILER.logUnknown == true then
|
||||||
|
if s == nil then
|
||||||
if PROFILER.logUnknown==true then
|
s = "<Unknown>"
|
||||||
if s==nil then s="<Unknown>" end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if s~=nil then
|
if s ~= nil then
|
||||||
|
|
||||||
-- Profile data.
|
-- Profile data.
|
||||||
local T=
|
local T = { func = s, src = src, line = line, count = count, tm = tm } -- #PROFILER.Data
|
||||||
{ func=s,
|
|
||||||
src=src,
|
|
||||||
line=line,
|
|
||||||
count=count,
|
|
||||||
tm=tm,
|
|
||||||
} --#PROFILER.Data
|
|
||||||
|
|
||||||
-- Collect special cases. Somehow, e.g. "_copy" appears multiple times so we try to gather all data.
|
-- Collect special cases. Somehow, e.g. "_copy" appears multiple times so we try to gather all data.
|
||||||
if s=="_copy" then
|
if s == "_copy" then
|
||||||
if tcopy==nil then
|
if tcopy == nil then
|
||||||
tcopy=T
|
tcopy = T
|
||||||
else
|
else
|
||||||
tcopy.count=tcopy.count+T.count
|
tcopy.count = tcopy.count + T.count
|
||||||
tcopy.tm=tcopy.tm+T.tm
|
tcopy.tm = tcopy.tm + T.tm
|
||||||
end
|
end
|
||||||
elseif s=="_Serialize" then
|
elseif s == "_Serialize" then
|
||||||
if tserialize==nil then
|
if tserialize == nil then
|
||||||
tserialize=T
|
tserialize = T
|
||||||
else
|
else
|
||||||
tserialize.count=tserialize.count+T.count
|
tserialize.count = tserialize.count + T.count
|
||||||
tserialize.tm=tserialize.tm+T.tm
|
tserialize.tm = tserialize.tm + T.tm
|
||||||
end
|
end
|
||||||
elseif s=="(for generator)" then
|
elseif s == "(for generator)" then
|
||||||
if tforgen==nil then
|
if tforgen == nil then
|
||||||
tforgen=T
|
tforgen = T
|
||||||
else
|
else
|
||||||
tforgen.count=tforgen.count+T.count
|
tforgen.count = tforgen.count + T.count
|
||||||
tforgen.tm=tforgen.tm+T.tm
|
tforgen.tm = tforgen.tm + T.tm
|
||||||
end
|
end
|
||||||
elseif s=="pairs" then
|
elseif s == "pairs" then
|
||||||
if tpairs==nil then
|
if tpairs == nil then
|
||||||
tpairs=T
|
tpairs = T
|
||||||
else
|
else
|
||||||
tpairs.count=tpairs.count+T.count
|
tpairs.count = tpairs.count + T.count
|
||||||
tpairs.tm=tpairs.tm+T.tm
|
tpairs.tm = tpairs.tm + T.tm
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
table.insert(t, T)
|
table.insert( t, T )
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Total function time.
|
-- Total function time.
|
||||||
Ttot=Ttot+tm
|
Ttot = Ttot + tm
|
||||||
|
|
||||||
-- Total number of calls.
|
-- Total number of calls.
|
||||||
Calls=Calls+count
|
Calls = Calls + count
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -450,90 +439,95 @@ function PROFILER.showInfo(runTimeGame, runTimeOS)
|
|||||||
|
|
||||||
-- Add special cases.
|
-- Add special cases.
|
||||||
if tcopy then
|
if tcopy then
|
||||||
table.insert(t, tcopy)
|
table.insert( t, tcopy )
|
||||||
end
|
end
|
||||||
if tserialize then
|
if tserialize then
|
||||||
table.insert(t, tserialize)
|
table.insert( t, tserialize )
|
||||||
end
|
end
|
||||||
if tforgen then
|
if tforgen then
|
||||||
table.insert(t, tforgen)
|
table.insert( t, tforgen )
|
||||||
end
|
end
|
||||||
if tpairs then
|
if tpairs then
|
||||||
table.insert(t, tpairs)
|
table.insert( t, tpairs )
|
||||||
end
|
end
|
||||||
|
|
||||||
env.info('############################ Profiler Stopped ############################')
|
env.info( '############################ Profiler Stopped ############################' )
|
||||||
env.info(string.format("* Runtime Game : %s = %d sec", UTILS.SecondsToClock(runTimeGame, true), runTimeGame))
|
env.info( string.format( "* Runtime Game : %s = %d sec", UTILS.SecondsToClock( runTimeGame, true ), runTimeGame ) )
|
||||||
env.info(string.format("* Runtime Real : %s = %d sec", UTILS.SecondsToClock(runTimeOS, true), runTimeOS))
|
env.info( string.format( "* Runtime Real : %s = %d sec", UTILS.SecondsToClock( runTimeOS, true ), runTimeOS ) )
|
||||||
env.info(string.format("* Function time : %s = %.1f sec (%.1f percent of runtime game)", UTILS.SecondsToClock(Ttot, true), Ttot, Ttot/runTimeGame*100))
|
env.info( string.format( "* Function time : %s = %.1f sec (%.1f percent of runtime game)", UTILS.SecondsToClock( Ttot, true ), Ttot, Ttot / runTimeGame * 100 ) )
|
||||||
env.info(string.format("* Total functions : %d", #t))
|
env.info( string.format( "* Total functions : %d", #t ) )
|
||||||
env.info(string.format("* Total func calls : %d", Calls))
|
env.info( string.format( "* Total func calls : %d", Calls ) )
|
||||||
env.info(string.format("* Writing to file : \"%s\"", file))
|
env.info( string.format( "* Writing to file : \"%s\"", file ) )
|
||||||
env.info(string.format("* Writing to file : \"%s\"", PROFILER.getfilename("csv")))
|
env.info( string.format( "* Writing to file : \"%s\"", PROFILER.getfilename( "csv" ) ) )
|
||||||
env.info("##############################################################################")
|
env.info( "##############################################################################" )
|
||||||
|
|
||||||
-- Sort by total time.
|
-- Sort by total time.
|
||||||
table.sort(t, function(a,b) return a.tm>b.tm end)
|
table.sort( t, function( a, b )
|
||||||
|
return a.tm > b.tm
|
||||||
|
end )
|
||||||
|
|
||||||
-- Write data.
|
-- Write data.
|
||||||
PROFILER._flog(f,"")
|
PROFILER._flog( f, "" )
|
||||||
PROFILER._flog(f,"************************************************************************************************************************")
|
PROFILER._flog( f, "************************************************************************************************************************" )
|
||||||
PROFILER._flog(f,"************************************************************************************************************************")
|
PROFILER._flog( f, "************************************************************************************************************************" )
|
||||||
PROFILER._flog(f,"************************************************************************************************************************")
|
PROFILER._flog( f, "************************************************************************************************************************" )
|
||||||
PROFILER._flog(f,"")
|
PROFILER._flog( f, "" )
|
||||||
PROFILER._flog(f,"-------------------------")
|
PROFILER._flog( f, "-------------------------" )
|
||||||
PROFILER._flog(f,"---- Profiler Report ----")
|
PROFILER._flog( f, "---- Profiler Report ----" )
|
||||||
PROFILER._flog(f,"-------------------------")
|
PROFILER._flog( f, "-------------------------" )
|
||||||
PROFILER._flog(f,"")
|
PROFILER._flog( f, "" )
|
||||||
PROFILER._flog(f,string.format("* Runtime Game : %s = %.1f sec", UTILS.SecondsToClock(runTimeGame, true), runTimeGame))
|
PROFILER._flog( f, string.format( "* Runtime Game : %s = %.1f sec", UTILS.SecondsToClock( runTimeGame, true ), runTimeGame ) )
|
||||||
PROFILER._flog(f,string.format("* Runtime Real : %s = %.1f sec", UTILS.SecondsToClock(runTimeOS, true), runTimeOS))
|
PROFILER._flog( f, string.format( "* Runtime Real : %s = %.1f sec", UTILS.SecondsToClock( runTimeOS, true ), runTimeOS ) )
|
||||||
PROFILER._flog(f,string.format("* Function time : %s = %.1f sec (%.1f %% of runtime game)", UTILS.SecondsToClock(Ttot, true), Ttot, Ttot/runTimeGame*100))
|
PROFILER._flog( f, string.format( "* Function time : %s = %.1f sec (%.1f %% of runtime game)", UTILS.SecondsToClock( Ttot, true ), Ttot, Ttot / runTimeGame * 100 ) )
|
||||||
PROFILER._flog(f,"")
|
PROFILER._flog( f, "" )
|
||||||
PROFILER._flog(f,string.format("* Total functions = %d", #t))
|
PROFILER._flog( f, string.format( "* Total functions = %d", #t ) )
|
||||||
PROFILER._flog(f,string.format("* Total func calls = %d", Calls))
|
PROFILER._flog( f, string.format( "* Total func calls = %d", Calls ) )
|
||||||
PROFILER._flog(f,"")
|
PROFILER._flog( f, "" )
|
||||||
PROFILER._flog(f,string.format("* Calls per second threshold = %.3f/sec", PROFILER.ThreshCPS))
|
PROFILER._flog( f, string.format( "* Calls per second threshold = %.3f/sec", PROFILER.ThreshCPS ) )
|
||||||
PROFILER._flog(f,string.format("* Total func time threshold = %.3f sec", PROFILER.ThreshTtot))
|
PROFILER._flog( f, string.format( "* Total func time threshold = %.3f sec", PROFILER.ThreshTtot ) )
|
||||||
PROFILER._flog(f,"")
|
PROFILER._flog( f, "" )
|
||||||
PROFILER._flog(f,"************************************************************************************************************************")
|
PROFILER._flog( f, "************************************************************************************************************************" )
|
||||||
PROFILER._flog(f,"")
|
PROFILER._flog( f, "" )
|
||||||
PROFILER.showTable(t, f, runTimeGame)
|
PROFILER.showTable( t, f, runTimeGame )
|
||||||
|
|
||||||
-- Sort by number of calls.
|
-- Sort by number of calls.
|
||||||
table.sort(t, function(a,b) return a.tm/a.count>b.tm/b.count end)
|
table.sort( t, function( a, b )
|
||||||
|
return a.tm / a.count > b.tm / b.count
|
||||||
|
end )
|
||||||
|
|
||||||
-- Detailed data.
|
-- Detailed data.
|
||||||
PROFILER._flog(f,"")
|
PROFILER._flog( f, "" )
|
||||||
PROFILER._flog(f,"************************************************************************************************************************")
|
PROFILER._flog( f, "************************************************************************************************************************" )
|
||||||
PROFILER._flog(f,"")
|
PROFILER._flog( f, "" )
|
||||||
PROFILER._flog(f,"--------------------------------------")
|
PROFILER._flog( f, "--------------------------------------" )
|
||||||
PROFILER._flog(f,"---- Data Sorted by Time per Call ----")
|
PROFILER._flog( f, "---- Data Sorted by Time per Call ----" )
|
||||||
PROFILER._flog(f,"--------------------------------------")
|
PROFILER._flog( f, "--------------------------------------" )
|
||||||
PROFILER._flog(f,"")
|
PROFILER._flog( f, "" )
|
||||||
PROFILER.showTable(t, f, runTimeGame)
|
PROFILER.showTable( t, f, runTimeGame )
|
||||||
|
|
||||||
-- Sort by number of calls.
|
-- Sort by number of calls.
|
||||||
table.sort(t, function(a,b) return a.count>b.count end)
|
table.sort( t, function( a, b )
|
||||||
|
return a.count > b.count
|
||||||
|
end )
|
||||||
|
|
||||||
-- Detailed data.
|
-- Detailed data.
|
||||||
PROFILER._flog(f,"")
|
PROFILER._flog( f, "" )
|
||||||
PROFILER._flog(f,"************************************************************************************************************************")
|
PROFILER._flog( f, "************************************************************************************************************************" )
|
||||||
PROFILER._flog(f,"")
|
PROFILER._flog( f, "" )
|
||||||
PROFILER._flog(f,"------------------------------------")
|
PROFILER._flog( f, "------------------------------------" )
|
||||||
PROFILER._flog(f,"---- Data Sorted by Total Calls ----")
|
PROFILER._flog( f, "---- Data Sorted by Total Calls ----" )
|
||||||
PROFILER._flog(f,"------------------------------------")
|
PROFILER._flog( f, "------------------------------------" )
|
||||||
PROFILER._flog(f,"")
|
PROFILER._flog( f, "" )
|
||||||
PROFILER.showTable(t, f, runTimeGame)
|
PROFILER.showTable( t, f, runTimeGame )
|
||||||
|
|
||||||
-- Closing.
|
-- Closing.
|
||||||
PROFILER._flog(f,"")
|
PROFILER._flog( f, "" )
|
||||||
PROFILER._flog(f,"************************************************************************************************************************")
|
PROFILER._flog( f, "************************************************************************************************************************" )
|
||||||
PROFILER._flog(f,"************************************************************************************************************************")
|
PROFILER._flog( f, "************************************************************************************************************************" )
|
||||||
PROFILER._flog(f,"************************************************************************************************************************")
|
PROFILER._flog( f, "************************************************************************************************************************" )
|
||||||
-- Close file.
|
-- Close file.
|
||||||
f:close()
|
f:close()
|
||||||
|
|
||||||
-- Print csv file.
|
-- Print csv file.
|
||||||
PROFILER.printCSV(t, runTimeGame)
|
PROFILER.printCSV( t, runTimeGame )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
256
Moose Development/Moose/Utilities/STTS.lua
Normal file
256
Moose Development/Moose/Utilities/STTS.lua
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
--- **Utilities** DCS Simple Text-To-Speech (STTS).
|
||||||
|
--
|
||||||
|
--
|
||||||
|
-- @module Utils.STTS
|
||||||
|
-- @image MOOSE.JPG
|
||||||
|
|
||||||
|
--- [DCS Enum world](https://wiki.hoggitworld.com/view/DCS_enum_world)
|
||||||
|
-- @type STTS
|
||||||
|
-- @field #string DIRECTORY Path of the SRS directory.
|
||||||
|
|
||||||
|
--- Simple Text-To-Speech
|
||||||
|
--
|
||||||
|
-- Version 0.4 - Compatible with SRS version 1.9.6.0+
|
||||||
|
--
|
||||||
|
-- # DCS Modification Required
|
||||||
|
--
|
||||||
|
-- You will need to edit MissionScripting.lua in DCS World/Scripts/MissionScripting.lua and remove the sanitization.
|
||||||
|
-- To do this remove all the code below the comment - the line starts "local function sanitizeModule(name)"
|
||||||
|
-- Do this without DCS running to allow mission scripts to use os functions.
|
||||||
|
--
|
||||||
|
-- *You WILL HAVE TO REAPPLY AFTER EVERY DCS UPDATE*
|
||||||
|
--
|
||||||
|
-- # USAGE:
|
||||||
|
--
|
||||||
|
-- Add this script into the mission as a DO SCRIPT or DO SCRIPT FROM FILE to initialize it
|
||||||
|
-- Make sure to edit the STTS.SRS_PORT and STTS.DIRECTORY to the correct values before adding to the mission.
|
||||||
|
-- Then its as simple as calling the correct function in LUA as a DO SCRIPT or in your own scripts.
|
||||||
|
--
|
||||||
|
-- Example calls:
|
||||||
|
--
|
||||||
|
-- STTS.TextToSpeech("Hello DCS WORLD","251","AM","1.0","SRS",2)
|
||||||
|
--
|
||||||
|
-- Arguments in order are:
|
||||||
|
--
|
||||||
|
-- * Message to say, make sure not to use a newline (\n) !
|
||||||
|
-- * Frequency in MHz
|
||||||
|
-- * Modulation - AM/FM
|
||||||
|
-- * Volume - 1.0 max, 0.5 half
|
||||||
|
-- * Name of the transmitter - ATC, RockFM etc
|
||||||
|
-- * Coalition - 0 spectator, 1 red 2 blue
|
||||||
|
-- * OPTIONAL - Vec3 Point i.e Unit.getByName("A UNIT"):getPoint() - needs Vec3 for Height! OR null if not needed
|
||||||
|
-- * OPTIONAL - Speed -10 to +10
|
||||||
|
-- * OPTIONAL - Gender male, female or neuter
|
||||||
|
-- * OPTIONAL - Culture - en-US, en-GB etc
|
||||||
|
-- * OPTIONAL - Voice - a specific voice by name. Run DCS-SR-ExternalAudio.exe with --help to get the ones you can use on the command line
|
||||||
|
-- * OPTIONAL - Google TTS - Switch to Google Text To Speech - Requires STTS.GOOGLE_CREDENTIALS path and Google project setup correctly
|
||||||
|
--
|
||||||
|
--
|
||||||
|
-- ## Example
|
||||||
|
--
|
||||||
|
-- This example will say the words "Hello DCS WORLD" on 251 MHz AM at maximum volume with a client called SRS and to the Blue coalition only
|
||||||
|
--
|
||||||
|
-- STTS.TextToSpeech("Hello DCS WORLD","251","AM","1.0","SRS",2,null,-5,"male","en-GB")
|
||||||
|
--
|
||||||
|
-- ## Example
|
||||||
|
--
|
||||||
|
-- This example will say the words "Hello DCS WORLD" on 251 MHz AM at maximum volume with a client called SRS and to the Blue coalition only centered on the position of the Unit called "A UNIT"
|
||||||
|
--
|
||||||
|
-- STTS.TextToSpeech("Hello DCS WORLD","251","AM","1.0","SRS",2,Unit.getByName("A UNIT"):getPoint(),-5,"male","en-GB")
|
||||||
|
--
|
||||||
|
-- Arguments in order are:
|
||||||
|
--
|
||||||
|
-- * FULL path to the MP3 OR OGG to play
|
||||||
|
-- * Frequency in MHz - to use multiple separate with a comma - Number of frequencies MUST match number of Modulations
|
||||||
|
-- * Modulation - AM/FM - to use multiple
|
||||||
|
-- * Volume - 1.0 max, 0.5 half
|
||||||
|
-- * Name of the transmitter - ATC, RockFM etc
|
||||||
|
-- * Coalition - 0 spectator, 1 red 2 blue
|
||||||
|
--
|
||||||
|
-- ## Example
|
||||||
|
--
|
||||||
|
-- This will play that MP3 on 255MHz AM & 31 FM at half volume with a client called "Multiple" and to Spectators only
|
||||||
|
--
|
||||||
|
-- STTS.PlayMP3("C:\\Users\\Ciaran\\Downloads\\PR-Music.mp3","255,31","AM,FM","0.5","Multiple",0)
|
||||||
|
--
|
||||||
|
-- @field #STTS
|
||||||
|
STTS = {
|
||||||
|
ClassName = "STTS",
|
||||||
|
DIRECTORY = "",
|
||||||
|
SRS_PORT = 5002,
|
||||||
|
GOOGLE_CREDENTIALS = "C:\\Users\\Ciaran\\Downloads\\googletts.json",
|
||||||
|
EXECUTABLE = "DCS-SR-ExternalAudio.exe"
|
||||||
|
}
|
||||||
|
|
||||||
|
--- FULL Path to the FOLDER containing DCS-SR-ExternalAudio.exe - EDIT TO CORRECT FOLDER
|
||||||
|
STTS.DIRECTORY = "D:/DCS/_SRS"
|
||||||
|
|
||||||
|
--- LOCAL SRS PORT - DEFAULT IS 5002
|
||||||
|
STTS.SRS_PORT = 5002
|
||||||
|
|
||||||
|
--- Google credentials file
|
||||||
|
STTS.GOOGLE_CREDENTIALS = "C:\\Users\\Ciaran\\Downloads\\googletts.json"
|
||||||
|
|
||||||
|
--- DON'T CHANGE THIS UNLESS YOU KNOW WHAT YOU'RE DOING
|
||||||
|
STTS.EXECUTABLE = "DCS-SR-ExternalAudio.exe"
|
||||||
|
|
||||||
|
--- Function for UUID.
|
||||||
|
function STTS.uuid()
|
||||||
|
local random = math.random
|
||||||
|
local template = 'yxxx-xxxxxxxxxxxx'
|
||||||
|
return string.gsub( template, '[xy]', function( c )
|
||||||
|
local v = (c == 'x') and random( 0, 0xf ) or random( 8, 0xb )
|
||||||
|
return string.format( '%x', v )
|
||||||
|
end )
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Round a number.
|
||||||
|
-- @param #number x Number.
|
||||||
|
-- @param #number n Precision.
|
||||||
|
function STTS.round( x, n )
|
||||||
|
n = math.pow( 10, n or 0 )
|
||||||
|
x = x * n
|
||||||
|
if x >= 0 then
|
||||||
|
x = math.floor( x + 0.5 )
|
||||||
|
else
|
||||||
|
x = math.ceil( x - 0.5 )
|
||||||
|
end
|
||||||
|
return x / n
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Function returns estimated speech time in seconds.
|
||||||
|
-- Assumptions for time calc: 100 Words per min, average of 5 letters for english word so
|
||||||
|
--
|
||||||
|
-- * 5 chars * 100wpm = 500 characters per min = 8.3 chars per second
|
||||||
|
--
|
||||||
|
-- So length of msg / 8.3 = number of seconds needed to read it. rounded down to 8 chars per sec map function:
|
||||||
|
--
|
||||||
|
-- * (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
|
||||||
|
--
|
||||||
|
function STTS.getSpeechTime( length, speed, isGoogle )
|
||||||
|
|
||||||
|
local maxRateRatio = 3
|
||||||
|
|
||||||
|
speed = speed or 1.0
|
||||||
|
isGoogle = isGoogle or false
|
||||||
|
|
||||||
|
local speedFactor = 1.0
|
||||||
|
if isGoogle then
|
||||||
|
speedFactor = speed
|
||||||
|
else
|
||||||
|
if speed ~= 0 then
|
||||||
|
speedFactor = math.abs( speed ) * (maxRateRatio - 1) / 10 + 1
|
||||||
|
end
|
||||||
|
if speed < 0 then
|
||||||
|
speedFactor = 1 / speedFactor
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local wpm = math.ceil( 100 * speedFactor )
|
||||||
|
local cps = math.floor( (wpm * 5) / 60 )
|
||||||
|
|
||||||
|
if type( length ) == "string" then
|
||||||
|
length = string.len( length )
|
||||||
|
end
|
||||||
|
|
||||||
|
return math.ceil( length / cps )
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Text to speech function.
|
||||||
|
function STTS.TextToSpeech( message, freqs, modulations, volume, name, coalition, point, speed, gender, culture, voice, googleTTS )
|
||||||
|
if os == nil or io == nil then
|
||||||
|
env.info( "[DCS-STTS] LUA modules os or io are sanitized. skipping. " )
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
speed = speed or 1
|
||||||
|
gender = gender or "female"
|
||||||
|
culture = culture or ""
|
||||||
|
voice = voice or ""
|
||||||
|
coalition = coalition or "0"
|
||||||
|
name = name or "ROBOT"
|
||||||
|
volume = 1
|
||||||
|
speed = 1
|
||||||
|
|
||||||
|
message = message:gsub( "\"", "\\\"" )
|
||||||
|
|
||||||
|
local cmd = string.format( "start /min \"\" /d \"%s\" /b \"%s\" -f %s -m %s -c %s -p %s -n \"%s\" -h", STTS.DIRECTORY, STTS.EXECUTABLE, freqs or "305", modulations or "AM", coalition, STTS.SRS_PORT, name )
|
||||||
|
|
||||||
|
if voice ~= "" then
|
||||||
|
cmd = cmd .. string.format( " -V \"%s\"", voice )
|
||||||
|
else
|
||||||
|
|
||||||
|
if culture ~= "" then
|
||||||
|
cmd = cmd .. string.format( " -l %s", culture )
|
||||||
|
end
|
||||||
|
|
||||||
|
if gender ~= "" then
|
||||||
|
cmd = cmd .. string.format( " -g %s", gender )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if googleTTS == true then
|
||||||
|
cmd = cmd .. string.format( " -G \"%s\"", STTS.GOOGLE_CREDENTIALS )
|
||||||
|
end
|
||||||
|
|
||||||
|
if speed ~= 1 then
|
||||||
|
cmd = cmd .. string.format( " -s %s", speed )
|
||||||
|
end
|
||||||
|
|
||||||
|
if volume ~= 1.0 then
|
||||||
|
cmd = cmd .. string.format( " -v %s", volume )
|
||||||
|
end
|
||||||
|
|
||||||
|
if point and type( point ) == "table" and point.x then
|
||||||
|
local lat, lon, alt = coord.LOtoLL( point )
|
||||||
|
|
||||||
|
lat = STTS.round( lat, 4 )
|
||||||
|
lon = STTS.round( lon, 4 )
|
||||||
|
alt = math.floor( alt )
|
||||||
|
|
||||||
|
cmd = cmd .. string.format( " -L %s -O %s -A %s", lat, lon, alt )
|
||||||
|
end
|
||||||
|
|
||||||
|
cmd = cmd .. string.format( " -t \"%s\"", message )
|
||||||
|
|
||||||
|
if string.len( cmd ) > 255 then
|
||||||
|
local filename = os.getenv( 'TMP' ) .. "\\DCS_STTS-" .. STTS.uuid() .. ".bat"
|
||||||
|
local script = io.open( filename, "w+" )
|
||||||
|
script:write( cmd .. " && exit" )
|
||||||
|
script:close()
|
||||||
|
cmd = string.format( "\"%s\"", filename )
|
||||||
|
timer.scheduleFunction( os.remove, filename, timer.getTime() + 1 )
|
||||||
|
end
|
||||||
|
|
||||||
|
if string.len( cmd ) > 255 then
|
||||||
|
env.info( "[DCS-STTS] - cmd string too long" )
|
||||||
|
env.info( "[DCS-STTS] TextToSpeech Command :\n" .. cmd .. "\n" )
|
||||||
|
end
|
||||||
|
os.execute( cmd )
|
||||||
|
|
||||||
|
return STTS.getSpeechTime( message, speed, googleTTS )
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Play mp3 function.
|
||||||
|
-- @param #string pathToMP3 Path to the sound file.
|
||||||
|
-- @param #string freqs Frequencies, e.g. "305, 256".
|
||||||
|
-- @param #string modulations Modulations, e.g. "AM, FM".
|
||||||
|
-- @param #string volume Volume, e.g. "0.5".
|
||||||
|
function STTS.PlayMP3( pathToMP3, freqs, modulations, volume, name, coalition, point )
|
||||||
|
|
||||||
|
local cmd = string.format( "start \"\" /d \"%s\" /b /min \"%s\" -i \"%s\" -f %s -m %s -c %s -p %s -n \"%s\" -v %s -h", STTS.DIRECTORY, STTS.EXECUTABLE, pathToMP3, freqs or "305", modulations or "AM", coalition or "0", STTS.SRS_PORT, name or "ROBOT", volume or "1" )
|
||||||
|
|
||||||
|
if point and type( point ) == "table" and point.x then
|
||||||
|
local lat, lon, alt = coord.LOtoLL( point )
|
||||||
|
|
||||||
|
lat = STTS.round( lat, 4 )
|
||||||
|
lon = STTS.round( lon, 4 )
|
||||||
|
alt = math.floor( alt )
|
||||||
|
|
||||||
|
cmd = cmd .. string.format( " -L %s -O %s -A %s", lat, lon, alt )
|
||||||
|
end
|
||||||
|
|
||||||
|
env.info( "[DCS-STTS] MP3/OGG Command :\n" .. cmd .. "\n" )
|
||||||
|
os.execute( cmd )
|
||||||
|
|
||||||
|
end
|
||||||
612
Moose Development/Moose/Utilities/Templates.lua
Normal file
612
Moose Development/Moose/Utilities/Templates.lua
Normal file
@@ -0,0 +1,612 @@
|
|||||||
|
--- **Utils** Templates
|
||||||
|
--
|
||||||
|
-- DCS unit templates
|
||||||
|
--
|
||||||
|
-- @module Utilities.Templates
|
||||||
|
-- @image MOOSE.JPG
|
||||||
|
|
||||||
|
--- TEMPLATE class.
|
||||||
|
-- @type TEMPLATE
|
||||||
|
-- @field #string ClassName Name of the class.
|
||||||
|
|
||||||
|
--- *Templates*
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- 
|
||||||
|
--
|
||||||
|
-- Get DCS templates from thin air.
|
||||||
|
--
|
||||||
|
-- # Ground Units
|
||||||
|
--
|
||||||
|
-- Ground units.
|
||||||
|
--
|
||||||
|
-- # Naval Units
|
||||||
|
--
|
||||||
|
-- Ships are not implemented yet.
|
||||||
|
--
|
||||||
|
-- # Aircraft
|
||||||
|
--
|
||||||
|
-- ## Airplanes
|
||||||
|
--
|
||||||
|
-- Airplanes are not implemented yet.
|
||||||
|
--
|
||||||
|
-- ## Helicopters
|
||||||
|
--
|
||||||
|
-- Helicopters are not implemented yet.
|
||||||
|
--
|
||||||
|
-- @field #TEMPLATE
|
||||||
|
TEMPLATE = {
|
||||||
|
ClassName = "TEMPLATE",
|
||||||
|
Ground = {},
|
||||||
|
Naval = {},
|
||||||
|
Airplane = {},
|
||||||
|
Helicopter = {},
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Ground unit type names.
|
||||||
|
-- @type TEMPLATE.TypeGround
|
||||||
|
-- @param #string InfantryAK
|
||||||
|
TEMPLATE.TypeGround={
|
||||||
|
InfantryAK="Infantry AK",
|
||||||
|
ParatrooperAKS74="Paratrooper AKS-74",
|
||||||
|
ParatrooperRPG16="Paratrooper RPG-16",
|
||||||
|
SoldierWWIIUS="soldier_wwii_us",
|
||||||
|
InfantryM248="Infantry M249",
|
||||||
|
SoldierM4="Soldier M4",
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Naval unit type names.
|
||||||
|
-- @type TEMPLATE.TypeNaval
|
||||||
|
-- @param #string Ticonderoga
|
||||||
|
TEMPLATE.TypeNaval={
|
||||||
|
Ticonderoga="TICONDEROG",
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Rotary wing unit type names.
|
||||||
|
-- @type TEMPLATE.TypeAirplane
|
||||||
|
-- @param #string A10C
|
||||||
|
TEMPLATE.TypeAirplane={
|
||||||
|
A10C="A-10C",
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Rotary wing unit type names.
|
||||||
|
-- @type TEMPLATE.TypeHelicopter
|
||||||
|
-- @param #string AH1W
|
||||||
|
TEMPLATE.TypeHelicopter={
|
||||||
|
AH1W="AH-1W",
|
||||||
|
}
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Ground Template
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Get template for ground units.
|
||||||
|
-- @param #string TypeName Type name of the unit(s) in the groups. See `TEMPLATE.Ground`.
|
||||||
|
-- @param #string GroupName Name of the spawned group. **Must be unique!**
|
||||||
|
-- @param #number CountryID Country ID. Default `country.id.USA`. Coalition is automatically determined by the one the country belongs to.
|
||||||
|
-- @param DCS#Vec3 Vec3 Position of the group and the first unit.
|
||||||
|
-- @param #number Nunits Number of units. Default 1.
|
||||||
|
-- @param #number Radius Spawn radius for additonal units in meters. Default 50 m.
|
||||||
|
-- @return #table Template Template table.
|
||||||
|
function TEMPLATE.GetGround(TypeName, GroupName, CountryID, Vec3, Nunits, Radius)
|
||||||
|
|
||||||
|
-- Defaults.
|
||||||
|
TypeName=TypeName or TEMPLATE.TypeGround.SoldierM4
|
||||||
|
GroupName=GroupName or "Ground-1"
|
||||||
|
CountryID=CountryID or country.id.USA
|
||||||
|
Vec3=Vec3 or {x=0, y=0, z=0}
|
||||||
|
Nunits=Nunits or 1
|
||||||
|
Radius=Radius or 50
|
||||||
|
|
||||||
|
|
||||||
|
-- Get generic template.
|
||||||
|
local template=UTILS.DeepCopy(TEMPLATE.GenericGround)
|
||||||
|
|
||||||
|
-- Set group name.
|
||||||
|
template.name=GroupName
|
||||||
|
|
||||||
|
-- These are additional entries required by the MOOSE _DATABASE:Spawn() function.
|
||||||
|
template.CountryID=CountryID
|
||||||
|
template.CoalitionID=coalition.getCountryCoalition(template.CountryID)
|
||||||
|
template.CategoryID=Unit.Category.GROUND_UNIT
|
||||||
|
|
||||||
|
-- Set first unit.
|
||||||
|
template.units[1].type=TypeName
|
||||||
|
template.units[1].name=GroupName.."-1"
|
||||||
|
|
||||||
|
if Vec3 then
|
||||||
|
TEMPLATE.SetPositionFromVec3(template, Vec3)
|
||||||
|
end
|
||||||
|
|
||||||
|
TEMPLATE.SetUnits(template, Nunits, COORDINATE:NewFromVec3(Vec3), Radius)
|
||||||
|
|
||||||
|
return template
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Naval Template
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Get template for ground units.
|
||||||
|
-- @param #string TypeName Type name of the unit(s) in the groups. See `TEMPLATE.Ground`.
|
||||||
|
-- @param #string GroupName Name of the spawned group. **Must be unique!**
|
||||||
|
-- @param #number CountryID Country ID. Default `country.id.USA`. Coalition is automatically determined by the one the country belongs to.
|
||||||
|
-- @param DCS#Vec3 Vec3 Position of the group and the first unit.
|
||||||
|
-- @param #number Nunits Number of units. Default 1.
|
||||||
|
-- @param #number Radius Spawn radius for additonal units in meters. Default 500 m.
|
||||||
|
-- @return #table Template Template table.
|
||||||
|
function TEMPLATE.GetNaval(TypeName, GroupName, CountryID, Vec3, Nunits, Radius)
|
||||||
|
|
||||||
|
-- Defaults.
|
||||||
|
TypeName=TypeName or TEMPLATE.TypeNaval.Ticonderoga
|
||||||
|
GroupName=GroupName or "Naval-1"
|
||||||
|
CountryID=CountryID or country.id.USA
|
||||||
|
Vec3=Vec3 or {x=0, y=0, z=0}
|
||||||
|
Nunits=Nunits or 1
|
||||||
|
Radius=Radius or 500
|
||||||
|
|
||||||
|
|
||||||
|
-- Get generic template.
|
||||||
|
local template=UTILS.DeepCopy(TEMPLATE.GenericNaval)
|
||||||
|
|
||||||
|
-- Set group name.
|
||||||
|
template.name=GroupName
|
||||||
|
|
||||||
|
-- These are additional entries required by the MOOSE _DATABASE:Spawn() function.
|
||||||
|
template.CountryID=CountryID
|
||||||
|
template.CoalitionID=coalition.getCountryCoalition(template.CountryID)
|
||||||
|
template.CategoryID=Unit.Category.SHIP
|
||||||
|
|
||||||
|
-- Set first unit.
|
||||||
|
template.units[1].type=TypeName
|
||||||
|
template.units[1].name=GroupName.."-1"
|
||||||
|
|
||||||
|
if Vec3 then
|
||||||
|
TEMPLATE.SetPositionFromVec3(template, Vec3)
|
||||||
|
end
|
||||||
|
|
||||||
|
TEMPLATE.SetUnits(template, Nunits, COORDINATE:NewFromVec3(Vec3), Radius)
|
||||||
|
|
||||||
|
return template
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Aircraft Template
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Get template for fixed wing units.
|
||||||
|
-- @param #string TypeName Type name of the unit(s) in the groups. See `TEMPLATE.Ground`.
|
||||||
|
-- @param #string GroupName Name of the spawned group. **Must be unique!**
|
||||||
|
-- @param #number CountryID Country ID. Default `country.id.USA`. Coalition is automatically determined by the one the country belongs to.
|
||||||
|
-- @param DCS#Vec3 Vec3 Position of the group and the first unit.
|
||||||
|
-- @param #number Nunits Number of units. Default 1.
|
||||||
|
-- @param #number Radius Spawn radius for additonal units in meters. Default 500 m.
|
||||||
|
-- @return #table Template Template table.
|
||||||
|
function TEMPLATE.GetAirplane(TypeName, GroupName, CountryID, Vec3, Nunits, Radius)
|
||||||
|
|
||||||
|
-- Defaults.
|
||||||
|
TypeName=TypeName or TEMPLATE.TypeAirplane.A10C
|
||||||
|
GroupName=GroupName or "Airplane-1"
|
||||||
|
CountryID=CountryID or country.id.USA
|
||||||
|
Vec3=Vec3 or {x=0, y=1000, z=0}
|
||||||
|
Nunits=Nunits or 1
|
||||||
|
Radius=Radius or 100
|
||||||
|
|
||||||
|
local template=TEMPLATE._GetAircraft(true, TypeName, GroupName, CountryID, Vec3, Nunits, Radius)
|
||||||
|
|
||||||
|
return template
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get template for fixed wing units.
|
||||||
|
-- @param #string TypeName Type name of the unit(s) in the groups. See `TEMPLATE.Ground`.
|
||||||
|
-- @param #string GroupName Name of the spawned group. **Must be unique!**
|
||||||
|
-- @param #number CountryID Country ID. Default `country.id.USA`. Coalition is automatically determined by the one the country belongs to.
|
||||||
|
-- @param DCS#Vec3 Vec3 Position of the group and the first unit.
|
||||||
|
-- @param #number Nunits Number of units. Default 1.
|
||||||
|
-- @param #number Radius Spawn radius for additonal units in meters. Default 500 m.
|
||||||
|
-- @return #table Template Template table.
|
||||||
|
function TEMPLATE.GetHelicopter(TypeName, GroupName, CountryID, Vec3, Nunits, Radius)
|
||||||
|
|
||||||
|
-- Defaults.
|
||||||
|
TypeName=TypeName or TEMPLATE.TypeHelicopter.AH1W
|
||||||
|
GroupName=GroupName or "Helicopter-1"
|
||||||
|
CountryID=CountryID or country.id.USA
|
||||||
|
Vec3=Vec3 or {x=0, y=500, z=0}
|
||||||
|
Nunits=Nunits or 1
|
||||||
|
Radius=Radius or 100
|
||||||
|
|
||||||
|
-- Limit unis to 4.
|
||||||
|
Nunits=math.min(Nunits, 4)
|
||||||
|
|
||||||
|
local template=TEMPLATE._GetAircraft(false, TypeName, GroupName, CountryID, Vec3, Nunits, Radius)
|
||||||
|
|
||||||
|
return template
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Get template for aircraft units.
|
||||||
|
-- @param #boolean Airplane If true, this is a fixed wing. Else, rotary wing.
|
||||||
|
-- @param #string TypeName Type name of the unit(s) in the groups. See `TEMPLATE.Ground`.
|
||||||
|
-- @param #string GroupName Name of the spawned group. **Must be unique!**
|
||||||
|
-- @param #number CountryID Country ID. Default `country.id.USA`. Coalition is automatically determined by the one the country belongs to.
|
||||||
|
-- @param DCS#Vec3 Vec3 Position of the group and the first unit.
|
||||||
|
-- @param #number Nunits Number of units. Default 1.
|
||||||
|
-- @param #number Radius Spawn radius for additonal units in meters. Default 500 m.
|
||||||
|
-- @return #table Template Template table.
|
||||||
|
function TEMPLATE._GetAircraft(Airplane, TypeName, GroupName, CountryID, Vec3, Nunits, Radius)
|
||||||
|
|
||||||
|
-- Defaults.
|
||||||
|
TypeName=TypeName
|
||||||
|
GroupName=GroupName or "Aircraft-1"
|
||||||
|
CountryID=CountryID or country.id.USA
|
||||||
|
Vec3=Vec3 or {x=0, y=0, z=0}
|
||||||
|
Nunits=Nunits or 1
|
||||||
|
Radius=Radius or 100
|
||||||
|
|
||||||
|
-- Get generic template.
|
||||||
|
local template=UTILS.DeepCopy(TEMPLATE.GenericAircraft)
|
||||||
|
|
||||||
|
-- Set group name.
|
||||||
|
template.name=GroupName
|
||||||
|
|
||||||
|
-- These are additional entries required by the MOOSE _DATABASE:Spawn() function.
|
||||||
|
template.CountryID=CountryID
|
||||||
|
template.CoalitionID=coalition.getCountryCoalition(template.CountryID)
|
||||||
|
if Airplane then
|
||||||
|
template.CategoryID=Unit.Category.AIRPLANE
|
||||||
|
else
|
||||||
|
template.CategoryID=Unit.Category.HELICOPTER
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set first unit.
|
||||||
|
template.units[1].type=TypeName
|
||||||
|
template.units[1].name=GroupName.."-1"
|
||||||
|
|
||||||
|
-- Set position.
|
||||||
|
if Vec3 then
|
||||||
|
TEMPLATE.SetPositionFromVec3(template, Vec3)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set number of units.
|
||||||
|
TEMPLATE.SetUnits(template, Nunits, COORDINATE:NewFromVec3(Vec3), Radius)
|
||||||
|
|
||||||
|
return template
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Misc Functions
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Set the position of the template.
|
||||||
|
-- @param #table Template The template to be modified.
|
||||||
|
-- @param DCS#Vec2 Vec2 2D Position vector with x and y components of the group.
|
||||||
|
function TEMPLATE.SetPositionFromVec2(Template, Vec2)
|
||||||
|
|
||||||
|
Template.x=Vec2.x
|
||||||
|
Template.y=Vec2.y
|
||||||
|
|
||||||
|
for _,unit in pairs(Template.units) do
|
||||||
|
unit.x=Vec2.x
|
||||||
|
unit.y=Vec2.y
|
||||||
|
end
|
||||||
|
|
||||||
|
Template.route.points[1].x=Vec2.x
|
||||||
|
Template.route.points[1].y=Vec2.y
|
||||||
|
Template.route.points[1].alt=0 --TODO: Use land height.
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the position of the template.
|
||||||
|
-- @param #table Template The template to be modified.
|
||||||
|
-- @param DCS#Vec3 Vec3 Position vector of the group.
|
||||||
|
function TEMPLATE.SetPositionFromVec3(Template, Vec3)
|
||||||
|
|
||||||
|
local Vec2={x=Vec3.x, y=Vec3.z}
|
||||||
|
|
||||||
|
TEMPLATE.SetPositionFromVec2(Template, Vec2)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the position of the template.
|
||||||
|
-- @param #table Template The template to be modified.
|
||||||
|
-- @param #number N Total number of units in the group.
|
||||||
|
-- @param Core.Point#COORDINATE Coordinate Position of the first unit.
|
||||||
|
-- @param #number Radius Radius in meters to randomly place the additional units.
|
||||||
|
function TEMPLATE.SetUnits(Template, N, Coordinate, Radius)
|
||||||
|
|
||||||
|
local units=Template.units
|
||||||
|
|
||||||
|
local unit1=units[1]
|
||||||
|
|
||||||
|
local Vec3=Coordinate:GetVec3()
|
||||||
|
|
||||||
|
unit1.x=Vec3.x
|
||||||
|
unit1.y=Vec3.z
|
||||||
|
unit1.alt=Vec3.y
|
||||||
|
|
||||||
|
for i=2,N do
|
||||||
|
units[i]=UTILS.DeepCopy(unit1)
|
||||||
|
end
|
||||||
|
|
||||||
|
for i=1,N do
|
||||||
|
local unit=units[i]
|
||||||
|
unit.name=string.format("%s-%d", Template.name, i)
|
||||||
|
if i>1 then
|
||||||
|
local vec2=Coordinate:GetRandomCoordinateInRadius(Radius, 5):GetVec2()
|
||||||
|
unit.x=vec2.x
|
||||||
|
unit.y=vec2.y
|
||||||
|
unit.alt=unit1.alt
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the position of the template.
|
||||||
|
-- @param #table Template The template to be modified.
|
||||||
|
-- @param Wrapper.Airbase#AIRBASE AirBase The airbase where the aircraft are spawned.
|
||||||
|
-- @param #table ParkingSpots List of parking spot IDs. Every unit needs one!
|
||||||
|
-- @param #boolean EngineOn If true, aircraft are spawned hot.
|
||||||
|
function TEMPLATE.SetAirbase(Template, AirBase, ParkingSpots, EngineOn)
|
||||||
|
|
||||||
|
-- Airbase ID.
|
||||||
|
local AirbaseID=AirBase:GetID()
|
||||||
|
|
||||||
|
-- Spawn point.
|
||||||
|
local point=Template.route.points[1]
|
||||||
|
|
||||||
|
-- Set ID.
|
||||||
|
if AirBase:IsAirdrome() then
|
||||||
|
point.airdromeId=AirbaseID
|
||||||
|
else
|
||||||
|
point.helipadId=AirbaseID
|
||||||
|
point.linkUnit=AirbaseID
|
||||||
|
end
|
||||||
|
|
||||||
|
if EngineOn then
|
||||||
|
point.action=COORDINATE.WaypointAction.FromParkingAreaHot
|
||||||
|
point.type=COORDINATE.WaypointType.TakeOffParkingHot
|
||||||
|
else
|
||||||
|
point.action=COORDINATE.WaypointAction.FromParkingArea
|
||||||
|
point.type=COORDINATE.WaypointType.TakeOffParking
|
||||||
|
end
|
||||||
|
|
||||||
|
for i,unit in ipairs(Template.units) do
|
||||||
|
unit.parking_id=ParkingSpots[i]
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Add a waypoint.
|
||||||
|
-- @param #table Template The template to be modified.
|
||||||
|
-- @param #table Waypoint Waypoint table.
|
||||||
|
function TEMPLATE.AddWaypoint(Template, Waypoint)
|
||||||
|
|
||||||
|
table.insert(Template.route.points, Waypoint)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Generic Ground Template
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
TEMPLATE.GenericGround=
|
||||||
|
{
|
||||||
|
["visible"] = false,
|
||||||
|
["tasks"] = {}, -- end of ["tasks"]
|
||||||
|
["uncontrollable"] = false,
|
||||||
|
["task"] = "Ground Nothing",
|
||||||
|
["route"] =
|
||||||
|
{
|
||||||
|
["spans"] = {}, -- end of ["spans"]
|
||||||
|
["points"] =
|
||||||
|
{
|
||||||
|
[1] =
|
||||||
|
{
|
||||||
|
["alt"] = 0,
|
||||||
|
["type"] = "Turning Point",
|
||||||
|
["ETA"] = 0,
|
||||||
|
["alt_type"] = "BARO",
|
||||||
|
["formation_template"] = "",
|
||||||
|
["y"] = 0,
|
||||||
|
["x"] = 0,
|
||||||
|
["ETA_locked"] = true,
|
||||||
|
["speed"] = 0,
|
||||||
|
["action"] = "Off Road",
|
||||||
|
["task"] =
|
||||||
|
{
|
||||||
|
["id"] = "ComboTask",
|
||||||
|
["params"] =
|
||||||
|
{
|
||||||
|
["tasks"] =
|
||||||
|
{
|
||||||
|
}, -- end of ["tasks"]
|
||||||
|
}, -- end of ["params"]
|
||||||
|
}, -- end of ["task"]
|
||||||
|
["speed_locked"] = true,
|
||||||
|
}, -- end of [1]
|
||||||
|
}, -- end of ["points"]
|
||||||
|
}, -- end of ["route"]
|
||||||
|
["groupId"] = nil,
|
||||||
|
["hidden"] = false,
|
||||||
|
["units"] =
|
||||||
|
{
|
||||||
|
[1] =
|
||||||
|
{
|
||||||
|
["transportable"] =
|
||||||
|
{
|
||||||
|
["randomTransportable"] = false,
|
||||||
|
}, -- end of ["transportable"]
|
||||||
|
["skill"] = "Average",
|
||||||
|
["type"] = "Infantry AK",
|
||||||
|
["unitId"] = nil,
|
||||||
|
["y"] = 0,
|
||||||
|
["x"] = 0,
|
||||||
|
["name"] = "Infantry AK-47 Rus",
|
||||||
|
["heading"] = 0,
|
||||||
|
["playerCanDrive"] = false,
|
||||||
|
}, -- end of [1]
|
||||||
|
}, -- end of ["units"]
|
||||||
|
["y"] = 0,
|
||||||
|
["x"] = 0,
|
||||||
|
["name"] = "Infantry AK-47 Rus",
|
||||||
|
["start_time"] = 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Generic Ship Template
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
TEMPLATE.GenericNaval=
|
||||||
|
{
|
||||||
|
["visible"] = false,
|
||||||
|
["tasks"] = {}, -- end of ["tasks"]
|
||||||
|
["uncontrollable"] = false,
|
||||||
|
["route"] =
|
||||||
|
{
|
||||||
|
["points"] =
|
||||||
|
{
|
||||||
|
[1] =
|
||||||
|
{
|
||||||
|
["alt"] = 0,
|
||||||
|
["type"] = "Turning Point",
|
||||||
|
["ETA"] = 0,
|
||||||
|
["alt_type"] = "BARO",
|
||||||
|
["formation_template"] = "",
|
||||||
|
["y"] = 0,
|
||||||
|
["x"] = 0,
|
||||||
|
["ETA_locked"] = true,
|
||||||
|
["speed"] = 0,
|
||||||
|
["action"] = "Turning Point",
|
||||||
|
["task"] =
|
||||||
|
{
|
||||||
|
["id"] = "ComboTask",
|
||||||
|
["params"] =
|
||||||
|
{
|
||||||
|
["tasks"] =
|
||||||
|
{
|
||||||
|
}, -- end of ["tasks"]
|
||||||
|
}, -- end of ["params"]
|
||||||
|
}, -- end of ["task"]
|
||||||
|
["speed_locked"] = true,
|
||||||
|
}, -- end of [1]
|
||||||
|
}, -- end of ["points"]
|
||||||
|
}, -- end of ["route"]
|
||||||
|
["groupId"] = nil,
|
||||||
|
["hidden"] = false,
|
||||||
|
["units"] =
|
||||||
|
{
|
||||||
|
[1] =
|
||||||
|
{
|
||||||
|
["transportable"] =
|
||||||
|
{
|
||||||
|
["randomTransportable"] = false,
|
||||||
|
}, -- end of ["transportable"]
|
||||||
|
["skill"] = "Average",
|
||||||
|
["type"] = "TICONDEROG",
|
||||||
|
["unitId"] = nil,
|
||||||
|
["y"] = 0,
|
||||||
|
["x"] = 0,
|
||||||
|
["name"] = "Naval-1-1",
|
||||||
|
["heading"] = 0,
|
||||||
|
["modulation"] = 0,
|
||||||
|
["frequency"] = 127500000,
|
||||||
|
}, -- end of [1]
|
||||||
|
}, -- end of ["units"]
|
||||||
|
["y"] = 0,
|
||||||
|
["x"] = 0,
|
||||||
|
["name"] = "Naval-1",
|
||||||
|
["start_time"] = 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Generic Aircraft Template
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
TEMPLATE.GenericAircraft=
|
||||||
|
{
|
||||||
|
["groupId"] = nil,
|
||||||
|
["name"] = "Rotary-1",
|
||||||
|
["uncontrolled"] = false,
|
||||||
|
["hidden"] = false,
|
||||||
|
["task"] = "Nothing",
|
||||||
|
["y"] = 0,
|
||||||
|
["x"] = 0,
|
||||||
|
["start_time"] = 0,
|
||||||
|
["communication"] = true,
|
||||||
|
["radioSet"] = false,
|
||||||
|
["frequency"] = 127.5,
|
||||||
|
["modulation"] = 0,
|
||||||
|
["taskSelected"] = true,
|
||||||
|
["tasks"] = {}, -- end of ["tasks"]
|
||||||
|
["route"] =
|
||||||
|
{
|
||||||
|
["points"] =
|
||||||
|
{
|
||||||
|
[1] =
|
||||||
|
{
|
||||||
|
["y"] = 0,
|
||||||
|
["x"] = 0,
|
||||||
|
["alt"] = 1000,
|
||||||
|
["alt_type"] = "BARO",
|
||||||
|
["action"] = "Turning Point",
|
||||||
|
["type"] = "Turning Point",
|
||||||
|
["airdromeId"] = nil,
|
||||||
|
["task"] =
|
||||||
|
{
|
||||||
|
["id"] = "ComboTask",
|
||||||
|
["params"] =
|
||||||
|
{
|
||||||
|
["tasks"] = {}, -- end of ["tasks"]
|
||||||
|
}, -- end of ["params"]
|
||||||
|
}, -- end of ["task"]
|
||||||
|
["ETA"] = 0,
|
||||||
|
["ETA_locked"] = true,
|
||||||
|
["speed"] = 100,
|
||||||
|
["speed_locked"] = true,
|
||||||
|
["formation_template"] = "",
|
||||||
|
}, -- end of [1]
|
||||||
|
}, -- end of ["points"]
|
||||||
|
}, -- end of ["route"]
|
||||||
|
["units"] =
|
||||||
|
{
|
||||||
|
[1] =
|
||||||
|
{
|
||||||
|
["name"] = "Rotary-1-1",
|
||||||
|
["unitId"] = nil,
|
||||||
|
["type"] = "AH-1W",
|
||||||
|
["onboard_num"] = "050",
|
||||||
|
["livery_id"] = "USA X Black",
|
||||||
|
["skill"] = "High",
|
||||||
|
["ropeLength"] = 15,
|
||||||
|
["speed"] = 0,
|
||||||
|
["x"] = 0,
|
||||||
|
["y"] = 0,
|
||||||
|
["alt"] = 10,
|
||||||
|
["alt_type"] = "BARO",
|
||||||
|
["heading"] = 0,
|
||||||
|
["psi"] = 0,
|
||||||
|
["parking"] = nil,
|
||||||
|
["parking_id"] = nil,
|
||||||
|
["payload"] =
|
||||||
|
{
|
||||||
|
["pylons"] = {}, -- end of ["pylons"]
|
||||||
|
["fuel"] = "1250.0",
|
||||||
|
["flare"] = 30,
|
||||||
|
["chaff"] = 30,
|
||||||
|
["gun"] = 100,
|
||||||
|
}, -- end of ["payload"]
|
||||||
|
["callsign"] =
|
||||||
|
{
|
||||||
|
[1] = 2,
|
||||||
|
[2] = 1,
|
||||||
|
[3] = 1,
|
||||||
|
["name"] = "Springfield11",
|
||||||
|
}, -- end of ["callsign"]
|
||||||
|
}, -- end of [1]
|
||||||
|
}, -- end of ["units"]
|
||||||
|
}
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -17,6 +17,7 @@
|
|||||||
-- @field #table CategoryName Names of airbase categories.
|
-- @field #table CategoryName Names of airbase categories.
|
||||||
-- @field #string AirbaseName Name of the airbase.
|
-- @field #string AirbaseName Name of the airbase.
|
||||||
-- @field #number AirbaseID Airbase ID.
|
-- @field #number AirbaseID Airbase ID.
|
||||||
|
-- @field Core.Zone#ZONE AirbaseZone Circular zone around the airbase with a radius of 2500 meters. For ships this is a ZONE_UNIT object.
|
||||||
-- @field #number category Airbase category.
|
-- @field #number category Airbase category.
|
||||||
-- @field #table descriptors DCS descriptors.
|
-- @field #table descriptors DCS descriptors.
|
||||||
-- @field #boolean isAirdrome Airbase is an airdrome.
|
-- @field #boolean isAirdrome Airbase is an airdrome.
|
||||||
@@ -25,6 +26,8 @@
|
|||||||
-- @field #table parking Parking spot data.
|
-- @field #table parking Parking spot data.
|
||||||
-- @field #table parkingByID Parking spot data table with ID as key.
|
-- @field #table parkingByID Parking spot data table with ID as key.
|
||||||
-- @field #number activerwyno Active runway number (forced).
|
-- @field #number activerwyno Active runway number (forced).
|
||||||
|
-- @field #table parkingWhitelist List of parking spot terminal IDs considered for spawning.
|
||||||
|
-- @field #table parkingBlacklist List of parking spot terminal IDs **not** considered for spawning.
|
||||||
-- @extends Wrapper.Positionable#POSITIONABLE
|
-- @extends Wrapper.Positionable#POSITIONABLE
|
||||||
|
|
||||||
--- Wrapper class to handle the DCS Airbase objects:
|
--- Wrapper class to handle the DCS Airbase objects:
|
||||||
@@ -71,7 +74,7 @@ AIRBASE = {
|
|||||||
|
|
||||||
--- Enumeration to identify the airbases in the Caucasus region.
|
--- Enumeration to identify the airbases in the Caucasus region.
|
||||||
--
|
--
|
||||||
-- These are all airbases of Caucasus:
|
-- Airbases of the Caucasus map:
|
||||||
--
|
--
|
||||||
-- * AIRBASE.Caucasus.Gelendzhik
|
-- * AIRBASE.Caucasus.Gelendzhik
|
||||||
-- * AIRBASE.Caucasus.Krasnodar_Pashkovsky
|
-- * AIRBASE.Caucasus.Krasnodar_Pashkovsky
|
||||||
@@ -120,7 +123,7 @@ AIRBASE.Caucasus = {
|
|||||||
["Beslan"] = "Beslan",
|
["Beslan"] = "Beslan",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- These are all airbases of Nevada:
|
--- Airbases of the Nevada map:
|
||||||
--
|
--
|
||||||
-- * AIRBASE.Nevada.Creech_AFB
|
-- * AIRBASE.Nevada.Creech_AFB
|
||||||
-- * AIRBASE.Nevada.Groom_Lake_AFB
|
-- * AIRBASE.Nevada.Groom_Lake_AFB
|
||||||
@@ -139,6 +142,7 @@ AIRBASE.Caucasus = {
|
|||||||
-- * AIRBASE.Nevada.Pahute_Mesa_Airstrip
|
-- * AIRBASE.Nevada.Pahute_Mesa_Airstrip
|
||||||
-- * AIRBASE.Nevada.Tonopah_Airport
|
-- * AIRBASE.Nevada.Tonopah_Airport
|
||||||
-- * AIRBASE.Nevada.Tonopah_Test_Range_Airfield
|
-- * AIRBASE.Nevada.Tonopah_Test_Range_Airfield
|
||||||
|
--
|
||||||
-- @field Nevada
|
-- @field Nevada
|
||||||
AIRBASE.Nevada = {
|
AIRBASE.Nevada = {
|
||||||
["Creech_AFB"] = "Creech AFB",
|
["Creech_AFB"] = "Creech AFB",
|
||||||
@@ -160,7 +164,7 @@ AIRBASE.Nevada = {
|
|||||||
["Tonopah_Test_Range_Airfield"] = "Tonopah Test Range Airfield",
|
["Tonopah_Test_Range_Airfield"] = "Tonopah Test Range Airfield",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- These are all airbases of Normandy:
|
--- Airbases of the Normandy map:
|
||||||
--
|
--
|
||||||
-- * AIRBASE.Normandy.Saint_Pierre_du_Mont
|
-- * AIRBASE.Normandy.Saint_Pierre_du_Mont
|
||||||
-- * AIRBASE.Normandy.Lignerolles
|
-- * AIRBASE.Normandy.Lignerolles
|
||||||
@@ -193,6 +197,7 @@ AIRBASE.Nevada = {
|
|||||||
-- * AIRBASE.Normandy.Funtington
|
-- * AIRBASE.Normandy.Funtington
|
||||||
-- * AIRBASE.Normandy.Tangmere
|
-- * AIRBASE.Normandy.Tangmere
|
||||||
-- * AIRBASE.Normandy.Ford_AF
|
-- * AIRBASE.Normandy.Ford_AF
|
||||||
|
--
|
||||||
-- @field Normandy
|
-- @field Normandy
|
||||||
AIRBASE.Normandy = {
|
AIRBASE.Normandy = {
|
||||||
["Saint_Pierre_du_Mont"] = "Saint Pierre du Mont",
|
["Saint_Pierre_du_Mont"] = "Saint Pierre du Mont",
|
||||||
@@ -235,7 +240,7 @@ AIRBASE.Normandy = {
|
|||||||
["Conches"] = "Conches",
|
["Conches"] = "Conches",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- These are all airbases of the Persion Gulf Map:
|
--- Airbases of the Persion Gulf Map:
|
||||||
--
|
--
|
||||||
-- * AIRBASE.PersianGulf.Abu_Dhabi_International_Airport
|
-- * AIRBASE.PersianGulf.Abu_Dhabi_International_Airport
|
||||||
-- * AIRBASE.PersianGulf.Abu_Musa_Island_Airport
|
-- * AIRBASE.PersianGulf.Abu_Musa_Island_Airport
|
||||||
@@ -266,40 +271,41 @@ AIRBASE.Normandy = {
|
|||||||
-- * AIRBASE.PersianGulf.Sirri_Island
|
-- * AIRBASE.PersianGulf.Sirri_Island
|
||||||
-- * AIRBASE.PersianGulf.Tunb_Island_AFB
|
-- * AIRBASE.PersianGulf.Tunb_Island_AFB
|
||||||
-- * AIRBASE.PersianGulf.Tunb_Kochak
|
-- * AIRBASE.PersianGulf.Tunb_Kochak
|
||||||
|
--
|
||||||
-- @field PersianGulf
|
-- @field PersianGulf
|
||||||
AIRBASE.PersianGulf = {
|
AIRBASE.PersianGulf = {
|
||||||
["Abu_Dhabi_International_Airport"] = "Abu Dhabi International Airport",
|
["Abu_Dhabi_International_Airport"] = "Abu Dhabi Intl",
|
||||||
["Abu_Musa_Island_Airport"] = "Abu Musa Island Airport",
|
["Abu_Musa_Island_Airport"] = "Abu Musa Island",
|
||||||
["Al_Ain_International_Airport"] = "Al Ain International Airport",
|
["Al_Ain_International_Airport"] = "Al Ain Intl",
|
||||||
["Al_Bateen_Airport"] = "Al-Bateen Airport",
|
["Al_Bateen_Airport"] = "Al-Bateen",
|
||||||
["Al_Dhafra_AB"] = "Al Dhafra AB",
|
["Al_Dhafra_AB"] = "Al Dhafra AFB",
|
||||||
["Al_Maktoum_Intl"] = "Al Maktoum Intl",
|
["Al_Maktoum_Intl"] = "Al Maktoum Intl",
|
||||||
["Al_Minhad_AB"] = "Al Minhad AB",
|
["Al_Minhad_AB"] = "Al Minhad AFB",
|
||||||
["Bandar_Abbas_Intl"] = "Bandar Abbas Intl",
|
["Bandar_Abbas_Intl"] = "Bandar Abbas Intl",
|
||||||
["Bandar_Lengeh"] = "Bandar Lengeh",
|
["Bandar_Lengeh"] = "Bandar Lengeh",
|
||||||
["Bandar_e_Jask_airfield"] = "Bandar-e-Jask airfield",
|
["Bandar_e_Jask_airfield"] = "Bandar-e-Jask",
|
||||||
["Dubai_Intl"] = "Dubai Intl",
|
["Dubai_Intl"] = "Dubai Intl",
|
||||||
["Fujairah_Intl"] = "Fujairah Intl",
|
["Fujairah_Intl"] = "Fujairah Intl",
|
||||||
["Havadarya"] = "Havadarya",
|
["Havadarya"] = "Havadarya",
|
||||||
["Jiroft_Airport"] = "Jiroft Airport",
|
["Jiroft_Airport"] = "Jiroft",
|
||||||
["Kerman_Airport"] = "Kerman Airport",
|
["Kerman_Airport"] = "Kerman",
|
||||||
["Khasab"] = "Khasab",
|
["Khasab"] = "Khasab",
|
||||||
["Kish_International_Airport"] = "Kish International Airport",
|
["Kish_International_Airport"] = "Kish Intl",
|
||||||
["Lar_Airbase"] = "Lar Airbase",
|
["Lar_Airbase"] = "Lar",
|
||||||
["Lavan_Island_Airport"] = "Lavan Island Airport",
|
["Lavan_Island_Airport"] = "Lavan Island",
|
||||||
["Liwa_Airbase"] = "Liwa Airbase",
|
["Liwa_Airbase"] = "Liwa AFB",
|
||||||
["Qeshm_Island"] = "Qeshm Island",
|
["Qeshm_Island"] = "Qeshm Island",
|
||||||
["Ras_Al_Khaimah"] = "Ras Al Khaimah",
|
["Ras_Al_Khaimah"] = "Ras Al Khaimah Intl",
|
||||||
["Sas_Al_Nakheel_Airport"] = "Sas Al Nakheel Airport",
|
["Sas_Al_Nakheel_Airport"] = "Sas Al Nakheel",
|
||||||
["Sharjah_Intl"] = "Sharjah Intl",
|
["Sharjah_Intl"] = "Sharjah Intl",
|
||||||
["Shiraz_International_Airport"] = "Shiraz International Airport",
|
["Shiraz_International_Airport"] = "Shiraz Intl",
|
||||||
["Sir_Abu_Nuayr"] = "Sir Abu Nuayr",
|
["Sir_Abu_Nuayr"] = "Sir Abu Nuayr",
|
||||||
["Sirri_Island"] = "Sirri Island",
|
["Sirri_Island"] = "Sirri Island",
|
||||||
["Tunb_Island_AFB"] = "Tunb Island AFB",
|
["Tunb_Island_AFB"] = "Tunb Island AFB",
|
||||||
["Tunb_Kochak"] = "Tunb Kochak",
|
["Tunb_Kochak"] = "Tunb Kochak",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- These are all airbases of the The Channel Map:
|
--- Airbases of The Channel Map:
|
||||||
--
|
--
|
||||||
-- * AIRBASE.TheChannel.Abbeville_Drucat
|
-- * AIRBASE.TheChannel.Abbeville_Drucat
|
||||||
-- * AIRBASE.TheChannel.Merville_Calonne
|
-- * AIRBASE.TheChannel.Merville_Calonne
|
||||||
@@ -324,7 +330,7 @@ AIRBASE.TheChannel = {
|
|||||||
["High_Halden"] = "High Halden",
|
["High_Halden"] = "High Halden",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Airbases of Syria
|
--- Airbases of the Syria map:
|
||||||
--
|
--
|
||||||
-- * AIRBASE.Syria.Kuweires
|
-- * AIRBASE.Syria.Kuweires
|
||||||
-- * AIRBASE.Syria.Marj_Ruhayyil
|
-- * AIRBASE.Syria.Marj_Ruhayyil
|
||||||
@@ -334,33 +340,53 @@ AIRBASE.TheChannel = {
|
|||||||
-- * AIRBASE.Syria.Incirlik
|
-- * AIRBASE.Syria.Incirlik
|
||||||
-- * AIRBASE.Syria.Damascus
|
-- * AIRBASE.Syria.Damascus
|
||||||
-- * AIRBASE.Syria.Bassel_Al_Assad
|
-- * AIRBASE.Syria.Bassel_Al_Assad
|
||||||
|
-- * AIRBASE.Syria.Rosh_Pina
|
||||||
-- * AIRBASE.Syria.Aleppo
|
-- * AIRBASE.Syria.Aleppo
|
||||||
-- * AIRBASE.Syria.Qabr_as_Sitt
|
-- * AIRBASE.Syria.Al_Qusayr
|
||||||
-- * AIRBASE.Syria.Wujah_Al_Hajar
|
-- * AIRBASE.Syria.Wujah_Al_Hajar
|
||||||
-- * AIRBASE.Syria.Al_Dumayr
|
-- * AIRBASE.Syria.Al_Dumayr
|
||||||
|
-- * AIRBASE.Syria.Gazipasa
|
||||||
|
-- * AIRBASE.Syria.Ru_Convoy_4
|
||||||
-- * AIRBASE.Syria.Hatay
|
-- * AIRBASE.Syria.Hatay
|
||||||
|
-- * AIRBASE.Syria.Nicosia
|
||||||
|
-- * AIRBASE.Syria.Pinarbashi
|
||||||
|
-- * AIRBASE.Syria.Paphos
|
||||||
|
-- * AIRBASE.Syria.Kingsfield
|
||||||
|
-- * AIRBASE.Syria.Thalah
|
||||||
-- * AIRBASE.Syria.Haifa
|
-- * AIRBASE.Syria.Haifa
|
||||||
-- * AIRBASE.Syria.Khalkhalah
|
-- * AIRBASE.Syria.Khalkhalah
|
||||||
-- * AIRBASE.Syria.Megiddo
|
-- * AIRBASE.Syria.Megiddo
|
||||||
|
-- * AIRBASE.Syria.Lakatamia
|
||||||
-- * AIRBASE.Syria.Rayak
|
-- * AIRBASE.Syria.Rayak
|
||||||
|
-- * AIRBASE.Syria.Larnaca
|
||||||
-- * AIRBASE.Syria.Mezzeh
|
-- * AIRBASE.Syria.Mezzeh
|
||||||
-- * AIRBASE.Syria.King_Hussein_Air_College
|
-- * AIRBASE.Syria.Gecitkale
|
||||||
-- * AIRBASE.Syria.Jirah
|
-- * AIRBASE.Syria.Akrotiri
|
||||||
|
-- * AIRBASE.Syria.Naqoura
|
||||||
|
-- * AIRBASE.Syria.Gaziantep
|
||||||
|
-- * AIRBASE.Syria.CVN_71
|
||||||
|
-- * AIRBASE.Syria.Sayqal
|
||||||
|
-- * AIRBASE.Syria.Tiyas
|
||||||
|
-- * AIRBASE.Syria.Shayrat
|
||||||
-- * AIRBASE.Syria.Taftanaz
|
-- * AIRBASE.Syria.Taftanaz
|
||||||
|
-- * AIRBASE.Syria.H4
|
||||||
|
-- * AIRBASE.Syria.King_Hussein_Air_College
|
||||||
-- * AIRBASE.Syria.Rene_Mouawad
|
-- * AIRBASE.Syria.Rene_Mouawad
|
||||||
|
-- * AIRBASE.Syria.Jirah
|
||||||
-- * AIRBASE.Syria.Ramat_David
|
-- * AIRBASE.Syria.Ramat_David
|
||||||
|
-- * AIRBASE.Syria.Qabr_as_Sitt
|
||||||
-- * AIRBASE.Syria.Minakh
|
-- * AIRBASE.Syria.Minakh
|
||||||
-- * AIRBASE.Syria.Adana_Sakirpasa
|
-- * AIRBASE.Syria.Adana_Sakirpasa
|
||||||
-- * AIRBASE.Syria.Marj_as_Sultan_South
|
|
||||||
-- * AIRBASE.Syria.Hama
|
|
||||||
-- * AIRBASE.Syria.Al_Qusayr
|
|
||||||
-- * AIRBASE.Syria.Palmyra
|
-- * AIRBASE.Syria.Palmyra
|
||||||
|
-- * AIRBASE.Syria.Hama
|
||||||
|
-- * AIRBASE.Syria.Ercan
|
||||||
|
-- * AIRBASE.Syria.Marj_as_Sultan_South
|
||||||
-- * AIRBASE.Syria.Tabqa
|
-- * AIRBASE.Syria.Tabqa
|
||||||
-- * AIRBASE.Syria.Beirut_Rafic_Hariri
|
-- * AIRBASE.Syria.Beirut_Rafic_Hariri
|
||||||
-- * AIRBASE.Syria.An_Nasiriyah
|
-- * AIRBASE.Syria.An_Nasiriyah
|
||||||
-- * AIRBASE.Syria.Abu_al_Duhur
|
-- * AIRBASE.Syria.Abu_al_Duhur
|
||||||
--
|
--
|
||||||
-- @field Syria
|
--@field Syria
|
||||||
AIRBASE.Syria={
|
AIRBASE.Syria={
|
||||||
["Kuweires"]="Kuweires",
|
["Kuweires"]="Kuweires",
|
||||||
["Marj_Ruhayyil"]="Marj Ruhayyil",
|
["Marj_Ruhayyil"]="Marj Ruhayyil",
|
||||||
@@ -370,27 +396,46 @@ AIRBASE.Syria={
|
|||||||
["Incirlik"]="Incirlik",
|
["Incirlik"]="Incirlik",
|
||||||
["Damascus"]="Damascus",
|
["Damascus"]="Damascus",
|
||||||
["Bassel_Al_Assad"]="Bassel Al-Assad",
|
["Bassel_Al_Assad"]="Bassel Al-Assad",
|
||||||
|
["Rosh_Pina"]="Rosh Pina",
|
||||||
["Aleppo"]="Aleppo",
|
["Aleppo"]="Aleppo",
|
||||||
["Qabr_as_Sitt"]="Qabr as Sitt",
|
["Al_Qusayr"]="Al Qusayr",
|
||||||
["Wujah_Al_Hajar"]="Wujah Al Hajar",
|
["Wujah_Al_Hajar"]="Wujah Al Hajar",
|
||||||
["Al_Dumayr"]="Al-Dumayr",
|
["Al_Dumayr"]="Al-Dumayr",
|
||||||
|
["Gazipasa"]="Gazipasa",
|
||||||
|
["Ru_Convoy_4"]="Ru Convoy-4",
|
||||||
["Hatay"]="Hatay",
|
["Hatay"]="Hatay",
|
||||||
|
["Nicosia"]="Nicosia",
|
||||||
|
["Pinarbashi"]="Pinarbashi",
|
||||||
|
["Paphos"]="Paphos",
|
||||||
|
["Kingsfield"]="Kingsfield",
|
||||||
|
["Thalah"]="Tha'lah",
|
||||||
["Haifa"]="Haifa",
|
["Haifa"]="Haifa",
|
||||||
["Khalkhalah"]="Khalkhalah",
|
["Khalkhalah"]="Khalkhalah",
|
||||||
["Megiddo"]="Megiddo",
|
["Megiddo"]="Megiddo",
|
||||||
|
["Lakatamia"]="Lakatamia",
|
||||||
["Rayak"]="Rayak",
|
["Rayak"]="Rayak",
|
||||||
|
["Larnaca"]="Larnaca",
|
||||||
["Mezzeh"]="Mezzeh",
|
["Mezzeh"]="Mezzeh",
|
||||||
["King_Hussein_Air_College"]="King Hussein Air College",
|
["Gecitkale"]="Gecitkale",
|
||||||
["Jirah"]="Jirah",
|
["Akrotiri"]="Akrotiri",
|
||||||
|
["Naqoura"]="Naqoura",
|
||||||
|
["Gaziantep"]="Gaziantep",
|
||||||
|
["Sayqal"]="Sayqal",
|
||||||
|
["Tiyas"]="Tiyas",
|
||||||
|
["Shayrat"]="Shayrat",
|
||||||
["Taftanaz"]="Taftanaz",
|
["Taftanaz"]="Taftanaz",
|
||||||
|
["H4"]="H4",
|
||||||
|
["King_Hussein_Air_College"]="King Hussein Air College",
|
||||||
["Rene_Mouawad"]="Rene Mouawad",
|
["Rene_Mouawad"]="Rene Mouawad",
|
||||||
|
["Jirah"]="Jirah",
|
||||||
["Ramat_David"]="Ramat David",
|
["Ramat_David"]="Ramat David",
|
||||||
|
["Qabr_as_Sitt"]="Qabr as Sitt",
|
||||||
["Minakh"]="Minakh",
|
["Minakh"]="Minakh",
|
||||||
["Adana_Sakirpasa"]="Adana Sakirpasa",
|
["Adana_Sakirpasa"]="Adana Sakirpasa",
|
||||||
["Marj_as_Sultan_South"]="Marj as Sultan South",
|
|
||||||
["Hama"]="Hama",
|
|
||||||
["Al_Qusayr"]="Al Qusayr",
|
|
||||||
["Palmyra"]="Palmyra",
|
["Palmyra"]="Palmyra",
|
||||||
|
["Hama"]="Hama",
|
||||||
|
["Ercan"]="Ercan",
|
||||||
|
["Marj_as_Sultan_South"]="Marj as Sultan South",
|
||||||
["Tabqa"]="Tabqa",
|
["Tabqa"]="Tabqa",
|
||||||
["Beirut_Rafic_Hariri"]="Beirut-Rafic Hariri",
|
["Beirut_Rafic_Hariri"]="Beirut-Rafic Hariri",
|
||||||
["An_Nasiriyah"]="An Nasiriyah",
|
["An_Nasiriyah"]="An Nasiriyah",
|
||||||
@@ -398,6 +443,27 @@ AIRBASE.Syria={
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- Airbases of the Mariana Islands map:
|
||||||
|
--
|
||||||
|
-- * AIRBASE.MarianaIslands.Rota_Intl
|
||||||
|
-- * AIRBASE.MarianaIslands.Andersen_AFB
|
||||||
|
-- * AIRBASE.MarianaIslands.Antonio_B_Won_Pat_Intl
|
||||||
|
-- * AIRBASE.MarianaIslands.Saipan_Intl
|
||||||
|
-- * AIRBASE.MarianaIslands.Tinian_Intl
|
||||||
|
-- * AIRBASE.MarianaIslands.Olf_Orote
|
||||||
|
--
|
||||||
|
--@field MarianaIslands
|
||||||
|
AIRBASE.MarianaIslands={
|
||||||
|
["Rota_Intl"]="Rota Intl",
|
||||||
|
["Andersen_AFB"]="Andersen AFB",
|
||||||
|
["Antonio_B_Won_Pat_Intl"]="Antonio B. Won Pat Intl",
|
||||||
|
["Saipan_Intl"]="Saipan Intl",
|
||||||
|
["Tinian_Intl"]="Tinian Intl",
|
||||||
|
["Olf_Orote"]="Olf Orote",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
--- AIRBASE.ParkingSpot ".Coordinate, ".TerminalID", ".TerminalType", ".TOAC", ".Free", ".TerminalID0", ".DistToRwy".
|
--- AIRBASE.ParkingSpot ".Coordinate, ".TerminalID", ".TerminalType", ".TOAC", ".Free", ".TerminalID0", ".DistToRwy".
|
||||||
-- @type AIRBASE.ParkingSpot
|
-- @type AIRBASE.ParkingSpot
|
||||||
-- @field Core.Point#COORDINATE Coordinate Coordinate of the parking spot.
|
-- @field Core.Point#COORDINATE Coordinate Coordinate of the parking spot.
|
||||||
@@ -481,6 +547,13 @@ function AIRBASE:Register(AirbaseName)
|
|||||||
self.isHelipad=true
|
self.isHelipad=true
|
||||||
elseif self.category==Airbase.Category.SHIP then
|
elseif self.category==Airbase.Category.SHIP then
|
||||||
self.isShip=true
|
self.isShip=true
|
||||||
|
-- DCS bug: Oil rigs and gas platforms have category=2 (ship). Also they cannot be retrieved by coalition.getStaticObjects()
|
||||||
|
if self.descriptors.typeName=="Oil rig" or self.descriptors.typeName=="Ga" then
|
||||||
|
self.isHelipad=true
|
||||||
|
self.isShip=false
|
||||||
|
self.category=Airbase.Category.HELIPAD
|
||||||
|
_DATABASE:AddStatic(AirbaseName)
|
||||||
|
end
|
||||||
else
|
else
|
||||||
self:E("ERROR: Unknown airbase category!")
|
self:E("ERROR: Unknown airbase category!")
|
||||||
end
|
end
|
||||||
@@ -493,8 +566,14 @@ function AIRBASE:Register(AirbaseName)
|
|||||||
self:GetCoordinate()
|
self:GetCoordinate()
|
||||||
|
|
||||||
if vec2 then
|
if vec2 then
|
||||||
-- TODO: For ships we need a moving zone.
|
if self.isShip then
|
||||||
self.AirbaseZone=ZONE_RADIUS:New( AirbaseName, vec2, 2500 )
|
local unit=UNIT:FindByName(AirbaseName)
|
||||||
|
if unit then
|
||||||
|
self.AirbaseZone=ZONE_UNIT:New(AirbaseName, unit, 2500)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self.AirbaseZone=ZONE_RADIUS:New(AirbaseName, vec2, 2500)
|
||||||
|
end
|
||||||
else
|
else
|
||||||
self:E(string.format("ERROR: Cound not get position Vec2 of airbase %s", AirbaseName))
|
self:E(string.format("ERROR: Cound not get position Vec2 of airbase %s", AirbaseName))
|
||||||
end
|
end
|
||||||
@@ -590,7 +669,7 @@ end
|
|||||||
|
|
||||||
--- Get all airbase names of the current map. This includes ships and FARPS.
|
--- Get all airbase names of the current map. This includes ships and FARPS.
|
||||||
-- @param DCS#Coalition coalition (Optional) Return only airbases belonging to the specified coalition. By default, all airbases of the map are returned.
|
-- @param DCS#Coalition coalition (Optional) Return only airbases belonging to the specified coalition. By default, all airbases of the map are returned.
|
||||||
-- @param #number category (Optional) Return only airbases of a certain category, e.g. Airbase.Category.FARP
|
-- @param #number category (Optional) Return only airbases of a certain category, e.g. `Airbase.Category.HELIPAD`.
|
||||||
-- @return #table Table containing all airbase names of the current map.
|
-- @return #table Table containing all airbase names of the current map.
|
||||||
function AIRBASE.GetAllAirbaseNames(coalition, category)
|
function AIRBASE.GetAllAirbaseNames(coalition, category)
|
||||||
|
|
||||||
@@ -645,6 +724,54 @@ function AIRBASE:GetID(unique)
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Set parking spot whitelist. Only these spots will be considered for spawning.
|
||||||
|
-- Black listed spots overrule white listed spots.
|
||||||
|
-- **NOTE** that terminal IDs are not necessarily the same as those displayed in the mission editor!
|
||||||
|
-- @param #AIRBASE self
|
||||||
|
-- @param #table TerminalIdBlacklist Table of white listed terminal IDs.
|
||||||
|
-- @return #AIRBASE self
|
||||||
|
-- @usage AIRBASE:FindByName("Batumi"):SetParkingSpotWhitelist({2, 3, 4}) --Only allow terminal IDs 2, 3, 4
|
||||||
|
function AIRBASE:SetParkingSpotWhitelist(TerminalIdWhitelist)
|
||||||
|
|
||||||
|
if TerminalIdWhitelist==nil then
|
||||||
|
self.parkingWhitelist={}
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Ensure we got a table.
|
||||||
|
if type(TerminalIdWhitelist)~="table" then
|
||||||
|
TerminalIdWhitelist={TerminalIdWhitelist}
|
||||||
|
end
|
||||||
|
|
||||||
|
self.parkingWhitelist=TerminalIdWhitelist
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set parking spot blacklist. These parking spots will *not* be used for spawning.
|
||||||
|
-- Black listed spots overrule white listed spots.
|
||||||
|
-- **NOTE** that terminal IDs are not necessarily the same as those displayed in the mission editor!
|
||||||
|
-- @param #AIRBASE self
|
||||||
|
-- @param #table TerminalIdBlacklist Table of black listed terminal IDs.
|
||||||
|
-- @return #AIRBASE self
|
||||||
|
-- @usage AIRBASE:FindByName("Batumi"):SetParkingSpotBlacklist({2, 3, 4}) --Forbit terminal IDs 2, 3, 4
|
||||||
|
function AIRBASE:SetParkingSpotBlacklist(TerminalIdBlacklist)
|
||||||
|
|
||||||
|
if TerminalIdBlacklist==nil then
|
||||||
|
self.parkingBlacklist={}
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Ensure we got a table.
|
||||||
|
if type(TerminalIdBlacklist)~="table" then
|
||||||
|
TerminalIdBlacklist={TerminalIdBlacklist}
|
||||||
|
end
|
||||||
|
|
||||||
|
self.parkingBlacklist=TerminalIdBlacklist
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Get category of airbase.
|
--- Get category of airbase.
|
||||||
-- @param #AIRBASE self
|
-- @param #AIRBASE self
|
||||||
@@ -829,25 +956,27 @@ function AIRBASE:_InitParkingSpots()
|
|||||||
-- Put coordinates of parking spots into table.
|
-- Put coordinates of parking spots into table.
|
||||||
for _,spot in pairs(parkingdata) do
|
for _,spot in pairs(parkingdata) do
|
||||||
|
|
||||||
-- New parking spot.
|
-- New parking spot.
|
||||||
local park={} --#AIRBASE.ParkingSpot
|
local park={} --#AIRBASE.ParkingSpot
|
||||||
park.Vec3=spot.vTerminalPos
|
park.Vec3=spot.vTerminalPos
|
||||||
park.Coordinate=COORDINATE:NewFromVec3(spot.vTerminalPos)
|
park.Coordinate=COORDINATE:NewFromVec3(spot.vTerminalPos)
|
||||||
park.DistToRwy=spot.fDistToRW
|
park.DistToRwy=spot.fDistToRW
|
||||||
park.Free=nil
|
park.Free=nil
|
||||||
park.TerminalID=spot.Term_Index
|
park.TerminalID=spot.Term_Index
|
||||||
park.TerminalID0=spot.Term_Index_0
|
park.TerminalID0=spot.Term_Index_0
|
||||||
park.TerminalType=spot.Term_Type
|
park.TerminalType=spot.Term_Type
|
||||||
park.TOAC=spot.TO_AC
|
park.TOAC=spot.TO_AC
|
||||||
|
|
||||||
for _,terminalType in pairs(AIRBASE.TerminalType) do
|
self.NparkingTotal=self.NparkingTotal+1
|
||||||
if self._CheckTerminalType(terminalType, park.TerminalType) then
|
|
||||||
self.NparkingTerminal[terminalType]=self.NparkingTerminal[terminalType]+1
|
for _,terminalType in pairs(AIRBASE.TerminalType) do
|
||||||
end
|
if self._CheckTerminalType(terminalType, park.TerminalType) then
|
||||||
|
self.NparkingTerminal[terminalType]=self.NparkingTerminal[terminalType]+1
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
self.parkingByID[park.TerminalID]=park
|
self.parkingByID[park.TerminalID]=park
|
||||||
table.insert(self.parking, park)
|
table.insert(self.parking, park)
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
@@ -891,10 +1020,19 @@ function AIRBASE:GetParkingSpotsTable(termtype)
|
|||||||
|
|
||||||
local spot=self:_GetParkingSpotByID(_spot.Term_Index)
|
local spot=self:_GetParkingSpotByID(_spot.Term_Index)
|
||||||
|
|
||||||
spot.Free=_isfree(_spot) -- updated
|
if spot then
|
||||||
spot.TOAC=_spot.TO_AC -- updated
|
|
||||||
|
spot.Free=_isfree(_spot) -- updated
|
||||||
|
spot.TOAC=_spot.TO_AC -- updated
|
||||||
|
|
||||||
|
table.insert(spots, spot)
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
self:E(string.format("ERROR: Parking spot %s is nil!", tostring(_spot.Term_Index)))
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
table.insert(spots, spot)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -969,7 +1107,7 @@ function AIRBASE:MarkParkingSpots(termtype, mark)
|
|||||||
|
|
||||||
-- Get airbase name.
|
-- Get airbase name.
|
||||||
local airbasename=self:GetName()
|
local airbasename=self:GetName()
|
||||||
self:E(string.format("Parking spots at %s for termial type %s:", airbasename, tostring(termtype)))
|
self:E(string.format("Parking spots at %s for terminal type %s:", airbasename, tostring(termtype)))
|
||||||
|
|
||||||
for _,_spot in pairs(parkingdata) do
|
for _,_spot in pairs(parkingdata) do
|
||||||
|
|
||||||
@@ -1046,14 +1184,25 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius,
|
|||||||
parkingdata=parkingdata or self:GetParkingSpotsTable(terminaltype)
|
parkingdata=parkingdata or self:GetParkingSpotsTable(terminaltype)
|
||||||
|
|
||||||
-- Get the aircraft size, i.e. it's longest side of x,z.
|
-- Get the aircraft size, i.e. it's longest side of x,z.
|
||||||
local aircraft=group:GetUnit(1)
|
local aircraft = nil -- fix local problem below
|
||||||
local _aircraftsize, ax,ay,az=aircraft:GetObjectSize()
|
local _aircraftsize, ax,ay,az
|
||||||
|
if group and group.ClassName == "GROUP" then
|
||||||
|
aircraft=group:GetUnit(1)
|
||||||
|
_aircraftsize, ax,ay,az=aircraft:GetObjectSize()
|
||||||
|
else
|
||||||
|
-- SU27 dimensions
|
||||||
|
_aircraftsize = 23
|
||||||
|
ax = 23 -- length
|
||||||
|
ay = 7 -- height
|
||||||
|
az = 17 -- width
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
-- Number of spots we are looking for. Note that, e.g. grouping can require a number different from the group size!
|
-- Number of spots we are looking for. Note that, e.g. grouping can require a number different from the group size!
|
||||||
local _nspots=nspots or group:GetSize()
|
local _nspots=nspots or group:GetSize()
|
||||||
|
|
||||||
-- Debug info.
|
-- Debug info.
|
||||||
self:E(string.format("%s: Looking for %d parking spot(s) for aircraft of size %.1f m (x=%.1f,y=%.1f,z=%.1f) at termial type %s.", airport, _nspots, _aircraftsize, ax, ay, az, tostring(terminaltype)))
|
self:E(string.format("%s: Looking for %d parking spot(s) for aircraft of size %.1f m (x=%.1f,y=%.1f,z=%.1f) at terminal type %s.", airport, _nspots, _aircraftsize, ax, ay, az, tostring(terminaltype)))
|
||||||
|
|
||||||
-- Table of valid spots.
|
-- Table of valid spots.
|
||||||
local validspots={}
|
local validspots={}
|
||||||
@@ -1075,9 +1224,8 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius,
|
|||||||
local _spot=parkingspot.Coordinate -- Core.Point#COORDINATE
|
local _spot=parkingspot.Coordinate -- Core.Point#COORDINATE
|
||||||
local _termid=parkingspot.TerminalID
|
local _termid=parkingspot.TerminalID
|
||||||
|
|
||||||
self:T2({_termid=_termid})
|
-- Check terminal type and black/white listed parking spots.
|
||||||
|
if AIRBASE._CheckTerminalType(parkingspot.TerminalType, terminaltype) and self:_CheckParkingLists(_termid) then
|
||||||
if AIRBASE._CheckTerminalType(parkingspot.TerminalType, terminaltype) then
|
|
||||||
|
|
||||||
-- Very safe uses the DCS getParking() info to check if a spot is free. Unfortunately, the function returns free=false until the aircraft has actually taken-off.
|
-- Very safe uses the DCS getParking() info to check if a spot is free. Unfortunately, the function returns free=false until the aircraft has actually taken-off.
|
||||||
if verysafe and (parkingspot.Free==false or parkingspot.TOAC==true) then
|
if verysafe and (parkingspot.Free==false or parkingspot.TOAC==true) then
|
||||||
@@ -1177,6 +1325,40 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius,
|
|||||||
|
|
||||||
-- Retrun spots we found, even if there were not enough.
|
-- Retrun spots we found, even if there were not enough.
|
||||||
return validspots
|
return validspots
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check black and white lists.
|
||||||
|
-- @param #AIRBASE self
|
||||||
|
-- @param #number TerminalID Terminal ID to check.
|
||||||
|
-- @return #boolean `true` if this is a valid spot.
|
||||||
|
function AIRBASE:_CheckParkingLists(TerminalID)
|
||||||
|
|
||||||
|
-- First check the black list. If we find a match, this spot is forbidden!
|
||||||
|
if self.parkingBlacklist and #self.parkingBlacklist>0 then
|
||||||
|
for _,terminalID in pairs(self.parkingBlacklist or {}) do
|
||||||
|
if terminalID==TerminalID then
|
||||||
|
-- This is a invalid spot.
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- Check if a whitelist was defined.
|
||||||
|
if self.parkingWhitelist and #self.parkingWhitelist>0 then
|
||||||
|
for _,terminalID in pairs(self.parkingWhitelist or {}) do
|
||||||
|
if terminalID==TerminalID then
|
||||||
|
-- This is a valid spot.
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- No match ==> invalid spot
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Neither black nor white lists were defined or spot is not in black list.
|
||||||
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Helper function to check for the correct terminal type including "artificial" ones.
|
--- Helper function to check for the correct terminal type including "artificial" ones.
|
||||||
@@ -1272,8 +1454,8 @@ function AIRBASE:GetRunwayData(magvar, mark)
|
|||||||
name==AIRBASE.PersianGulf.Abu_Dhabi_International_Airport or
|
name==AIRBASE.PersianGulf.Abu_Dhabi_International_Airport or
|
||||||
name==AIRBASE.PersianGulf.Dubai_Intl or
|
name==AIRBASE.PersianGulf.Dubai_Intl or
|
||||||
name==AIRBASE.PersianGulf.Shiraz_International_Airport or
|
name==AIRBASE.PersianGulf.Shiraz_International_Airport or
|
||||||
name==AIRBASE.PersianGulf.Kish_International_Airport
|
name==AIRBASE.PersianGulf.Kish_International_Airport or
|
||||||
then
|
name==AIRBASE.MarianaIslands.Andersen_AFB then
|
||||||
|
|
||||||
-- 1-->4, 2-->3, 3-->2, 4-->1
|
-- 1-->4, 2-->3, 3-->2, 4-->1
|
||||||
exception=1
|
exception=1
|
||||||
@@ -1290,13 +1472,14 @@ function AIRBASE:GetRunwayData(magvar, mark)
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Function returning the index of the runway coordinate belonding to the given index i.
|
||||||
local function f(i)
|
local function f(i)
|
||||||
|
|
||||||
local j
|
local j
|
||||||
|
|
||||||
if exception==1 then
|
if exception==1 then
|
||||||
|
|
||||||
j=N-(i+1) -- 1-->4, 2-->3
|
j=N-(i-1) -- 1-->4, 2-->3
|
||||||
|
|
||||||
elseif exception==2 then
|
elseif exception==2 then
|
||||||
|
|
||||||
@@ -1358,6 +1541,9 @@ function AIRBASE:GetRunwayData(magvar, mark)
|
|||||||
-- Get the other spawn point coordinate.
|
-- Get the other spawn point coordinate.
|
||||||
local j=f(i)
|
local j=f(i)
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
--env.info(string.format("Runway i=%s j=%s (N=%d #runwaycoord=%d)", tostring(i), tostring(j), N, #runwaycoords))
|
||||||
|
|
||||||
-- Coordinates of the two runway points.
|
-- Coordinates of the two runway points.
|
||||||
local c1=runwaycoords[i] --Core.Point#COORDINATE
|
local c1=runwaycoords[i] --Core.Point#COORDINATE
|
||||||
local c2=runwaycoords[j] --Core.Point#COORDINATE
|
local c2=runwaycoords[j] --Core.Point#COORDINATE
|
||||||
|
|||||||
@@ -3,8 +3,7 @@
|
|||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ### Author: **FlightControl**
|
-- ### Author: **FlightControl**
|
||||||
--
|
-- ### Contributions: **funkyfranky**
|
||||||
-- ### Contributions:
|
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
@@ -52,13 +51,6 @@
|
|||||||
--
|
--
|
||||||
-- @field #CLIENT
|
-- @field #CLIENT
|
||||||
CLIENT = {
|
CLIENT = {
|
||||||
ONBOARDSIDE = {
|
|
||||||
NONE = 0,
|
|
||||||
LEFT = 1,
|
|
||||||
RIGHT = 2,
|
|
||||||
BACK = 3,
|
|
||||||
FRONT = 4
|
|
||||||
},
|
|
||||||
ClassName = "CLIENT",
|
ClassName = "CLIENT",
|
||||||
ClientName = nil,
|
ClientName = nil,
|
||||||
ClientAlive = false,
|
ClientAlive = false,
|
||||||
@@ -66,27 +58,20 @@ CLIENT = {
|
|||||||
ClientBriefingShown = false,
|
ClientBriefingShown = false,
|
||||||
_Menus = {},
|
_Menus = {},
|
||||||
_Tasks = {},
|
_Tasks = {},
|
||||||
Messages = {
|
Messages = {},
|
||||||
}
|
Players = {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
--- Finds a CLIENT from the _DATABASE using the relevant DCS Unit.
|
--- Finds a CLIENT from the _DATABASE using the relevant DCS Unit.
|
||||||
-- @param #CLIENT self
|
-- @param #CLIENT self
|
||||||
-- @param #string ClientName Name of the DCS **Unit** as defined within the Mission Editor.
|
-- @param DCS#Unit DCSUnit The DCS unit of the client.
|
||||||
-- @param #string ClientBriefing Text that describes the briefing of the mission when a Player logs into the Client.
|
-- @param #boolean Error Throw an error message.
|
||||||
-- @return #CLIENT
|
-- @return #CLIENT The CLIENT found in the _DATABASE.
|
||||||
-- @usage
|
function CLIENT:Find(DCSUnit, Error)
|
||||||
-- -- Create new Clients.
|
|
||||||
-- local Mission = MISSIONSCHEDULER.AddMission( 'Russia Transport Troops SA-6', 'Operational', 'Transport troops from the control center to one of the SA-6 SAM sites to activate their operation.', 'Russia' )
|
|
||||||
-- Mission:AddGoal( DeploySA6TroopsGoal )
|
|
||||||
--
|
|
||||||
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*HOT-Deploy Troops 1' ):Transport() )
|
|
||||||
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*RAMP-Deploy Troops 3' ):Transport() )
|
|
||||||
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*HOT-Deploy Troops 2' ):Transport() )
|
|
||||||
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*RAMP-Deploy Troops 4' ):Transport() )
|
|
||||||
function CLIENT:Find( DCSUnit, Error )
|
|
||||||
local ClientName = DCSUnit:getName()
|
local ClientName = DCSUnit:getName()
|
||||||
|
|
||||||
local ClientFound = _DATABASE:FindClient( ClientName )
|
local ClientFound = _DATABASE:FindClient( ClientName )
|
||||||
|
|
||||||
if ClientFound then
|
if ClientFound then
|
||||||
@@ -117,11 +102,15 @@ end
|
|||||||
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*HOT-Deploy Troops 2' ):Transport() )
|
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*HOT-Deploy Troops 2' ):Transport() )
|
||||||
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*RAMP-Deploy Troops 4' ):Transport() )
|
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*RAMP-Deploy Troops 4' ):Transport() )
|
||||||
function CLIENT:FindByName( ClientName, ClientBriefing, Error )
|
function CLIENT:FindByName( ClientName, ClientBriefing, Error )
|
||||||
|
|
||||||
|
-- Client
|
||||||
local ClientFound = _DATABASE:FindClient( ClientName )
|
local ClientFound = _DATABASE:FindClient( ClientName )
|
||||||
|
|
||||||
if ClientFound then
|
if ClientFound then
|
||||||
ClientFound:F( { ClientName, ClientBriefing } )
|
ClientFound:F( { ClientName, ClientBriefing } )
|
||||||
ClientFound:AddBriefing( ClientBriefing )
|
|
||||||
|
ClientFound:AddBriefing(ClientBriefing)
|
||||||
|
|
||||||
ClientFound.MessageSwitch = true
|
ClientFound.MessageSwitch = true
|
||||||
|
|
||||||
return ClientFound
|
return ClientFound
|
||||||
@@ -132,51 +121,105 @@ function CLIENT:FindByName( ClientName, ClientBriefing, Error )
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function CLIENT:Register( ClientName )
|
--- Transport defines that the Client is a Transport. Transports show cargo.
|
||||||
|
-- @param #CLIENT self
|
||||||
|
-- @param #string ClientName Name of the client unit.
|
||||||
|
-- @return #CLIENT self
|
||||||
|
function CLIENT:Register(ClientName)
|
||||||
|
|
||||||
local self = BASE:Inherit( self, UNIT:Register( ClientName ) ) -- #CLIENT
|
-- Inherit unit.
|
||||||
|
local self = BASE:Inherit( self, UNIT:Register(ClientName )) -- #CLIENT
|
||||||
|
|
||||||
self:F( ClientName )
|
-- Set client name.
|
||||||
self.ClientName = ClientName
|
self.ClientName = ClientName
|
||||||
|
|
||||||
|
-- Message switch.
|
||||||
self.MessageSwitch = true
|
self.MessageSwitch = true
|
||||||
|
|
||||||
|
-- Alive2.
|
||||||
self.ClientAlive2 = false
|
self.ClientAlive2 = false
|
||||||
|
|
||||||
--self.AliveCheckScheduler = routines.scheduleFunction( self._AliveCheckScheduler, { self }, timer.getTime() + 1, 5 )
|
|
||||||
self.AliveCheckScheduler = SCHEDULER:New( self, self._AliveCheckScheduler, { "Client Alive " .. ClientName }, 1, 5, 0.5 )
|
|
||||||
self.AliveCheckScheduler:NoTrace()
|
|
||||||
|
|
||||||
self:F( self )
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Transport defines that the Client is a Transport. Transports show cargo.
|
--- Transport defines that the Client is a Transport. Transports show cargo.
|
||||||
-- @param #CLIENT self
|
-- @param #CLIENT self
|
||||||
-- @return #CLIENT
|
-- @return #CLIENT self
|
||||||
function CLIENT:Transport()
|
function CLIENT:Transport()
|
||||||
self:F()
|
|
||||||
|
|
||||||
self.ClientTransport = true
|
self.ClientTransport = true
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- AddBriefing adds a briefing to a CLIENT when a player joins a mission.
|
--- Adds a briefing to a CLIENT when a player joins a mission.
|
||||||
-- @param #CLIENT self
|
-- @param #CLIENT self
|
||||||
-- @param #string ClientBriefing is the text defining the Mission briefing.
|
-- @param #string ClientBriefing is the text defining the Mission briefing.
|
||||||
-- @return #CLIENT self
|
-- @return #CLIENT self
|
||||||
function CLIENT:AddBriefing( ClientBriefing )
|
function CLIENT:AddBriefing( ClientBriefing )
|
||||||
self:F( ClientBriefing )
|
|
||||||
self.ClientBriefing = ClientBriefing
|
self.ClientBriefing = ClientBriefing
|
||||||
self.ClientBriefingShown = false
|
self.ClientBriefingShown = false
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Add player name.
|
||||||
|
-- @param #CLIENT self
|
||||||
|
-- @param #string PlayerName Name of the player.
|
||||||
|
-- @return #CLIENT self
|
||||||
|
function CLIENT:AddPlayer(PlayerName)
|
||||||
|
|
||||||
|
table.insert(self.Players, PlayerName)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get player name(s).
|
||||||
|
-- @param #CLIENT self
|
||||||
|
-- @return #table List of player names or an empty table `{}`.
|
||||||
|
function CLIENT:GetPlayers()
|
||||||
|
return self.Players
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get name of player.
|
||||||
|
-- @param #CLIENT self
|
||||||
|
-- @return #string Player name or `nil`.
|
||||||
|
function CLIENT:GetPlayer()
|
||||||
|
if #self.Players>0 then
|
||||||
|
return self.Players[1]
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Remove player.
|
||||||
|
-- @param #CLIENT self
|
||||||
|
-- @param #string PlayerName Name of the player.
|
||||||
|
-- @return #CLIENT self
|
||||||
|
function CLIENT:RemovePlayer(PlayerName)
|
||||||
|
|
||||||
|
for i,playername in pairs(self.Players) do
|
||||||
|
if PlayerName==playername then
|
||||||
|
table.remove(self.Players, i)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Remove all players.
|
||||||
|
-- @param #CLIENT self
|
||||||
|
-- @return #CLIENT self
|
||||||
|
function CLIENT:RemovePlayers()
|
||||||
|
self.Players={}
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Show the briefing of a CLIENT.
|
--- Show the briefing of a CLIENT.
|
||||||
-- @param #CLIENT self
|
-- @param #CLIENT self
|
||||||
-- @return #CLIENT self
|
-- @return #CLIENT self
|
||||||
function CLIENT:ShowBriefing()
|
function CLIENT:ShowBriefing()
|
||||||
self:F( { self.ClientName, self.ClientBriefingShown } )
|
|
||||||
|
|
||||||
if not self.ClientBriefingShown then
|
if not self.ClientBriefingShown then
|
||||||
self.ClientBriefingShown = true
|
self.ClientBriefingShown = true
|
||||||
@@ -204,8 +247,6 @@ function CLIENT:ShowMissionBriefing( MissionBriefing )
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- Resets a CLIENT.
|
--- Resets a CLIENT.
|
||||||
-- @param #CLIENT self
|
-- @param #CLIENT self
|
||||||
-- @param #string ClientName Name of the Group as defined within the Mission Editor. The Group must have a Unit with the type Client.
|
-- @param #string ClientName Name of the Group as defined within the Mission Editor. The Group must have a Unit with the type Client.
|
||||||
@@ -241,6 +282,7 @@ end
|
|||||||
--- Checks for a client alive event and calls a function on a continuous basis.
|
--- Checks for a client alive event and calls a function on a continuous basis.
|
||||||
-- @param #CLIENT self
|
-- @param #CLIENT self
|
||||||
-- @param #function CallBackFunction Create a function that will be called when a player joins the slot.
|
-- @param #function CallBackFunction Create a function that will be called when a player joins the slot.
|
||||||
|
-- @param ... (Optional) Arguments for callback function as comma separated list.
|
||||||
-- @return #CLIENT
|
-- @return #CLIENT
|
||||||
function CLIENT:Alive( CallBackFunction, ... )
|
function CLIENT:Alive( CallBackFunction, ... )
|
||||||
self:F()
|
self:F()
|
||||||
@@ -248,6 +290,9 @@ function CLIENT:Alive( CallBackFunction, ... )
|
|||||||
self.ClientCallBack = CallBackFunction
|
self.ClientCallBack = CallBackFunction
|
||||||
self.ClientParameters = arg
|
self.ClientParameters = arg
|
||||||
|
|
||||||
|
self.AliveCheckScheduler = SCHEDULER:New( self, self._AliveCheckScheduler, { "Client Alive " .. self.ClientName }, 0.1, 5, 0.5 )
|
||||||
|
self.AliveCheckScheduler:NoTrace()
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -256,18 +301,28 @@ function CLIENT:_AliveCheckScheduler( SchedulerName )
|
|||||||
self:F3( { SchedulerName, self.ClientName, self.ClientAlive2, self.ClientBriefingShown, self.ClientCallBack } )
|
self:F3( { SchedulerName, self.ClientName, self.ClientAlive2, self.ClientBriefingShown, self.ClientCallBack } )
|
||||||
|
|
||||||
if self:IsAlive() then
|
if self:IsAlive() then
|
||||||
|
|
||||||
if self.ClientAlive2 == false then
|
if self.ClientAlive2 == false then
|
||||||
|
|
||||||
|
-- Show briefing.
|
||||||
self:ShowBriefing()
|
self:ShowBriefing()
|
||||||
|
|
||||||
|
-- Callback function.
|
||||||
if self.ClientCallBack then
|
if self.ClientCallBack then
|
||||||
self:T("Calling Callback function")
|
self:T("Calling Callback function")
|
||||||
self.ClientCallBack( self, unpack( self.ClientParameters ) )
|
self.ClientCallBack( self, unpack( self.ClientParameters ) )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Alive.
|
||||||
self.ClientAlive2 = true
|
self.ClientAlive2 = true
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
if self.ClientAlive2 == true then
|
if self.ClientAlive2 == true then
|
||||||
self.ClientAlive2 = false
|
self.ClientAlive2 = false
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return true
|
return true
|
||||||
@@ -291,6 +346,7 @@ function CLIENT:GetDCSGroup()
|
|||||||
local ClientUnit = Unit.getByName( self.ClientName )
|
local ClientUnit = Unit.getByName( self.ClientName )
|
||||||
|
|
||||||
local CoalitionsData = { AlivePlayersRed = coalition.getPlayers( coalition.side.RED ), AlivePlayersBlue = coalition.getPlayers( coalition.side.BLUE ) }
|
local CoalitionsData = { AlivePlayersRed = coalition.getPlayers( coalition.side.RED ), AlivePlayersBlue = coalition.getPlayers( coalition.side.BLUE ) }
|
||||||
|
|
||||||
for CoalitionId, CoalitionData in pairs( CoalitionsData ) do
|
for CoalitionId, CoalitionData in pairs( CoalitionsData ) do
|
||||||
self:T3( { "CoalitionData:", CoalitionData } )
|
self:T3( { "CoalitionData:", CoalitionData } )
|
||||||
for UnitId, UnitData in pairs( CoalitionData ) do
|
for UnitId, UnitData in pairs( CoalitionData ) do
|
||||||
@@ -299,10 +355,14 @@ function CLIENT:GetDCSGroup()
|
|||||||
|
|
||||||
--self:F(self.ClientName)
|
--self:F(self.ClientName)
|
||||||
if ClientUnit then
|
if ClientUnit then
|
||||||
|
|
||||||
local ClientGroup = ClientUnit:getGroup()
|
local ClientGroup = ClientUnit:getGroup()
|
||||||
|
|
||||||
if ClientGroup then
|
if ClientGroup then
|
||||||
self:T3( "ClientGroup = " .. self.ClientName )
|
self:T3( "ClientGroup = " .. self.ClientName )
|
||||||
|
|
||||||
if ClientGroup:isExist() and UnitData:getGroup():isExist() then
|
if ClientGroup:isExist() and UnitData:getGroup():isExist() then
|
||||||
|
|
||||||
if ClientGroup:getID() == UnitData:getGroup():getID() then
|
if ClientGroup:getID() == UnitData:getGroup():getID() then
|
||||||
self:T3( "Normal logic" )
|
self:T3( "Normal logic" )
|
||||||
self:T3( self.ClientName .. " : group found!" )
|
self:T3( self.ClientName .. " : group found!" )
|
||||||
@@ -310,15 +370,22 @@ function CLIENT:GetDCSGroup()
|
|||||||
self.ClientGroupName = ClientGroup:getName()
|
self.ClientGroupName = ClientGroup:getName()
|
||||||
return ClientGroup
|
return ClientGroup
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
-- Now we need to resolve the bugs in DCS 1.5 ...
|
-- Now we need to resolve the bugs in DCS 1.5 ...
|
||||||
-- Consult the database for the units of the Client Group. (ClientGroup:getUnits() returns nil)
|
-- Consult the database for the units of the Client Group. (ClientGroup:getUnits() returns nil)
|
||||||
self:T3( "Bug 1.5 logic" )
|
self:T3( "Bug 1.5 logic" )
|
||||||
|
|
||||||
local ClientGroupTemplate = _DATABASE.Templates.Units[self.ClientName].GroupTemplate
|
local ClientGroupTemplate = _DATABASE.Templates.Units[self.ClientName].GroupTemplate
|
||||||
|
|
||||||
self.ClientGroupID = ClientGroupTemplate.groupId
|
self.ClientGroupID = ClientGroupTemplate.groupId
|
||||||
|
|
||||||
self.ClientGroupName = _DATABASE.Templates.Units[self.ClientName].GroupName
|
self.ClientGroupName = _DATABASE.Templates.Units[self.ClientName].GroupName
|
||||||
|
|
||||||
self:T3( self.ClientName .. " : group found in bug 1.5 resolvement logic!" )
|
self:T3( self.ClientName .. " : group found in bug 1.5 resolvement logic!" )
|
||||||
return ClientGroup
|
return ClientGroup
|
||||||
|
|
||||||
end
|
end
|
||||||
-- else
|
-- else
|
||||||
-- error( "Client " .. self.ClientName .. " not found!" )
|
-- error( "Client " .. self.ClientName .. " not found!" )
|
||||||
@@ -343,22 +410,22 @@ function CLIENT:GetDCSGroup()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Nothing could be found :(
|
||||||
self.ClientGroupID = nil
|
self.ClientGroupID = nil
|
||||||
self.ClientGroupUnit = nil
|
self.ClientGroupName = nil
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- TODO: Check DCS#Group.ID
|
|
||||||
--- Get the group ID of the client.
|
--- Get the group ID of the client.
|
||||||
-- @param #CLIENT self
|
-- @param #CLIENT self
|
||||||
-- @return DCS#Group.ID
|
-- @return #number DCS#Group ID.
|
||||||
function CLIENT:GetClientGroupID()
|
function CLIENT:GetClientGroupID()
|
||||||
|
|
||||||
local ClientGroup = self:GetDCSGroup()
|
-- This updates the ID.
|
||||||
|
self:GetDCSGroup()
|
||||||
|
|
||||||
--self:F( self.ClientGroupID ) -- Determined in GetDCSGroup()
|
|
||||||
return self.ClientGroupID
|
return self.ClientGroupID
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -368,26 +435,28 @@ end
|
|||||||
-- @return #string
|
-- @return #string
|
||||||
function CLIENT:GetClientGroupName()
|
function CLIENT:GetClientGroupName()
|
||||||
|
|
||||||
local ClientGroup = self:GetDCSGroup()
|
-- This updates the group name.
|
||||||
|
self:GetDCSGroup()
|
||||||
|
|
||||||
self:T( self.ClientGroupName ) -- Determined in GetDCSGroup()
|
|
||||||
return self.ClientGroupName
|
return self.ClientGroupName
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns the UNIT of the CLIENT.
|
--- Returns the UNIT of the CLIENT.
|
||||||
-- @param #CLIENT self
|
-- @param #CLIENT self
|
||||||
-- @return Wrapper.Unit#UNIT
|
-- @return Wrapper.Unit#UNIT The client UNIT or `nil`.
|
||||||
function CLIENT:GetClientGroupUnit()
|
function CLIENT:GetClientGroupUnit()
|
||||||
self:F2()
|
self:F2()
|
||||||
|
|
||||||
local ClientDCSUnit = Unit.getByName( self.ClientName )
|
local ClientDCSUnit = Unit.getByName( self.ClientName )
|
||||||
|
|
||||||
self:T( self.ClientDCSUnit )
|
self:T( self.ClientDCSUnit )
|
||||||
|
|
||||||
if ClientDCSUnit and ClientDCSUnit:isExist() then
|
if ClientDCSUnit and ClientDCSUnit:isExist() then
|
||||||
local ClientUnit = _DATABASE:FindUnit( self.ClientName )
|
local ClientUnit=_DATABASE:FindUnit( self.ClientName )
|
||||||
self:T2( ClientUnit )
|
|
||||||
return ClientUnit
|
return ClientUnit
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns the DCSUnit of the CLIENT.
|
--- Returns the DCSUnit of the CLIENT.
|
||||||
|
|||||||
@@ -129,6 +129,7 @@
|
|||||||
-- * @{#CONTROLLABLE.Route}(): Make the Controllable to follow a given route.
|
-- * @{#CONTROLLABLE.Route}(): Make the Controllable to follow a given route.
|
||||||
-- * @{#CONTROLLABLE.RouteGroundTo}(): Make the GROUND Controllable to drive towards a specific coordinate.
|
-- * @{#CONTROLLABLE.RouteGroundTo}(): Make the GROUND Controllable to drive towards a specific coordinate.
|
||||||
-- * @{#CONTROLLABLE.RouteAirTo}(): Make the AIR Controllable to fly towards a specific coordinate.
|
-- * @{#CONTROLLABLE.RouteAirTo}(): Make the AIR Controllable to fly towards a specific coordinate.
|
||||||
|
-- * @{#CONTROLLABLE.RelocateGroundRandomInRadius}(): Relocate the GROUND controllable to a random point in a given radius.
|
||||||
--
|
--
|
||||||
-- # 5) Option methods
|
-- # 5) Option methods
|
||||||
--
|
--
|
||||||
@@ -173,6 +174,9 @@
|
|||||||
-- * @{#CONTROLLABLE.OptionAllowJettisonWeaponsOnThreat}
|
-- * @{#CONTROLLABLE.OptionAllowJettisonWeaponsOnThreat}
|
||||||
-- * @{#CONTROLLABLE.OptionKeepWeaponsOnThreat}
|
-- * @{#CONTROLLABLE.OptionKeepWeaponsOnThreat}
|
||||||
--
|
--
|
||||||
|
-- ## 5.5) Air-2-Air missile attack range:
|
||||||
|
-- * @{#CONTROLLABLE.OptionAAAttackRange}(): Defines the usage of A2A missiles against possible targets .
|
||||||
|
--
|
||||||
-- @field #CONTROLLABLE
|
-- @field #CONTROLLABLE
|
||||||
CONTROLLABLE = {
|
CONTROLLABLE = {
|
||||||
ClassName = "CONTROLLABLE",
|
ClassName = "CONTROLLABLE",
|
||||||
@@ -1424,16 +1428,22 @@ end
|
|||||||
-- @param DCS#Distance Radius The radius of the zone to deploy the fire at.
|
-- @param DCS#Distance Radius The radius of the zone to deploy the fire at.
|
||||||
-- @param #number AmmoCount (optional) Quantity of ammunition to expand (omit to fire until ammunition is depleted).
|
-- @param #number AmmoCount (optional) Quantity of ammunition to expand (omit to fire until ammunition is depleted).
|
||||||
-- @param #number WeaponType (optional) Enum for weapon type ID. This value is only required if you want the group firing to use a specific weapon, for instance using the task on a ship to force it to fire guided missiles at targets within cannon range. See http://wiki.hoggit.us/view/DCS_enum_weapon_flag
|
-- @param #number WeaponType (optional) Enum for weapon type ID. This value is only required if you want the group firing to use a specific weapon, for instance using the task on a ship to force it to fire guided missiles at targets within cannon range. See http://wiki.hoggit.us/view/DCS_enum_weapon_flag
|
||||||
|
-- @param #number Altitude (Optional) Altitude in meters.
|
||||||
|
-- @param #number ASL Altitude is above mean sea level. Default is above ground level.
|
||||||
-- @return DCS#Task The DCS task structure.
|
-- @return DCS#Task The DCS task structure.
|
||||||
function CONTROLLABLE:TaskFireAtPoint( Vec2, Radius, AmmoCount, WeaponType )
|
function CONTROLLABLE:TaskFireAtPoint( Vec2, Radius, AmmoCount, WeaponType, Altitude, ASL )
|
||||||
|
|
||||||
local DCSTask = {
|
local DCSTask = {
|
||||||
id = 'FireAtPoint',
|
id = 'FireAtPoint',
|
||||||
params = {
|
params = {
|
||||||
point = Vec2,
|
point = Vec2,
|
||||||
|
x=Vec2.x,
|
||||||
|
y=Vec2.y,
|
||||||
zoneRadius = Radius,
|
zoneRadius = Radius,
|
||||||
|
radius = Radius,
|
||||||
expendQty = 100, -- dummy value
|
expendQty = 100, -- dummy value
|
||||||
expendQtyEnabled = false,
|
expendQtyEnabled = false,
|
||||||
|
alt_type = ASL and 0 or 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1442,10 +1452,16 @@ function CONTROLLABLE:TaskFireAtPoint( Vec2, Radius, AmmoCount, WeaponType )
|
|||||||
DCSTask.params.expendQtyEnabled = true
|
DCSTask.params.expendQtyEnabled = true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if Altitude then
|
||||||
|
DCSTask.params.altitude=Altitude
|
||||||
|
end
|
||||||
|
|
||||||
if WeaponType then
|
if WeaponType then
|
||||||
DCSTask.params.weaponType=WeaponType
|
DCSTask.params.weaponType=WeaponType
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--self:I(DCSTask)
|
||||||
|
|
||||||
return DCSTask
|
return DCSTask
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1463,6 +1479,7 @@ end
|
|||||||
--- (AIR + GROUND) The task makes the controllable/unit a FAC and orders the FAC to control the target (enemy ground controllable) destruction.
|
--- (AIR + GROUND) The task makes the controllable/unit a FAC and orders the FAC to control the target (enemy ground controllable) destruction.
|
||||||
-- The killer is player-controlled allied CAS-aircraft that is in contact with the FAC.
|
-- The killer is player-controlled allied CAS-aircraft that is in contact with the FAC.
|
||||||
-- If the task is assigned to the controllable lead unit will be a FAC.
|
-- If the task is assigned to the controllable lead unit will be a FAC.
|
||||||
|
-- It's important to note that depending on the type of unit that is being assigned the task (AIR or GROUND), you must choose the correct type of callsign enumerator. For airborne controllables use CALLSIGN.Aircraft and for ground based use CALLSIGN.JTAC enumerators.
|
||||||
-- @param #CONTROLLABLE self
|
-- @param #CONTROLLABLE self
|
||||||
-- @param Wrapper.Group#GROUP AttackGroup Target GROUP object.
|
-- @param Wrapper.Group#GROUP AttackGroup Target GROUP object.
|
||||||
-- @param #number WeaponType Bitmask of weapon types, which are allowed to use.
|
-- @param #number WeaponType Bitmask of weapon types, which are allowed to use.
|
||||||
@@ -1470,7 +1487,7 @@ end
|
|||||||
-- @param #boolean Datalink (Optional) Allows to use datalink to send the target information to attack aircraft. Enabled by default.
|
-- @param #boolean Datalink (Optional) Allows to use datalink to send the target information to attack aircraft. Enabled by default.
|
||||||
-- @param #number Frequency Frequency in MHz used to communicate with the FAC. Default 133 MHz.
|
-- @param #number Frequency Frequency in MHz used to communicate with the FAC. Default 133 MHz.
|
||||||
-- @param #number Modulation Modulation of radio for communication. Default 0=AM.
|
-- @param #number Modulation Modulation of radio for communication. Default 0=AM.
|
||||||
-- @param #number CallsignName Callsign enumerator name of the FAC.
|
-- @param #number CallsignName Callsign enumerator name of the FAC. (CALLSIGN.Aircraft.{name} for airborne controllables, CALLSIGN.JTACS.{name} for ground units)
|
||||||
-- @param #number CallsignNumber Callsign number, e.g. Axeman-**1**.
|
-- @param #number CallsignNumber Callsign number, e.g. Axeman-**1**.
|
||||||
-- @return DCS#Task The DCS task structure.
|
-- @return DCS#Task The DCS task structure.
|
||||||
function CONTROLLABLE:TaskFAC_AttackGroup( AttackGroup, WeaponType, Designation, Datalink, Frequency, Modulation, CallsignName, CallsignNumber )
|
function CONTROLLABLE:TaskFAC_AttackGroup( AttackGroup, WeaponType, Designation, Datalink, Frequency, Modulation, CallsignName, CallsignNumber )
|
||||||
@@ -1834,7 +1851,26 @@ do -- Patrol methods
|
|||||||
|
|
||||||
-- Calculate the new Route.
|
-- Calculate the new Route.
|
||||||
local FromCoord = PatrolGroup:GetCoordinate()
|
local FromCoord = PatrolGroup:GetCoordinate()
|
||||||
local From = FromCoord:WaypointGround( 120 )
|
|
||||||
|
-- test for submarine
|
||||||
|
local depth = 0
|
||||||
|
local IsSub = false
|
||||||
|
if PatrolGroup:IsShip() then
|
||||||
|
local navalvec3 = FromCoord:GetVec3()
|
||||||
|
if navalvec3.y < 0 then
|
||||||
|
depth = navalvec3.y
|
||||||
|
IsSub = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local Waypoint = Waypoints[1]
|
||||||
|
local Speed = Waypoint.speed or (20 / 3.6)
|
||||||
|
local From = FromCoord:WaypointGround( Speed )
|
||||||
|
|
||||||
|
if IsSub then
|
||||||
|
From = FromCoord:WaypointNaval( Speed, Waypoint.alt )
|
||||||
|
end
|
||||||
|
|
||||||
table.insert( Waypoints, 1, From )
|
table.insert( Waypoints, 1, From )
|
||||||
|
|
||||||
@@ -1875,7 +1911,16 @@ do -- Patrol methods
|
|||||||
if ToWaypoint then
|
if ToWaypoint then
|
||||||
FromWaypoint = ToWaypoint
|
FromWaypoint = ToWaypoint
|
||||||
end
|
end
|
||||||
|
-- test for submarine
|
||||||
|
local depth = 0
|
||||||
|
local IsSub = false
|
||||||
|
if PatrolGroup:IsShip() then
|
||||||
|
local navalvec3 = FromCoord:GetVec3()
|
||||||
|
if navalvec3.y < 0 then
|
||||||
|
depth = navalvec3.y
|
||||||
|
IsSub = true
|
||||||
|
end
|
||||||
|
end
|
||||||
-- Loop until a waypoint has been found that is not the same as the current waypoint.
|
-- Loop until a waypoint has been found that is not the same as the current waypoint.
|
||||||
-- Otherwise the object zon't move or drive in circles and the algorithm would not do exactly
|
-- Otherwise the object zon't move or drive in circles and the algorithm would not do exactly
|
||||||
-- what it is supposed to do, which is making groups drive around.
|
-- what it is supposed to do, which is making groups drive around.
|
||||||
@@ -1890,9 +1935,13 @@ do -- Patrol methods
|
|||||||
local ToCoord = COORDINATE:NewFromVec2( { x = Waypoint.x, y = Waypoint.y } )
|
local ToCoord = COORDINATE:NewFromVec2( { x = Waypoint.x, y = Waypoint.y } )
|
||||||
-- Create a "ground route point", which is a "point" structure that can be given as a parameter to a Task
|
-- Create a "ground route point", which is a "point" structure that can be given as a parameter to a Task
|
||||||
local Route = {}
|
local Route = {}
|
||||||
Route[#Route+1] = FromCoord:WaypointGround( 0 )
|
if IsSub then
|
||||||
Route[#Route+1] = ToCoord:WaypointGround( Speed, Formation )
|
Route[#Route+1] = FromCoord:WaypointNaval( Speed, depth )
|
||||||
|
Route[#Route+1] = ToCoord:WaypointNaval( Speed, Waypoint.alt )
|
||||||
|
else
|
||||||
|
Route[#Route+1] = FromCoord:WaypointGround( Speed, Formation )
|
||||||
|
Route[#Route+1] = ToCoord:WaypointGround( Speed, Formation )
|
||||||
|
end
|
||||||
|
|
||||||
local TaskRouteToZone = PatrolGroup:TaskFunction( "CONTROLLABLE.PatrolRouteRandom", Speed, Formation, ToWaypoint )
|
local TaskRouteToZone = PatrolGroup:TaskFunction( "CONTROLLABLE.PatrolRouteRandom", Speed, Formation, ToWaypoint )
|
||||||
|
|
||||||
@@ -1937,15 +1986,30 @@ do -- Patrol methods
|
|||||||
-- Calculate the new Route.
|
-- Calculate the new Route.
|
||||||
local FromCoord = PatrolGroup:GetCoordinate()
|
local FromCoord = PatrolGroup:GetCoordinate()
|
||||||
|
|
||||||
|
-- test for submarine
|
||||||
|
local depth = 0
|
||||||
|
local IsSub = false
|
||||||
|
if PatrolGroup:IsShip() then
|
||||||
|
local navalvec3 = FromCoord:GetVec3()
|
||||||
|
if navalvec3.y < 0 then
|
||||||
|
depth = navalvec3.y
|
||||||
|
IsSub = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- Select a random Zone and get the Coordinate of the new Zone.
|
-- Select a random Zone and get the Coordinate of the new Zone.
|
||||||
local RandomZone = ZoneList[ math.random( 1, #ZoneList ) ] -- Core.Zone#ZONE
|
local RandomZone = ZoneList[ math.random( 1, #ZoneList ) ] -- Core.Zone#ZONE
|
||||||
local ToCoord = RandomZone:GetRandomCoordinate( 10 )
|
local ToCoord = RandomZone:GetRandomCoordinate( 10 )
|
||||||
|
|
||||||
-- Create a "ground route point", which is a "point" structure that can be given as a parameter to a Task
|
-- Create a "ground route point", which is a "point" structure that can be given as a parameter to a Task
|
||||||
local Route = {}
|
local Route = {}
|
||||||
Route[#Route+1] = FromCoord:WaypointGround( 20 )
|
if IsSub then
|
||||||
Route[#Route+1] = ToCoord:WaypointGround( Speed, Formation )
|
Route[#Route+1] = FromCoord:WaypointNaval( Speed, depth )
|
||||||
|
Route[#Route+1] = ToCoord:WaypointNaval( Speed, depth )
|
||||||
|
else
|
||||||
|
Route[#Route+1] = FromCoord:WaypointGround( Speed, Formation )
|
||||||
|
Route[#Route+1] = ToCoord:WaypointGround( Speed, Formation )
|
||||||
|
end
|
||||||
|
|
||||||
local TaskRouteToZone = PatrolGroup:TaskFunction( "CONTROLLABLE.PatrolZones", ZoneList, Speed, Formation, DelayMin, DelayMax )
|
local TaskRouteToZone = PatrolGroup:TaskFunction( "CONTROLLABLE.PatrolZones", ZoneList, Speed, Formation, DelayMin, DelayMax )
|
||||||
|
|
||||||
@@ -2828,7 +2892,7 @@ function CONTROLLABLE:GetDetectedUnitSet(DetectVisual, DetectOptical, DetectRada
|
|||||||
return unitset
|
return unitset
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Return the detected target groups of the controllable as a @{SET_GROUP}.
|
--- Return the detected target groups of the controllable as a @{Core.Set#SET_GROUP}.
|
||||||
-- The optional parametes specify the detection methods that can be applied.
|
-- The optional parametes specify the detection methods that can be applied.
|
||||||
-- If no detection method is given, the detection will use all the available methods by default.
|
-- If no detection method is given, the detection will use all the available methods by default.
|
||||||
-- @param Wrapper.Controllable#CONTROLLABLE self
|
-- @param Wrapper.Controllable#CONTROLLABLE self
|
||||||
@@ -2891,7 +2955,7 @@ end
|
|||||||
--- Set option for Rules of Engagement (ROE).
|
--- Set option for Rules of Engagement (ROE).
|
||||||
-- @param Wrapper.Controllable#CONTROLLABLE self
|
-- @param Wrapper.Controllable#CONTROLLABLE self
|
||||||
-- @param #number ROEvalue ROE value. See ENUMS.ROE.
|
-- @param #number ROEvalue ROE value. See ENUMS.ROE.
|
||||||
-- @return Wrapper.Controllable#CONTROLLABLE self
|
-- @return #CONTROLLABLE self
|
||||||
function CONTROLLABLE:OptionROE(ROEvalue)
|
function CONTROLLABLE:OptionROE(ROEvalue)
|
||||||
|
|
||||||
local DCSControllable = self:GetDCSObject()
|
local DCSControllable = self:GetDCSObject()
|
||||||
@@ -2933,8 +2997,8 @@ function CONTROLLABLE:OptionROEHoldFirePossible()
|
|||||||
end
|
end
|
||||||
|
|
||||||
--- Weapons Hold: AI will hold fire under all circumstances.
|
--- Weapons Hold: AI will hold fire under all circumstances.
|
||||||
-- @param Wrapper.Controllable#CONTROLLABLE self
|
-- @param #CONTROLLABLE self
|
||||||
-- @return Wrapper.Controllable#CONTROLLABLE self
|
-- @return #CONTROLLABLE self
|
||||||
function CONTROLLABLE:OptionROEHoldFire()
|
function CONTROLLABLE:OptionROEHoldFire()
|
||||||
self:F2( { self.ControllableName } )
|
self:F2( { self.ControllableName } )
|
||||||
|
|
||||||
@@ -3532,7 +3596,7 @@ end
|
|||||||
-- Note that when WayPointInitialize is called, the Mission of the controllable is RESTARTED!
|
-- Note that when WayPointInitialize is called, the Mission of the controllable is RESTARTED!
|
||||||
-- @param #CONTROLLABLE self
|
-- @param #CONTROLLABLE self
|
||||||
-- @param #table WayPoints If WayPoints is given, then use the route.
|
-- @param #table WayPoints If WayPoints is given, then use the route.
|
||||||
-- @return #CONTROLLABLE
|
-- @return #CONTROLLABLE self
|
||||||
function CONTROLLABLE:WayPointInitialize( WayPoints )
|
function CONTROLLABLE:WayPointInitialize( WayPoints )
|
||||||
self:F( { WayPoints } )
|
self:F( { WayPoints } )
|
||||||
|
|
||||||
@@ -3563,7 +3627,7 @@ end
|
|||||||
-- @param #number WayPoint The waypoint number. Note that the start waypoint on the route is WayPoint 1!
|
-- @param #number WayPoint The waypoint number. Note that the start waypoint on the route is WayPoint 1!
|
||||||
-- @param #number WayPointIndex When defining multiple WayPoint functions for one WayPoint, use WayPointIndex to set the sequence of actions.
|
-- @param #number WayPointIndex When defining multiple WayPoint functions for one WayPoint, use WayPointIndex to set the sequence of actions.
|
||||||
-- @param #function WayPointFunction The waypoint function to be called when the controllable moves over the waypoint. The waypoint function takes variable parameters.
|
-- @param #function WayPointFunction The waypoint function to be called when the controllable moves over the waypoint. The waypoint function takes variable parameters.
|
||||||
-- @return #CONTROLLABLE
|
-- @return #CONTROLLABLE self
|
||||||
function CONTROLLABLE:WayPointFunction( WayPoint, WayPointIndex, WayPointFunction, ... )
|
function CONTROLLABLE:WayPointFunction( WayPoint, WayPointIndex, WayPointFunction, ... )
|
||||||
self:F2( { WayPoint, WayPointIndex, WayPointFunction } )
|
self:F2( { WayPoint, WayPointIndex, WayPointFunction } )
|
||||||
|
|
||||||
@@ -3579,7 +3643,7 @@ end
|
|||||||
-- @param #CONTROLLABLE self
|
-- @param #CONTROLLABLE self
|
||||||
-- @param #number WayPoint The WayPoint from where to execute the mission.
|
-- @param #number WayPoint The WayPoint from where to execute the mission.
|
||||||
-- @param #number WaitTime The amount seconds to wait before initiating the mission.
|
-- @param #number WaitTime The amount seconds to wait before initiating the mission.
|
||||||
-- @return #CONTROLLABLE
|
-- @return #CONTROLLABLE self
|
||||||
function CONTROLLABLE:WayPointExecute( WayPoint, WaitTime )
|
function CONTROLLABLE:WayPointExecute( WayPoint, WaitTime )
|
||||||
self:F( { WayPoint, WaitTime } )
|
self:F( { WayPoint, WaitTime } )
|
||||||
|
|
||||||
@@ -3659,3 +3723,125 @@ function CONTROLLABLE:OptionRestrictBurner(RestrictBurner)
|
|||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Sets Controllable Option for A2A attack range for AIR FIGHTER units.
|
||||||
|
-- @param #CONTROLLABLE self
|
||||||
|
-- @param #number range Defines the range
|
||||||
|
-- @return #CONTROLLABLE self
|
||||||
|
-- @usage Range can be one of MAX_RANGE = 0, NEZ_RANGE = 1, HALF_WAY_RMAX_NEZ = 2, TARGET_THREAT_EST = 3, RANDOM_RANGE = 4. Defaults to 3. See: https://wiki.hoggitworld.com/view/DCS_option_missileAttack
|
||||||
|
function CONTROLLABLE:OptionAAAttackRange(range)
|
||||||
|
self:F2( { self.ControllableName } )
|
||||||
|
-- defaults to 3
|
||||||
|
local range = range or 3
|
||||||
|
if range < 0 or range > 4 then
|
||||||
|
range = 3
|
||||||
|
end
|
||||||
|
local DCSControllable = self:GetDCSObject()
|
||||||
|
if DCSControllable then
|
||||||
|
local Controller = self:_GetController()
|
||||||
|
if Controller then
|
||||||
|
if self:IsAir() then
|
||||||
|
self:SetOption(AI.Option.Air.val.MISSILE_ATTACK, range)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Defines the range at which a GROUND unit/group is allowed to use its weapons automatically.
|
||||||
|
-- @param #CONTROLLABLE self
|
||||||
|
-- @param #number EngageRange Engage range limit in percent (a number between 0 and 100). Default 100.
|
||||||
|
-- @return #CONTROLLABLE self
|
||||||
|
function CONTROLLABLE:OptionEngageRange(EngageRange)
|
||||||
|
self:F2( { self.ControllableName } )
|
||||||
|
-- Set default if not specified.
|
||||||
|
EngageRange=EngageRange or 100
|
||||||
|
if EngageRange < 0 or EngageRange > 100 then
|
||||||
|
EngageRange = 100
|
||||||
|
end
|
||||||
|
local DCSControllable = self:GetDCSObject()
|
||||||
|
if DCSControllable then
|
||||||
|
local Controller = self:_GetController()
|
||||||
|
if Controller then
|
||||||
|
if self:IsGround() then
|
||||||
|
self:SetOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION, EngageRange)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- (GROUND) Relocate controllable to a random point within a given radius; use e.g.for evasive actions; Note that not all ground controllables can actually drive, also the alarm state of the controllable might stop it from moving.
|
||||||
|
-- @param #CONTROLLABLE self
|
||||||
|
-- @param #number speed Speed of the controllable, default 20
|
||||||
|
-- @param #number radius Radius of the relocation zone, default 500
|
||||||
|
-- @param #boolean onroad If true, route on road (less problems with AI way finding), default true
|
||||||
|
-- @param #boolean shortcut If true and onroad is set, take a shorter route - if available - off road, default false
|
||||||
|
-- @param #string formation Formation string as in the mission editor, e.g. "Vee", "Diamond", "Line abreast", etc. Defaults to "Off Road"
|
||||||
|
-- @return #CONTROLLABLE self
|
||||||
|
function CONTROLLABLE:RelocateGroundRandomInRadius(speed, radius, onroad, shortcut, formation)
|
||||||
|
self:F2( { self.ControllableName } )
|
||||||
|
|
||||||
|
local _coord = self:GetCoordinate()
|
||||||
|
local _radius = radius or 500
|
||||||
|
local _speed = speed or 20
|
||||||
|
local _tocoord = _coord:GetRandomCoordinateInRadius(_radius,100)
|
||||||
|
local _onroad = onroad or true
|
||||||
|
local _grptsk = {}
|
||||||
|
local _candoroad = false
|
||||||
|
local _shortcut = shortcut or false
|
||||||
|
local _formation = formation or "Off Road"
|
||||||
|
|
||||||
|
-- create a DCS Task an push it on the group
|
||||||
|
if onroad then
|
||||||
|
_grptsk, _candoroad = self:TaskGroundOnRoad(_tocoord,_speed,_formation,_shortcut)
|
||||||
|
self:Route(_grptsk,5)
|
||||||
|
else
|
||||||
|
self:TaskRouteToVec2(_tocoord:GetVec2(),_speed,_formation)
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Defines how long a GROUND unit/group will move to avoid an ongoing attack.
|
||||||
|
-- @param #CONTROLLABLE self
|
||||||
|
-- @param #number Seconds Any positive number: AI will disperse, but only for the specified time before continuing their route. 0: AI will not disperse.
|
||||||
|
-- @return #CONTROLLABLE self
|
||||||
|
function CONTROLLABLE:OptionDisperseOnAttack(Seconds)
|
||||||
|
self:F2( { self.ControllableName } )
|
||||||
|
-- Set default if not specified.
|
||||||
|
local seconds = Seconds or 0
|
||||||
|
local DCSControllable = self:GetDCSObject()
|
||||||
|
if DCSControllable then
|
||||||
|
local Controller = self:_GetController()
|
||||||
|
if Controller then
|
||||||
|
if self:IsGround() then
|
||||||
|
self:SetOption(AI.Option.Ground.id.DISPERSE_ON_ATTACK, seconds)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns if the unit is a submarine.
|
||||||
|
-- @param #POSITIONABLE self
|
||||||
|
-- @return #boolean Submarines attributes result.
|
||||||
|
function POSITIONABLE:IsSubmarine()
|
||||||
|
self:F2()
|
||||||
|
|
||||||
|
local DCSUnit = self:GetDCSObject()
|
||||||
|
|
||||||
|
if DCSUnit then
|
||||||
|
local UnitDescriptor = DCSUnit:getDesc()
|
||||||
|
if UnitDescriptor.attributes["Submarines"] == true then
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|||||||
@@ -310,8 +310,7 @@ end
|
|||||||
|
|
||||||
--- Returns the @{DCS#Position3} position vectors indicating the point and direction vectors in 3D of the POSITIONABLE within the mission.
|
--- Returns the @{DCS#Position3} position vectors indicating the point and direction vectors in 3D of the POSITIONABLE within the mission.
|
||||||
-- @param Wrapper.Positionable#POSITIONABLE self
|
-- @param Wrapper.Positionable#POSITIONABLE self
|
||||||
-- @return DCS#Position The 3D position vectors of the POSITIONABLE.
|
-- @return DCS#Position The 3D position vectors of the POSITIONABLE or #nil if the groups not existing or alive.
|
||||||
-- @return #nil The POSITIONABLE is not existing or alive.
|
|
||||||
function GROUP:GetPositionVec3() -- Overridden from POSITIONABLE:GetPositionVec3()
|
function GROUP:GetPositionVec3() -- Overridden from POSITIONABLE:GetPositionVec3()
|
||||||
self:F2( self.PositionableName )
|
self:F2( self.PositionableName )
|
||||||
|
|
||||||
@@ -339,9 +338,7 @@ end
|
|||||||
-- If the first @{Wrapper.Unit} of the group is inactive, it will return false.
|
-- If the first @{Wrapper.Unit} of the group is inactive, it will return false.
|
||||||
--
|
--
|
||||||
-- @param #GROUP self
|
-- @param #GROUP self
|
||||||
-- @return #boolean true if the group is alive and active.
|
-- @return #boolean `true` if the group is alive *and* active, `false` if the group is alive but inactive or `#nil` if the group does not exist anymore.
|
||||||
-- @return #boolean false if the group is alive but inactive.
|
|
||||||
-- @return #nil if the group does not exist anymore.
|
|
||||||
function GROUP:IsAlive()
|
function GROUP:IsAlive()
|
||||||
self:F2( self.GroupName )
|
self:F2( self.GroupName )
|
||||||
|
|
||||||
@@ -363,8 +360,7 @@ end
|
|||||||
|
|
||||||
--- Returns if the group is activated.
|
--- Returns if the group is activated.
|
||||||
-- @param #GROUP self
|
-- @param #GROUP self
|
||||||
-- @return #boolean true if group is activated.
|
-- @return #boolean `true` if group is activated or `#nil` The group is not existing or alive.
|
||||||
-- @return #nil The group is not existing or alive.
|
|
||||||
function GROUP:IsActive()
|
function GROUP:IsActive()
|
||||||
self:F2( self.GroupName )
|
self:F2( self.GroupName )
|
||||||
|
|
||||||
@@ -412,7 +408,6 @@ function GROUP:Destroy( GenerateEvent, delay )
|
|||||||
self:F2( self.GroupName )
|
self:F2( self.GroupName )
|
||||||
|
|
||||||
if delay and delay>0 then
|
if delay and delay>0 then
|
||||||
--SCHEDULER:New(nil, GROUP.Destroy, {self, GenerateEvent}, delay)
|
|
||||||
self:ScheduleOnce(delay, GROUP.Destroy, self, GenerateEvent)
|
self:ScheduleOnce(delay, GROUP.Destroy, self, GenerateEvent)
|
||||||
else
|
else
|
||||||
|
|
||||||
@@ -568,12 +563,12 @@ function GROUP:GetSpeedMax()
|
|||||||
|
|
||||||
local Units=self:GetUnits()
|
local Units=self:GetUnits()
|
||||||
|
|
||||||
local speedmax=nil
|
local speedmax=0
|
||||||
|
|
||||||
for _,unit in pairs(Units) do
|
for _,unit in pairs(Units) do
|
||||||
local unit=unit --Wrapper.Unit#UNIT
|
local unit=unit --Wrapper.Unit#UNIT
|
||||||
local speed=unit:GetSpeedMax()
|
local speed=unit:GetSpeedMax()
|
||||||
if speedmax==nil then
|
if speedmax==0 then
|
||||||
speedmax=speed
|
speedmax=speed
|
||||||
elseif speed<speedmax then
|
elseif speed<speedmax then
|
||||||
speedmax=speed
|
speedmax=speed
|
||||||
@@ -1147,6 +1142,7 @@ function GROUP:GetAmmunition()
|
|||||||
local Nshells=0
|
local Nshells=0
|
||||||
local Nrockets=0
|
local Nrockets=0
|
||||||
local Nmissiles=0
|
local Nmissiles=0
|
||||||
|
local Nbombs=0
|
||||||
|
|
||||||
if DCSControllable then
|
if DCSControllable then
|
||||||
|
|
||||||
@@ -1155,18 +1151,19 @@ function GROUP:GetAmmunition()
|
|||||||
local Unit = UnitData -- Wrapper.Unit#UNIT
|
local Unit = UnitData -- Wrapper.Unit#UNIT
|
||||||
|
|
||||||
-- Get ammo of the unit
|
-- Get ammo of the unit
|
||||||
local ntot, nshells, nrockets, nmissiles = Unit:GetAmmunition()
|
local ntot, nshells, nrockets, nbombs, nmissiles = Unit:GetAmmunition()
|
||||||
|
|
||||||
Ntot=Ntot+ntot
|
Ntot=Ntot+ntot
|
||||||
Nshells=Nshells+nshells
|
Nshells=Nshells+nshells
|
||||||
Nrockets=Nrockets+nrockets
|
Nrockets=Nrockets+nrockets
|
||||||
Nmissiles=Nmissiles+nmissiles
|
Nmissiles=Nmissiles+nmissiles
|
||||||
|
Nbombs=Nbombs+nbombs
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return Ntot, Nshells, Nrockets, Nmissiles
|
return Ntot, Nshells, Nrockets, Nbombs, Nmissiles
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -2202,27 +2199,52 @@ function GROUP:IsAirborne(AllUnits)
|
|||||||
|
|
||||||
if units then
|
if units then
|
||||||
|
|
||||||
for _,_unit in pairs(units) do
|
if AllUnits then
|
||||||
local unit=_unit --Wrapper.Unit#UNIT
|
|
||||||
|
|
||||||
if unit then
|
--- We want to know if ALL units are airborne.
|
||||||
|
|
||||||
-- Unit in air or not.
|
for _,_unit in pairs(units) do
|
||||||
local inair=unit:InAir()
|
local unit=_unit --Wrapper.Unit#UNIT
|
||||||
|
|
||||||
-- Unit is not in air and we wanted to know whether ALL units are ==> return false
|
if unit then
|
||||||
if inair==false and AllUnits==true then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- At least one unit is in are and we did not care which one.
|
-- Unit in air or not.
|
||||||
if inair==true and not AllUnits then
|
local inair=unit:InAir()
|
||||||
return true
|
|
||||||
|
-- At least one unit is not in air.
|
||||||
|
if not inair then
|
||||||
|
return false
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
-- At least one unit is in the air.
|
|
||||||
|
-- All units are in air.
|
||||||
return true
|
return true
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
--- We want to know if ANY unit is airborne.
|
||||||
|
|
||||||
|
for _,_unit in pairs(units) do
|
||||||
|
local unit=_unit --Wrapper.Unit#UNIT
|
||||||
|
|
||||||
|
if unit then
|
||||||
|
|
||||||
|
-- Unit in air or not.
|
||||||
|
local inair=unit:InAir()
|
||||||
|
|
||||||
|
if inair then
|
||||||
|
-- At least one unit is in air.
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
-- No unit is in air.
|
||||||
|
return false
|
||||||
|
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -2299,41 +2321,44 @@ function GROUP:GetAttribute()
|
|||||||
local unarmedship=self:HasAttribute("Unarmed ships")
|
local unarmedship=self:HasAttribute("Unarmed ships")
|
||||||
|
|
||||||
|
|
||||||
-- Define attribute. Order is important.
|
-- Define attribute. Order of attack is important.
|
||||||
if transportplane then
|
if fighter then
|
||||||
attribute=GROUP.Attribute.AIR_TRANSPORTPLANE
|
|
||||||
elseif awacs then
|
|
||||||
attribute=GROUP.Attribute.AIR_AWACS
|
|
||||||
elseif fighter then
|
|
||||||
attribute=GROUP.Attribute.AIR_FIGHTER
|
attribute=GROUP.Attribute.AIR_FIGHTER
|
||||||
elseif bomber then
|
elseif bomber then
|
||||||
attribute=GROUP.Attribute.AIR_BOMBER
|
attribute=GROUP.Attribute.AIR_BOMBER
|
||||||
|
elseif awacs then
|
||||||
|
attribute=GROUP.Attribute.AIR_AWACS
|
||||||
|
elseif transportplane then
|
||||||
|
attribute=GROUP.Attribute.AIR_TRANSPORTPLANE
|
||||||
elseif tanker then
|
elseif tanker then
|
||||||
attribute=GROUP.Attribute.AIR_TANKER
|
attribute=GROUP.Attribute.AIR_TANKER
|
||||||
elseif transporthelo then
|
-- helos
|
||||||
attribute=GROUP.Attribute.AIR_TRANSPORTHELO
|
|
||||||
elseif attackhelicopter then
|
elseif attackhelicopter then
|
||||||
attribute=GROUP.Attribute.AIR_ATTACKHELO
|
attribute=GROUP.Attribute.AIR_ATTACKHELO
|
||||||
|
elseif transporthelo then
|
||||||
|
attribute=GROUP.Attribute.AIR_TRANSPORTHELO
|
||||||
elseif uav then
|
elseif uav then
|
||||||
attribute=GROUP.Attribute.AIR_UAV
|
attribute=GROUP.Attribute.AIR_UAV
|
||||||
elseif apc then
|
-- ground - order of attack
|
||||||
attribute=GROUP.Attribute.GROUND_APC
|
|
||||||
elseif infantry then
|
|
||||||
attribute=GROUP.Attribute.GROUND_INFANTRY
|
|
||||||
elseif artillery then
|
|
||||||
attribute=GROUP.Attribute.GROUND_ARTILLERY
|
|
||||||
elseif tank then
|
|
||||||
attribute=GROUP.Attribute.GROUND_TANK
|
|
||||||
elseif aaa then
|
|
||||||
attribute=GROUP.Attribute.GROUND_AAA
|
|
||||||
elseif ewr then
|
elseif ewr then
|
||||||
attribute=GROUP.Attribute.GROUND_EWR
|
attribute=GROUP.Attribute.GROUND_EWR
|
||||||
elseif sam then
|
elseif sam then
|
||||||
attribute=GROUP.Attribute.GROUND_SAM
|
attribute=GROUP.Attribute.GROUND_SAM
|
||||||
|
elseif aaa then
|
||||||
|
attribute=GROUP.Attribute.GROUND_AAA
|
||||||
|
elseif artillery then
|
||||||
|
attribute=GROUP.Attribute.GROUND_ARTILLERY
|
||||||
|
elseif tank then
|
||||||
|
attribute=GROUP.Attribute.GROUND_TANK
|
||||||
|
elseif apc then
|
||||||
|
attribute=GROUP.Attribute.GROUND_APC
|
||||||
|
elseif infantry then
|
||||||
|
attribute=GROUP.Attribute.GROUND_INFANTRY
|
||||||
elseif truck then
|
elseif truck then
|
||||||
attribute=GROUP.Attribute.GROUND_TRUCK
|
attribute=GROUP.Attribute.GROUND_TRUCK
|
||||||
elseif train then
|
elseif train then
|
||||||
attribute=GROUP.Attribute.GROUND_TRAIN
|
attribute=GROUP.Attribute.GROUND_TRAIN
|
||||||
|
-- ships
|
||||||
elseif aircraftcarrier then
|
elseif aircraftcarrier then
|
||||||
attribute=GROUP.Attribute.NAVAL_AIRCRAFTCARRIER
|
attribute=GROUP.Attribute.NAVAL_AIRCRAFTCARRIER
|
||||||
elseif warship then
|
elseif warship then
|
||||||
@@ -2526,6 +2551,64 @@ do -- Players
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- GROUND - Switch on/off radar emissions for the group.
|
||||||
|
-- @param #GROUP self
|
||||||
|
-- @param #boolean switch If true, emission is enabled. If false, emission is disabled.
|
||||||
|
-- @return #GROUP self
|
||||||
|
function GROUP:EnableEmission(switch)
|
||||||
|
self:F2( self.GroupName )
|
||||||
|
local switch = switch or false
|
||||||
|
|
||||||
|
local DCSUnit = self:GetDCSObject()
|
||||||
|
|
||||||
|
if DCSUnit then
|
||||||
|
|
||||||
|
DCSUnit:enableEmission(switch)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Switch on/off invisible flag for the group.
|
||||||
|
-- @param #GROUP self
|
||||||
|
-- @param #boolean switch If true, emission is enabled. If false, emission is disabled.
|
||||||
|
-- @return #GROUP self
|
||||||
|
function GROUP:SetCommandInvisible(switch)
|
||||||
|
self:F2( self.GroupName )
|
||||||
|
if switch==nil then
|
||||||
|
switch=false
|
||||||
|
end
|
||||||
|
local SetInvisible = {id = 'SetInvisible', params = {value = switch}}
|
||||||
|
self:SetCommand(SetInvisible)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Switch on/off immortal flag for the group.
|
||||||
|
-- @param #GROUP self
|
||||||
|
-- @param #boolean switch If true, emission is enabled. If false, emission is disabled.
|
||||||
|
-- @return #GROUP self
|
||||||
|
function GROUP:SetCommandImmortal(switch)
|
||||||
|
self:F2( self.GroupName )
|
||||||
|
if switch==nil then
|
||||||
|
switch=false
|
||||||
|
end
|
||||||
|
local SetImmortal = {id = 'SetImmortal', params = {value = switch}}
|
||||||
|
self:SetCommand(SetImmortal)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get skill from Group. Effectively gets the skill from Unit 1 as the group holds no skill value.
|
||||||
|
-- @param #GROUP self
|
||||||
|
-- @return #string Skill String of skill name.
|
||||||
|
function GROUP:GetSkill()
|
||||||
|
self:F2( self.GroupName )
|
||||||
|
local unit = self:GetUnit(1)
|
||||||
|
local name = unit:GetName()
|
||||||
|
local skill = _DATABASE.Templates.Units[name].Template.skill or "Random"
|
||||||
|
return skill
|
||||||
|
end
|
||||||
|
|
||||||
--do -- Smoke
|
--do -- Smoke
|
||||||
--
|
--
|
||||||
----- Signal a flare at the position of the GROUP.
|
----- Signal a flare at the position of the GROUP.
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -10,11 +10,8 @@
|
|||||||
--
|
--
|
||||||
-- @module Wrapper.Static
|
-- @module Wrapper.Static
|
||||||
-- @image Wrapper_Static.JPG
|
-- @image Wrapper_Static.JPG
|
||||||
|
|
||||||
|
|
||||||
--- @type STATIC
|
--- @type STATIC
|
||||||
-- @extends Wrapper.Positionable#POSITIONABLE
|
-- @extends Wrapper.Positionable#POSITIONABLE
|
||||||
|
|
||||||
--- Wrapper class to handle Static objects.
|
--- Wrapper class to handle Static objects.
|
||||||
--
|
--
|
||||||
-- Note that Statics are almost the same as Units, but they don't have a controller.
|
-- Note that Statics are almost the same as Units, but they don't have a controller.
|
||||||
@@ -40,13 +37,10 @@
|
|||||||
--
|
--
|
||||||
-- * @{#STATIC.FindByName}(): Find a STATIC instance from the _DATABASE object using a DCS Static name.
|
-- * @{#STATIC.FindByName}(): Find a STATIC instance from the _DATABASE object using a DCS Static name.
|
||||||
--
|
--
|
||||||
-- IMPORTANT: ONE SHOULD NEVER SANATIZE these STATIC OBJECT REFERENCES! (make the STATIC object references nil).
|
-- IMPORTANT: ONE SHOULD NEVER SANITIZE these STATIC OBJECT REFERENCES! (make the STATIC object references nil).
|
||||||
--
|
--
|
||||||
-- @field #STATIC
|
-- @field #STATIC
|
||||||
STATIC = {
|
STATIC = { ClassName = "STATIC" }
|
||||||
ClassName = "STATIC",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
--- Register a static object.
|
--- Register a static object.
|
||||||
-- @param #STATIC self
|
-- @param #STATIC self
|
||||||
@@ -58,7 +52,6 @@ function STATIC:Register( StaticName )
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Finds a STATIC from the _DATABASE using a DCSStatic object.
|
--- Finds a STATIC from the _DATABASE using a DCSStatic object.
|
||||||
-- @param #STATIC self
|
-- @param #STATIC self
|
||||||
-- @param DCS#StaticObject DCSStatic An existing DCS Static object reference.
|
-- @param DCS#StaticObject DCSStatic An existing DCS Static object reference.
|
||||||
@@ -85,7 +78,7 @@ function STATIC:FindByName( StaticName, RaiseError )
|
|||||||
self.StaticName = StaticName
|
self.StaticName = StaticName
|
||||||
|
|
||||||
if StaticFound then
|
if StaticFound then
|
||||||
return StaticFound
|
return StaticFound
|
||||||
end
|
end
|
||||||
|
|
||||||
if RaiseError == nil or RaiseError == true then
|
if RaiseError == nil or RaiseError == true then
|
||||||
@@ -97,8 +90,9 @@ end
|
|||||||
|
|
||||||
--- Destroys the STATIC.
|
--- Destroys the STATIC.
|
||||||
-- @param #STATIC self
|
-- @param #STATIC self
|
||||||
-- @param #boolean GenerateEvent (Optional) true if you want to generate a crash or dead event for the static.
|
-- @param #boolean GenerateEvent (Optional) true to generate a crash or dead event, false to not generate any event. `nil` (default) creates a remove event.
|
||||||
-- @return #nil The DCS StaticObject is not existing or alive.
|
-- @return #nil The DCS StaticObject is not existing or alive.
|
||||||
|
--
|
||||||
-- @usage
|
-- @usage
|
||||||
-- -- Air static example: destroy the static Helicopter and generate a S_EVENT_CRASH.
|
-- -- Air static example: destroy the static Helicopter and generate a S_EVENT_CRASH.
|
||||||
-- Helicopter = STATIC:FindByName( "Helicopter" )
|
-- Helicopter = STATIC:FindByName( "Helicopter" )
|
||||||
@@ -117,7 +111,7 @@ end
|
|||||||
-- @usage
|
-- @usage
|
||||||
-- -- Destroy without event generation example.
|
-- -- Destroy without event generation example.
|
||||||
-- Ship = STATIC:FindByName( "Boat" )
|
-- Ship = STATIC:FindByName( "Boat" )
|
||||||
-- Ship:Destroy( false ) -- Don't generate an event upon destruction.
|
-- Ship:Destroy( false ) -- Don't generate any event upon destruction.
|
||||||
--
|
--
|
||||||
function STATIC:Destroy( GenerateEvent )
|
function STATIC:Destroy( GenerateEvent )
|
||||||
self:F2( self.ObjectName )
|
self:F2( self.ObjectName )
|
||||||
@@ -148,7 +142,6 @@ function STATIC:Destroy( GenerateEvent )
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Get DCS object of static of static.
|
--- Get DCS object of static of static.
|
||||||
-- @param #STATIC self
|
-- @param #STATIC self
|
||||||
-- @return DCS static object
|
-- @return DCS static object
|
||||||
@@ -180,7 +173,6 @@ function STATIC:GetUnits()
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Get threat level of static.
|
--- Get threat level of static.
|
||||||
-- @param #STATIC self
|
-- @param #STATIC self
|
||||||
-- @return #number Threat level 1.
|
-- @return #number Threat level 1.
|
||||||
@@ -194,15 +186,15 @@ end
|
|||||||
-- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static.
|
-- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static.
|
||||||
-- @param #number Heading The heading of the static respawn in degrees. Default is 0 deg.
|
-- @param #number Heading The heading of the static respawn in degrees. Default is 0 deg.
|
||||||
-- @param #number Delay Delay in seconds before the static is spawned.
|
-- @param #number Delay Delay in seconds before the static is spawned.
|
||||||
function STATIC:SpawnAt(Coordinate, Heading, Delay)
|
function STATIC:SpawnAt( Coordinate, Heading, Delay )
|
||||||
|
|
||||||
Heading=Heading or 0
|
Heading = Heading or 0
|
||||||
|
|
||||||
if Delay and Delay>0 then
|
if Delay and Delay > 0 then
|
||||||
SCHEDULER:New(nil, self.SpawnAt, {self, Coordinate, Heading}, Delay)
|
SCHEDULER:New( nil, self.SpawnAt, { self, Coordinate, Heading }, Delay )
|
||||||
else
|
else
|
||||||
|
|
||||||
local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName)
|
local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName )
|
||||||
|
|
||||||
SpawnStatic:SpawnFromPointVec2( Coordinate, Heading, self.StaticName )
|
SpawnStatic:SpawnFromPointVec2( Coordinate, Heading, self.StaticName )
|
||||||
|
|
||||||
@@ -211,47 +203,43 @@ function STATIC:SpawnAt(Coordinate, Heading, Delay)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Respawn the @{Wrapper.Unit} at the same location with the same properties.
|
--- Respawn the @{Wrapper.Unit} at the same location with the same properties.
|
||||||
-- This is useful to respawn a cargo after it has been destroyed.
|
-- This is useful to respawn a cargo after it has been destroyed.
|
||||||
-- @param #STATIC self
|
-- @param #STATIC self
|
||||||
-- @param DCS#country.id CountryID (Optional) The country ID used for spawning the new static. Default is same as currently.
|
-- @param DCS#country.id CountryID (Optional) The country ID used for spawning the new static. Default is same as currently.
|
||||||
-- @param #number Delay (Optional) Delay in seconds before static is respawned. Default now.
|
-- @param #number Delay (Optional) Delay in seconds before static is respawned. Default now.
|
||||||
function STATIC:ReSpawn(CountryID, Delay)
|
function STATIC:ReSpawn( CountryID, Delay )
|
||||||
|
|
||||||
if Delay and Delay>0 then
|
if Delay and Delay > 0 then
|
||||||
SCHEDULER:New(nil, self.ReSpawn, {self, CountryID}, Delay)
|
SCHEDULER:New( nil, self.ReSpawn, { self, CountryID }, Delay )
|
||||||
else
|
else
|
||||||
|
|
||||||
CountryID=CountryID or self:GetCountry()
|
CountryID = CountryID or self:GetCountry()
|
||||||
|
|
||||||
local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName, CountryID)
|
local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName, CountryID )
|
||||||
|
|
||||||
SpawnStatic:Spawn(nil, self.StaticName)
|
SpawnStatic:Spawn( nil, self.StaticName )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Respawn the @{Wrapper.Unit} at a defined Coordinate with an optional heading.
|
--- Respawn the @{Wrapper.Unit} at a defined Coordinate with an optional heading.
|
||||||
-- @param #STATIC self
|
-- @param #STATIC self
|
||||||
-- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static.
|
-- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static.
|
||||||
-- @param #number Heading (Optional) The heading of the static respawn in degrees. Default the current heading.
|
-- @param #number Heading (Optional) The heading of the static respawn in degrees. Default is the current heading.
|
||||||
-- @param #number Delay (Optional) Delay in seconds before static is respawned. Default now.
|
-- @param #number Delay (Optional) Delay in seconds before static is respawned. Default is now.
|
||||||
function STATIC:ReSpawnAt(Coordinate, Heading, Delay)
|
function STATIC:ReSpawnAt( Coordinate, Heading, Delay )
|
||||||
|
|
||||||
--Heading=Heading or 0
|
-- Heading=Heading or 0
|
||||||
|
|
||||||
if Delay and Delay>0 then
|
if Delay and Delay > 0 then
|
||||||
SCHEDULER:New(nil, self.ReSpawnAt, {self, Coordinate, Heading}, Delay)
|
SCHEDULER:New( nil, self.ReSpawnAt, { self, Coordinate, Heading }, Delay )
|
||||||
else
|
else
|
||||||
|
local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName, self:GetCountry() )
|
||||||
|
|
||||||
local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName, self:GetCountry())
|
SpawnStatic:SpawnFromCoordinate( Coordinate, Heading, self.StaticName )
|
||||||
|
|
||||||
SpawnStatic:SpawnFromCoordinate(Coordinate, Heading, self.StaticName)
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
--
|
--
|
||||||
-- ### Author: **FlightControl**
|
-- ### Author: **FlightControl**
|
||||||
--
|
--
|
||||||
-- ### Contributions:
|
-- ### Contributions: **funkyfranky**
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
@@ -22,6 +22,8 @@
|
|||||||
|
|
||||||
|
|
||||||
--- @type UNIT
|
--- @type UNIT
|
||||||
|
-- @field #string ClassName Name of the class.
|
||||||
|
-- @field #string UnitName Name of the unit.
|
||||||
-- @extends Wrapper.Controllable#CONTROLLABLE
|
-- @extends Wrapper.Controllable#CONTROLLABLE
|
||||||
|
|
||||||
--- For each DCS Unit object alive within a running mission, a UNIT wrapper object (instance) will be created within the _@{DATABASE} object.
|
--- For each DCS Unit object alive within a running mission, a UNIT wrapper object (instance) will be created within the _@{DATABASE} object.
|
||||||
@@ -61,7 +63,7 @@
|
|||||||
--
|
--
|
||||||
-- The UNIT class provides methods to obtain the current point or position of the DCS Unit.
|
-- The UNIT class provides methods to obtain the current point or position of the DCS Unit.
|
||||||
-- The @{#UNIT.GetPointVec2}(), @{#UNIT.GetVec3}() will obtain the current **location** of the DCS Unit in a Vec2 (2D) or a **point** in a Vec3 (3D) vector respectively.
|
-- The @{#UNIT.GetPointVec2}(), @{#UNIT.GetVec3}() will obtain the current **location** of the DCS Unit in a Vec2 (2D) or a **point** in a Vec3 (3D) vector respectively.
|
||||||
-- If you want to obtain the complete **3D position** including ori<EFBFBD>ntation and direction vectors, consult the @{#UNIT.GetPositionVec3}() method respectively.
|
-- If you want to obtain the complete **3D position** including orientation and direction vectors, consult the @{#UNIT.GetPositionVec3}() method respectively.
|
||||||
--
|
--
|
||||||
-- ## Test if alive
|
-- ## Test if alive
|
||||||
--
|
--
|
||||||
@@ -87,6 +89,7 @@
|
|||||||
-- @field #UNIT UNIT
|
-- @field #UNIT UNIT
|
||||||
UNIT = {
|
UNIT = {
|
||||||
ClassName="UNIT",
|
ClassName="UNIT",
|
||||||
|
UnitName=nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -103,12 +106,18 @@ UNIT = {
|
|||||||
--- Create a new UNIT from DCSUnit.
|
--- Create a new UNIT from DCSUnit.
|
||||||
-- @param #UNIT self
|
-- @param #UNIT self
|
||||||
-- @param #string UnitName The name of the DCS unit.
|
-- @param #string UnitName The name of the DCS unit.
|
||||||
-- @return #UNIT
|
-- @return #UNIT self
|
||||||
function UNIT:Register( UnitName )
|
function UNIT:Register( UnitName )
|
||||||
|
|
||||||
|
-- Inherit CONTROLLABLE.
|
||||||
local self = BASE:Inherit( self, CONTROLLABLE:New( UnitName ) )
|
local self = BASE:Inherit( self, CONTROLLABLE:New( UnitName ) )
|
||||||
|
|
||||||
|
-- Set unit name.
|
||||||
self.UnitName = UnitName
|
self.UnitName = UnitName
|
||||||
|
|
||||||
|
-- Set event prio.
|
||||||
self:SetEventPriority( 3 )
|
self:SetEventPriority( 3 )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -145,8 +154,8 @@ function UNIT:Name()
|
|||||||
return self.UnitName
|
return self.UnitName
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get the DCS unit object.
|
||||||
--- @param #UNIT self
|
-- @param #UNIT self
|
||||||
-- @return DCS#Unit
|
-- @return DCS#Unit
|
||||||
function UNIT:GetDCSObject()
|
function UNIT:GetDCSObject()
|
||||||
|
|
||||||
@@ -159,9 +168,6 @@ function UNIT:GetDCSObject()
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- Respawn the @{Wrapper.Unit} using a (tweaked) template of the parent Group.
|
--- Respawn the @{Wrapper.Unit} using a (tweaked) template of the parent Group.
|
||||||
--
|
--
|
||||||
-- This function will:
|
-- This function will:
|
||||||
@@ -254,8 +260,6 @@ function UNIT:ReSpawnAt( Coordinate, Heading )
|
|||||||
_DATABASE:Spawn( SpawnGroupTemplate )
|
_DATABASE:Spawn( SpawnGroupTemplate )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- Returns if the unit is activated.
|
--- Returns if the unit is activated.
|
||||||
-- @param #UNIT self
|
-- @param #UNIT self
|
||||||
-- @return #boolean `true` if Unit is activated. `nil` The DCS Unit is not existing or alive.
|
-- @return #boolean `true` if Unit is activated. `nil` The DCS Unit is not existing or alive.
|
||||||
@@ -292,8 +296,6 @@ function UNIT:IsAlive()
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- Returns the Unit's callsign - the localized string.
|
--- Returns the Unit's callsign - the localized string.
|
||||||
-- @param #UNIT self
|
-- @param #UNIT self
|
||||||
-- @return #string The Callsign of the Unit.
|
-- @return #string The Callsign of the Unit.
|
||||||
@@ -373,6 +375,32 @@ function UNIT:GetPlayerName()
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Checks is the unit is a *Player* or *Client* slot.
|
||||||
|
-- @param #UNIT self
|
||||||
|
-- @return #boolean If true, unit is a player or client aircraft
|
||||||
|
function UNIT:IsClient()
|
||||||
|
|
||||||
|
if _DATABASE.CLIENTS[self.UnitName] then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the CLIENT of the unit
|
||||||
|
-- @param #UNIT self
|
||||||
|
-- @return Wrapper.Client#CLIENT
|
||||||
|
function UNIT:GetClient()
|
||||||
|
|
||||||
|
local client=_DATABASE.CLIENTS[self.UnitName]
|
||||||
|
|
||||||
|
if client then
|
||||||
|
return client
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
--- Returns the unit's number in the group.
|
--- Returns the unit's number in the group.
|
||||||
-- The number is the same number the unit has in ME.
|
-- The number is the same number the unit has in ME.
|
||||||
-- It may not be changed during the mission.
|
-- It may not be changed during the mission.
|
||||||
@@ -407,7 +435,7 @@ function UNIT:GetSpeedMax()
|
|||||||
return SpeedMax*3.6
|
return SpeedMax*3.6
|
||||||
end
|
end
|
||||||
|
|
||||||
return nil
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns the unit's max range in meters derived from the DCS descriptors.
|
--- Returns the unit's max range in meters derived from the DCS descriptors.
|
||||||
@@ -492,8 +520,7 @@ end
|
|||||||
|
|
||||||
--- Returns the unit's group if it exist and nil otherwise.
|
--- Returns the unit's group if it exist and nil otherwise.
|
||||||
-- @param Wrapper.Unit#UNIT self
|
-- @param Wrapper.Unit#UNIT self
|
||||||
-- @return Wrapper.Group#GROUP The Group of the Unit.
|
-- @return Wrapper.Group#GROUP The Group of the Unit or `nil` if the unit does not exist.
|
||||||
-- @return #nil The DCS Unit is not existing or alive.
|
|
||||||
function UNIT:GetGroup()
|
function UNIT:GetGroup()
|
||||||
self:F2( self.UnitName )
|
self:F2( self.UnitName )
|
||||||
|
|
||||||
@@ -546,6 +573,18 @@ function UNIT:GetAmmo()
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Sets the Unit's Internal Cargo Mass, in kg
|
||||||
|
-- @param #UNIT self
|
||||||
|
-- @param #number mass to set cargo to
|
||||||
|
-- @return #UNIT self
|
||||||
|
function UNIT:SetUnitInternalCargo(mass)
|
||||||
|
local DCSUnit = self:GetDCSObject()
|
||||||
|
if DCSUnit then
|
||||||
|
trigger.action.setUnitInternalCargo(DCSUnit:getName(), mass)
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Get the number of ammunition and in particular the number of shells, rockets, bombs and missiles a unit currently has.
|
--- Get the number of ammunition and in particular the number of shells, rockets, bombs and missiles a unit currently has.
|
||||||
-- @param #UNIT self
|
-- @param #UNIT self
|
||||||
-- @return #number Total amount of ammo the unit has left. This is the sum of shells, rockets, bombs and missiles.
|
-- @return #number Total amount of ammo the unit has left. This is the sum of shells, rockets, bombs and missiles.
|
||||||
@@ -580,8 +619,8 @@ function UNIT:GetAmmunition()
|
|||||||
-- Type name of current weapon.
|
-- Type name of current weapon.
|
||||||
local Tammo=ammotable[w]["desc"]["typeName"]
|
local Tammo=ammotable[w]["desc"]["typeName"]
|
||||||
|
|
||||||
local _weaponString = UTILS.Split(Tammo,"%.")
|
--local _weaponString = UTILS.Split(Tammo,"%.")
|
||||||
local _weaponName = _weaponString[#_weaponString]
|
--local _weaponName = _weaponString[#_weaponString]
|
||||||
|
|
||||||
-- Get the weapon category: shell=0, missile=1, rocket=2, bomb=3
|
-- Get the weapon category: shell=0, missile=1, rocket=2, bomb=3
|
||||||
local Category=ammotable[w].desc.category
|
local Category=ammotable[w].desc.category
|
||||||
@@ -610,7 +649,8 @@ function UNIT:GetAmmunition()
|
|||||||
|
|
||||||
elseif Category==Weapon.Category.MISSILE then
|
elseif Category==Weapon.Category.MISSILE then
|
||||||
|
|
||||||
-- Add up all cruise missiles (category 5)
|
|
||||||
|
-- Add up all missiles (category 5)
|
||||||
if MissileCategory==Weapon.MissileCategory.AAM then
|
if MissileCategory==Weapon.MissileCategory.AAM then
|
||||||
nmissiles=nmissiles+Nammo
|
nmissiles=nmissiles+Nammo
|
||||||
elseif MissileCategory==Weapon.MissileCategory.ANTI_SHIP then
|
elseif MissileCategory==Weapon.MissileCategory.ANTI_SHIP then
|
||||||
@@ -619,6 +659,10 @@ function UNIT:GetAmmunition()
|
|||||||
nmissiles=nmissiles+Nammo
|
nmissiles=nmissiles+Nammo
|
||||||
elseif MissileCategory==Weapon.MissileCategory.OTHER then
|
elseif MissileCategory==Weapon.MissileCategory.OTHER then
|
||||||
nmissiles=nmissiles+Nammo
|
nmissiles=nmissiles+Nammo
|
||||||
|
elseif MissileCategory==Weapon.MissileCategory.SAM then
|
||||||
|
nmissiles=nmissiles+Nammo
|
||||||
|
elseif MissileCategory==Weapon.MissileCategory.CRUISE then
|
||||||
|
nmissiles=nmissiles+Nammo
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -727,6 +771,7 @@ function UNIT:GetFuel()
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Returns a list of one @{Wrapper.Unit}.
|
--- Returns a list of one @{Wrapper.Unit}.
|
||||||
-- @param #UNIT self
|
-- @param #UNIT self
|
||||||
-- @return #list<Wrapper.Unit#UNIT> A list of one @{Wrapper.Unit}.
|
-- @return #list<Wrapper.Unit#UNIT> A list of one @{Wrapper.Unit}.
|
||||||
@@ -909,7 +954,6 @@ end
|
|||||||
-- @return #string Some text.
|
-- @return #string Some text.
|
||||||
function UNIT:GetThreatLevel()
|
function UNIT:GetThreatLevel()
|
||||||
|
|
||||||
|
|
||||||
local ThreatLevel = 0
|
local ThreatLevel = 0
|
||||||
local ThreatText = ""
|
local ThreatText = ""
|
||||||
|
|
||||||
@@ -935,7 +979,6 @@ function UNIT:GetThreatLevel()
|
|||||||
"LR SAMs"
|
"LR SAMs"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if Attributes["LR SAM"] then ThreatLevel = 10
|
if Attributes["LR SAM"] then ThreatLevel = 10
|
||||||
elseif Attributes["MR SAM"] then ThreatLevel = 9
|
elseif Attributes["MR SAM"] then ThreatLevel = 9
|
||||||
elseif Attributes["SR SAM"] and
|
elseif Attributes["SR SAM"] and
|
||||||
@@ -971,7 +1014,6 @@ function UNIT:GetThreatLevel()
|
|||||||
"Fighter"
|
"Fighter"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if Attributes["Fighters"] then ThreatLevel = 10
|
if Attributes["Fighters"] then ThreatLevel = 10
|
||||||
elseif Attributes["Multirole fighters"] then ThreatLevel = 9
|
elseif Attributes["Multirole fighters"] then ThreatLevel = 9
|
||||||
elseif Attributes["Battleplanes"] then ThreatLevel = 8
|
elseif Attributes["Battleplanes"] then ThreatLevel = 8
|
||||||
@@ -1089,12 +1131,6 @@ function UNIT:OtherUnitInRadius( AwaitUnit, Radius )
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- Returns if the unit is a friendly unit.
|
--- Returns if the unit is a friendly unit.
|
||||||
-- @param #UNIT self
|
-- @param #UNIT self
|
||||||
-- @return #boolean IsFriendly evaluation result.
|
-- @return #boolean IsFriendly evaluation result.
|
||||||
@@ -1140,8 +1176,9 @@ end
|
|||||||
|
|
||||||
--- Returns true if the UNIT is in the air.
|
--- Returns true if the UNIT is in the air.
|
||||||
-- @param #UNIT self
|
-- @param #UNIT self
|
||||||
|
-- @param #boolean NoHeloCheck If true, no additonal checks for helos are performed.
|
||||||
-- @return #boolean Return true if in the air or #nil if the UNIT is not existing or alive.
|
-- @return #boolean Return true if in the air or #nil if the UNIT is not existing or alive.
|
||||||
function UNIT:InAir()
|
function UNIT:InAir(NoHeloCheck)
|
||||||
self:F2( self.UnitName )
|
self:F2( self.UnitName )
|
||||||
|
|
||||||
-- Get DCS unit object.
|
-- Get DCS unit object.
|
||||||
@@ -1158,7 +1195,7 @@ function UNIT:InAir()
|
|||||||
-- If DCS says that it is in air, check if this is really the case, since we might have landed on a building where inAir()=true but actually is not.
|
-- If DCS says that it is in air, check if this is really the case, since we might have landed on a building where inAir()=true but actually is not.
|
||||||
-- This is a workaround since DCS currently does not acknoledge that helos land on buildings.
|
-- This is a workaround since DCS currently does not acknoledge that helos land on buildings.
|
||||||
-- Note however, that the velocity check will fail if the ground is moving, e.g. on an aircraft carrier!
|
-- Note however, that the velocity check will fail if the ground is moving, e.g. on an aircraft carrier!
|
||||||
if UnitInAir==true and UnitCategory == Unit.Category.HELICOPTER then
|
if UnitInAir==true and UnitCategory == Unit.Category.HELICOPTER and (not NoHeloCheck) then
|
||||||
local VelocityVec3 = DCSUnit:getVelocity()
|
local VelocityVec3 = DCSUnit:getVelocity()
|
||||||
local Velocity = UTILS.VecNorm(VelocityVec3)
|
local Velocity = UTILS.VecNorm(VelocityVec3)
|
||||||
local Coordinate = DCSUnit:getPoint()
|
local Coordinate = DCSUnit:getPoint()
|
||||||
@@ -1180,23 +1217,26 @@ do -- Event Handling
|
|||||||
|
|
||||||
--- Subscribe to a DCS Event.
|
--- Subscribe to a DCS Event.
|
||||||
-- @param #UNIT self
|
-- @param #UNIT self
|
||||||
-- @param Core.Event#EVENTS Event
|
-- @param Core.Event#EVENTS EventID Event ID.
|
||||||
-- @param #function EventFunction (optional) The function to be called when the event occurs for the unit.
|
-- @param #function EventFunction (Optional) The function to be called when the event occurs for the unit.
|
||||||
-- @return #UNIT
|
-- @return #UNIT self
|
||||||
function UNIT:HandleEvent( Event, EventFunction )
|
function UNIT:HandleEvent(EventID, EventFunction)
|
||||||
|
|
||||||
self:EventDispatcher():OnEventForUnit( self:GetName(), EventFunction, self, Event )
|
self:EventDispatcher():OnEventForUnit(self:GetName(), EventFunction, self, EventID)
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- UnSubscribe to a DCS event.
|
--- UnSubscribe to a DCS event.
|
||||||
-- @param #UNIT self
|
-- @param #UNIT self
|
||||||
-- @param Core.Event#EVENTS Event
|
-- @param Core.Event#EVENTS EventID Event ID.
|
||||||
-- @return #UNIT
|
-- @return #UNIT self
|
||||||
function UNIT:UnHandleEvent( Event )
|
function UNIT:UnHandleEvent(EventID)
|
||||||
|
|
||||||
self:EventDispatcher():RemoveForUnit( self:GetName(), self, Event )
|
--self:EventDispatcher():RemoveForUnit( self:GetName(), self, EventID )
|
||||||
|
|
||||||
|
-- Fixes issue #1365 https://github.com/FlightControl-Master/MOOSE/issues/1365
|
||||||
|
self:EventDispatcher():RemoveEvent(self, EventID)
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@@ -1355,3 +1395,33 @@ function UNIT:GetTemplateFuel()
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- GROUND - Switch on/off radar emissions of a unit.
|
||||||
|
-- @param #UNIT self
|
||||||
|
-- @param #boolean switch If true, emission is enabled. If false, emission is disabled.
|
||||||
|
-- @return #UNIT self
|
||||||
|
function UNIT:EnableEmission(switch)
|
||||||
|
self:F2( self.UnitName )
|
||||||
|
|
||||||
|
local switch = switch or false
|
||||||
|
|
||||||
|
local DCSUnit = self:GetDCSObject()
|
||||||
|
|
||||||
|
if DCSUnit then
|
||||||
|
|
||||||
|
DCSUnit:enableEmission(switch)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get skill from Unit.
|
||||||
|
-- @param #UNIT self
|
||||||
|
-- @return #string Skill String of skill name.
|
||||||
|
function UNIT:GetSkill()
|
||||||
|
self:F2( self.UnitName )
|
||||||
|
local name = self.UnitName
|
||||||
|
local skill = _DATABASE.Templates.Units[name].Template.skill or "Random"
|
||||||
|
return skill
|
||||||
|
end
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
|
Utilities/Enums.lua
|
||||||
Utilities/Routines.lua
|
Utilities/Routines.lua
|
||||||
Utilities/Utils.lua
|
Utilities/Utils.lua
|
||||||
Utilities/Enums.lua
|
Utilities/Enums.lua
|
||||||
Utilities/Profiler.lua
|
Utilities/Profiler.lua
|
||||||
|
Utilities/Templates.lua
|
||||||
|
Utilities/STTS.lua
|
||||||
|
|
||||||
Core/Base.lua
|
Core/Base.lua
|
||||||
|
Core/Beacon.lua
|
||||||
Core/UserFlag.lua
|
Core/UserFlag.lua
|
||||||
Core/UserSound.lua
|
|
||||||
Core/Report.lua
|
Core/Report.lua
|
||||||
Core/Scheduler.lua
|
Core/Scheduler.lua
|
||||||
Core/ScheduleDispatcher.lua
|
Core/ScheduleDispatcher.lua
|
||||||
@@ -13,13 +16,13 @@ Core/Event.lua
|
|||||||
Core/Settings.lua
|
Core/Settings.lua
|
||||||
Core/Menu.lua
|
Core/Menu.lua
|
||||||
Core/Zone.lua
|
Core/Zone.lua
|
||||||
|
Core/Zone_Detection.lua
|
||||||
Core/Database.lua
|
Core/Database.lua
|
||||||
Core/Set.lua
|
Core/Set.lua
|
||||||
Core/Point.lua
|
Core/Point.lua
|
||||||
Core/Velocity.lua
|
Core/Velocity.lua
|
||||||
Core/Message.lua
|
Core/Message.lua
|
||||||
Core/Fsm.lua
|
Core/Fsm.lua
|
||||||
Core/Radio.lua
|
|
||||||
Core/Spawn.lua
|
Core/Spawn.lua
|
||||||
Core/SpawnStatic.lua
|
Core/SpawnStatic.lua
|
||||||
Core/Timer.lua
|
Core/Timer.lua
|
||||||
@@ -52,6 +55,7 @@ Functional/Escort.lua
|
|||||||
Functional/MissileTrainer.lua
|
Functional/MissileTrainer.lua
|
||||||
Functional/ATC_Ground.lua
|
Functional/ATC_Ground.lua
|
||||||
Functional/Detection.lua
|
Functional/Detection.lua
|
||||||
|
Functional/DetectionZones.lua
|
||||||
Functional/Designate.lua
|
Functional/Designate.lua
|
||||||
Functional/RAT.lua
|
Functional/RAT.lua
|
||||||
Functional/Range.lua
|
Functional/Range.lua
|
||||||
@@ -63,45 +67,60 @@ Functional/Suppression.lua
|
|||||||
Functional/PseudoATC.lua
|
Functional/PseudoATC.lua
|
||||||
Functional/Warehouse.lua
|
Functional/Warehouse.lua
|
||||||
Functional/Fox.lua
|
Functional/Fox.lua
|
||||||
|
Functional/Mantis.lua
|
||||||
|
Functional/Shorad.lua
|
||||||
|
|
||||||
Ops/Airboss.lua
|
Ops/Airboss.lua
|
||||||
Ops/RecoveryTanker.lua
|
Ops/RecoveryTanker.lua
|
||||||
Ops/RescueHelo.lua
|
Ops/RescueHelo.lua
|
||||||
Ops/ATIS.lua
|
Ops/ATIS.lua
|
||||||
|
Ops/CTLD.lua
|
||||||
|
Ops/CSAR.lua
|
||||||
|
|
||||||
AI/AI_Balancer.lua
|
AI/AI_Balancer.lua
|
||||||
AI/AI_Air.lua
|
AI/AI_Air.lua
|
||||||
AI/AI_A2A.lua
|
AI/AI_Air_Patrol.lua
|
||||||
|
AI/AI_Air_Engage.lua
|
||||||
AI/AI_A2A_Patrol.lua
|
AI/AI_A2A_Patrol.lua
|
||||||
AI/AI_A2A_Cap.lua
|
AI/AI_A2A_Cap.lua
|
||||||
AI/AI_A2A_Gci.lua
|
AI/AI_A2A_Gci.lua
|
||||||
AI/AI_A2A_Dispatcher.lua
|
AI/AI_A2A_Dispatcher.lua
|
||||||
AI/AI_A2G.lua
|
|
||||||
AI/AI_A2G_Engage.lua
|
|
||||||
AI/AI_A2G_BAI.lua
|
AI/AI_A2G_BAI.lua
|
||||||
AI/AI_A2G_CAS.lua
|
AI/AI_A2G_CAS.lua
|
||||||
AI/AI_A2G_SEAD.lua
|
AI/AI_A2G_SEAD.lua
|
||||||
AI/AI_A2G_Patrol.lua
|
|
||||||
AI/AI_A2G_Dispatcher.lua
|
AI/AI_A2G_Dispatcher.lua
|
||||||
AI/AI_Patrol.lua
|
AI/AI_Patrol.lua
|
||||||
AI/AI_Cap.lua
|
AI/AI_CAP.lua
|
||||||
AI/AI_Cas.lua
|
AI/AI_CAS.lua
|
||||||
AI/AI_Bai.lua
|
AI/AI_BAI.lua
|
||||||
AI/AI_Formation.lua
|
AI/AI_Formation.lua
|
||||||
|
AI/AI_Escort.lua
|
||||||
|
AI/AI_Escort_Request.lua
|
||||||
|
AI/AI_Escort_Dispatcher.lua
|
||||||
|
AI/AI_Escort_Dispatcher_Request.lua
|
||||||
AI/AI_Cargo.lua
|
AI/AI_Cargo.lua
|
||||||
AI/AI_Cargo_APC.lua
|
AI/AI_Cargo_APC.lua
|
||||||
AI/AI_Cargo_Helicopter.lua
|
AI/AI_Cargo_Helicopter.lua
|
||||||
AI/AI_Cargo_Airplane.lua
|
AI/AI_Cargo_Airplane.lua
|
||||||
|
AI/AI_Cargo_Ship.lua
|
||||||
AI/AI_Cargo_Dispatcher.lua
|
AI/AI_Cargo_Dispatcher.lua
|
||||||
AI/AI_Cargo_Dispatcher_APC.lua
|
AI/AI_Cargo_Dispatcher_APC.lua
|
||||||
AI/AI_Cargo_Dispatcher_Helicopter.lua
|
AI/AI_Cargo_Dispatcher_Helicopter.lua
|
||||||
AI/AI_Cargo_Dispatcher_Airplane.lua
|
AI/AI_Cargo_Dispatcher_Airplane.lua
|
||||||
|
AI/AI_Cargo_Dispatcher_Ship.lua
|
||||||
|
|
||||||
Actions/Act_Assign.lua
|
Actions/Act_Assign.lua
|
||||||
Actions/Act_Route.lua
|
Actions/Act_Route.lua
|
||||||
Actions/Act_Account.lua
|
Actions/Act_Account.lua
|
||||||
Actions/Act_Assist.lua
|
Actions/Act_Assist.lua
|
||||||
|
|
||||||
|
Sound/UserSound.lua
|
||||||
|
Sound/SoundOutput.lua
|
||||||
|
Sound/Radio.lua
|
||||||
|
Sound/RadioQueue.lua
|
||||||
|
Sound/RadioSpeech.lua
|
||||||
|
Sound/SRS.lua
|
||||||
|
|
||||||
Tasking/CommandCenter.lua
|
Tasking/CommandCenter.lua
|
||||||
Tasking/Mission.lua
|
Tasking/Mission.lua
|
||||||
Tasking/Task.lua
|
Tasking/Task.lua
|
||||||
@@ -112,10 +131,11 @@ Tasking/Task_A2G_Dispatcher.lua
|
|||||||
Tasking/Task_A2G.lua
|
Tasking/Task_A2G.lua
|
||||||
Tasking/Task_A2A_Dispatcher.lua
|
Tasking/Task_A2A_Dispatcher.lua
|
||||||
Tasking/Task_A2A.lua
|
Tasking/Task_A2A.lua
|
||||||
Tasking/Task_Cargo.lua
|
Tasking/Task_CARGO.lua
|
||||||
Tasking/Task_Cargo_Transport.lua
|
Tasking/Task_Cargo_Transport.lua
|
||||||
Tasking/Task_Cargo_CSAR.lua
|
Tasking/Task_Cargo_CSAR.lua
|
||||||
Tasking/Task_Cargo_Dispatcher.lua
|
Tasking/Task_Cargo_Dispatcher.lua
|
||||||
Tasking/TaskZoneCapture.lua
|
Tasking/Task_Capture_Zone.lua
|
||||||
|
Tasking/Task_Capture_Dispatcher.lua
|
||||||
|
|
||||||
Globals.lua
|
Globals.lua
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
[](https://ci.appveyor.com/project/Applevangelist/MOOSE)
|
||||||
|
|
||||||
# MOOSE framework
|
# MOOSE framework
|
||||||
|
|
||||||
MOOSE is a **M**ission **O**bject **O**riented **S**cripting **E**nvironment, and is meant for mission designers in DCS World.
|
MOOSE is a **M**ission **O**bject **O**riented **S**cripting **E**nvironment, and is meant for mission designers in DCS World.
|
||||||
@@ -28,7 +30,7 @@ This repository contains the source lua code of the MOOSE framework.
|
|||||||
|
|
||||||
### [MOOSE_INCLUDE](https://github.com/FlightControl-Master/MOOSE_INCLUDE) - For use and generated
|
### [MOOSE_INCLUDE](https://github.com/FlightControl-Master/MOOSE_INCLUDE) - For use and generated
|
||||||
|
|
||||||
This repository contains the Moose.lua file to be included within your missions.
|
This repository contains the Moose.lua file to be included within your missions. Note that the Moose\_.lua is technically the same as Moose.lua, but without any commentary or unnecessary whitespace in it. You only need to load **one** of those at the beginning of your mission.
|
||||||
|
|
||||||
|
|
||||||
### [MOOSE_DOCS](https://github.com/FlightControl-Master/MOOSE_DOCS) - Not for use
|
### [MOOSE_DOCS](https://github.com/FlightControl-Master/MOOSE_DOCS) - Not for use
|
||||||
@@ -51,8 +53,6 @@ This repository contains all the demonstration missions in packed format (*.miz)
|
|||||||
This repository contains all the demonstration missions in unpacked format. That means that there is no .miz file included, but all the .miz contents are unpacked.
|
This repository contains all the demonstration missions in unpacked format. That means that there is no .miz file included, but all the .miz contents are unpacked.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [MOOSE Web Site](https://flightcontrol-master.github.io/MOOSE_DOCS/)
|
## [MOOSE Web Site](https://flightcontrol-master.github.io/MOOSE_DOCS/)
|
||||||
|
|
||||||
Documentation on the MOOSE class hierarchy, usage guides and background information can be found here for normal users, beta testers and contributors.
|
Documentation on the MOOSE class hierarchy, usage guides and background information can be found here for normal users, beta testers and contributors.
|
||||||
@@ -74,6 +74,6 @@ MOOSE has a living (chat and video) community of users, beta testers and contrib
|
|||||||
|
|
||||||
|
|
||||||
Kind regards,
|
Kind regards,
|
||||||
FlightControl (FC)
|
The Moose Team
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user