mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-08-15 10:47:21 +00:00
Compare commits
256 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2d7e7d55a9 | ||
|
|
6e37300d9b | ||
|
|
85fef96d00 | ||
|
|
47d814e409 | ||
|
|
41b01a508d | ||
|
|
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'))
|
||||
|
||||
install:
|
||||
- cmd:
|
||||
# Outcomment if lua environment invalidates and needs to be reinstalled, otherwise all will run from the cache.
|
||||
# - call choco install 7zip.commandline
|
||||
# - call choco install lua51
|
||||
# - call choco install luarocks
|
||||
# - call refreshenv
|
||||
# - call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"
|
||||
# - cmd: PATH = %PATH%;C:\ProgramData\chocolatey\lib\luarocks\luarocks-2.4.3-win32\systree\bin
|
||||
# - cmd: set LUA_PATH = %LUA_PATH%;C:\ProgramData\chocolatey\lib\luarocks\luarocks-2.4.3-win32\systree\share\lua\5.1\?.lua;C:\ProgramData\chocolatey\lib\luarocks\luarocks-2.4.3-win32\systree\share\lua\5.1\?\init.lua
|
||||
# - cmd: set LUA_CPATH = %LUA_CPATH%;C:\ProgramData\chocolatey\lib\luarocks\luarocks-2.4.3-win32\systree\lib\lua\5.1\?.dll
|
||||
# - call luarocks install luasrcdiet
|
||||
# - call luarocks install checks
|
||||
# - call luarocks install luadocumentor
|
||||
# - call luarocks install luacheck
|
||||
call choco install 7zip.commandline
|
||||
call choco install lua51
|
||||
call choco install luarocks
|
||||
call refreshenv
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"
|
||||
cmd: PATH = %PATH%;C:\ProgramData\chocolatey\lib\luarocks\luarocks-2.4.3-win32\systree\bin
|
||||
cmd: set LUA_PATH = %LUA_PATH%;C:\ProgramData\chocolatey\lib\luarocks\luarocks-2.4.3-win32\systree\share\lua\5.1\?.lua;C:\ProgramData\chocolatey\lib\luarocks\luarocks-2.4.3-win32\systree\share\lua\5.1\?\init.lua
|
||||
cmd: set LUA_CPATH = %LUA_CPATH%;C:\ProgramData\chocolatey\lib\luarocks\luarocks-2.4.3-win32\systree\lib\lua\5.1\?.dll
|
||||
call luarocks install luasrcdiet
|
||||
call luarocks install checks
|
||||
call luarocks install luadocumentor
|
||||
call luarocks install luacheck
|
||||
|
||||
|
||||
#cache:
|
||||
# - C:\ProgramData\chocolatey\lib
|
||||
# - C:\ProgramData\chocolatey\bin
|
||||
cache:
|
||||
C:\ProgramData\chocolatey\lib
|
||||
C:\ProgramData\chocolatey\bin
|
||||
|
||||
|
||||
|
||||
@@ -51,8 +52,9 @@ build_script:
|
||||
- ps: |
|
||||
if( $env:appveyor_repo_branch -eq 'master' -or $env:appveyor_repo_branch -eq 'develop' )
|
||||
{
|
||||
echo "Hello World!"
|
||||
$apiUrl = 'https://ci.appveyor.com/api'
|
||||
$token = 'qts80b5kpq0ooj4x6vvw'
|
||||
$token = 'v2.6hcv3ige78kg3yvg4ge8'
|
||||
$headers = @{
|
||||
"Authorization" = "Bearer $token"
|
||||
"Content-type" = "application/json"
|
||||
@@ -65,7 +67,7 @@ build_script:
|
||||
if( $env:appveyor_repo_branch -eq 'master' -or $env:appveyor_repo_branch -eq 'develop' )
|
||||
{
|
||||
$apiUrl = 'https://ci.appveyor.com/api'
|
||||
$token = 'qts80b5kpq0ooj4x6vvw'
|
||||
$token = 'v2.6hcv3ige78kg3yvg4ge8'
|
||||
$headers = @{
|
||||
"Authorization" = "Bearer $token"
|
||||
"Content-type" = "application/json"
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -18,6 +18,8 @@ local.properties
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
||||
# AppVeyor
|
||||
.appveyor/
|
||||
|
||||
# CDT-specific
|
||||
.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.
|
||||
}
|
||||
@@ -143,7 +143,7 @@ end
|
||||
-- @return #AI_A2A_CAP
|
||||
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
|
||||
|
||||
|
||||
@@ -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.
|
||||
-- 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
|
||||
-- whole pattern lies within the patrol zone.
|
||||
@@ -2774,7 +2776,7 @@ do -- AI_A2A_DISPATCHER
|
||||
-- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection )
|
||||
--
|
||||
-- -- 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 )
|
||||
|
||||
@@ -2817,7 +2819,7 @@ do -- AI_A2A_DISPATCHER
|
||||
-- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection )
|
||||
--
|
||||
-- -- 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.
|
||||
-- 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.
|
||||
function AI_A2A_DISPATCHER:GetSquadronFromDefender( Defender )
|
||||
self.Defenders = self.Defenders or {}
|
||||
local DefenderName = Defender:GetName()
|
||||
self:F( { DefenderName = DefenderName } )
|
||||
return self.Defenders[ DefenderName ]
|
||||
if Defender ~= nil then
|
||||
local DefenderName = Defender:GetName()
|
||||
self:F( { DefenderName = DefenderName } )
|
||||
return self.Defenders[ DefenderName ]
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -431,7 +431,7 @@ do -- AI_A2G_DISPATCHER
|
||||
--
|
||||
-- ### 2.2. The **Defense Reactivity**.
|
||||
--
|
||||
-- There are 5 levels that can be configured to tweak the defense reactivity. As explained above, the threat to a defense coordinate is
|
||||
-- There are three levels that can be configured to tweak the defense reactivity. As explained above, the threat to a defense coordinate is
|
||||
-- also determined by the distance of the enemy ground target to the defense coordinate.
|
||||
-- If you want to have a **low** defense reactivity, that is, the probability that an A2G defense will engage to the enemy ground target, then
|
||||
-- use the @{#AI_A2G_DISPATCHER.SetDefenseReactivityLow}() method. For medium and high reactivity, use the methods
|
||||
|
||||
@@ -595,19 +595,24 @@ function AI_AIR:onafterRTB( AIGroup, From, Event, To )
|
||||
--- Calculate the target route point.
|
||||
|
||||
local FromCoord = AIGroup:GetCoordinate()
|
||||
local ToTargetCoord = self.HomeAirbase:GetCoordinate()
|
||||
|
||||
if not self.RTBMinSpeed and not self.RTBMaxSpeed then
|
||||
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 or not self.RTBMaxSpeed then
|
||||
local RTBSpeedMax = AIGroup:GetSpeedMax()
|
||||
self:SetRTBSpeed( RTBSpeedMax * 0.25, RTBSpeedMax * 0.25 )
|
||||
self:SetRTBSpeed( RTBSpeedMax * 0.5, RTBSpeedMax * 0.6 )
|
||||
end
|
||||
|
||||
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 = FromCoord:Translate( 5000, ToAirbaseAngle )
|
||||
local ToAirbaseCoord = ToTargetCoord2
|
||||
|
||||
if Distance < 5000 then
|
||||
self:I( "RTB and near the airbase!" )
|
||||
self:Home()
|
||||
|
||||
@@ -418,6 +418,7 @@ end
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event 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 )
|
||||
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!
|
||||
|
||||
local AttackCount = AttackSetUnit:Count()
|
||||
local AttackCount = AttackSetUnit:CountAlive()
|
||||
|
||||
if AttackCount > 0 then
|
||||
|
||||
@@ -510,6 +511,7 @@ end
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event 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 )
|
||||
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!
|
||||
|
||||
local AttackCount = AttackSetUnit:Count()
|
||||
self:I({AttackCount = AttackCount})
|
||||
local AttackCount = AttackSetUnit:CountAlive()
|
||||
self:T({AttackCount = AttackCount})
|
||||
|
||||
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.
|
||||
-- @param #AI_CARGO self
|
||||
-- @param Wrapper.Group#GROUP Carrier
|
||||
-- @param Core.Set#SET_CARGO CargoSet
|
||||
-- @param #number CombatRadius
|
||||
-- @return #AI_CARGO
|
||||
-- @param Wrapper.Group#GROUP Carrier Cargo carrier group.
|
||||
-- @param Core.Set#SET_CARGO CargoSet Set of cargo(s) to transport.
|
||||
-- @return #AI_CARGO self
|
||||
function AI_CARGO:New( Carrier, CargoSet )
|
||||
|
||||
local self = BASE:Inherit( self, FSM_CONTROLLABLE:New( Carrier ) ) -- #AI_CARGO
|
||||
@@ -52,7 +51,8 @@ function AI_CARGO:New( Carrier, CargoSet )
|
||||
self:AddTransition( "Loaded", "Deploy", "*" )
|
||||
|
||||
self:AddTransition( "*", "Load", "Boarding" )
|
||||
self:AddTransition( { "Boarding", "Loaded" }, "Board", "Boarding" )
|
||||
self:AddTransition( "Boarding", "Board", "Boarding" )
|
||||
self:AddTransition( "Loaded", "Board", "Loaded" )
|
||||
self:AddTransition( "Boarding", "Loaded", "Boarding" )
|
||||
self:AddTransition( "Boarding", "PickedUp", "Loaded" )
|
||||
|
||||
@@ -393,7 +393,7 @@ end
|
||||
function AI_CARGO:onafterBoard( Carrier, From, Event, To, Cargo, CarrierUnit, PickupZone )
|
||||
self:F( { Carrier, From, Event, To, Cargo, CarrierUnit:GetName() } )
|
||||
|
||||
if Carrier and Carrier:IsAlive() then
|
||||
if Carrier and Carrier:IsAlive() and From == "Boarding" then
|
||||
self:F({ IsLoaded = Cargo:IsLoaded(), Cargo:GetName(), Carrier:GetName() } )
|
||||
if not Cargo:IsLoaded() and not Cargo:IsDestroyed() then
|
||||
self:__Board( -10, Cargo, CarrierUnit, PickupZone )
|
||||
@@ -509,7 +509,7 @@ end
|
||||
function AI_CARGO:onafterUnboard( Carrier, From, Event, To, Cargo, CarrierUnit, DeployZone, Defend )
|
||||
self:F( { Carrier, From, Event, To, Cargo:GetName(), DeployZone = DeployZone, Defend = Defend } )
|
||||
|
||||
if Carrier and Carrier:IsAlive() then
|
||||
if Carrier and Carrier:IsAlive() and From == "Unboarding" then
|
||||
if not Cargo:IsUnLoaded() then
|
||||
self:__Unboard( 10, Cargo, CarrierUnit, DeployZone, Defend )
|
||||
return
|
||||
|
||||
@@ -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.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -157,6 +157,45 @@ function AI_CARGO_APC:SetCarrier( CargoCarrier )
|
||||
return self
|
||||
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.
|
||||
-- @param #AI_CARGO_APC self
|
||||
@@ -350,10 +389,13 @@ function AI_CARGO_APC:onafterFollow( APC, From, Event, To )
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- @param #AI_CARGO_APC
|
||||
-- @param Wrapper.Group#GROUP APC
|
||||
function AI_CARGO_APC._Pickup( APC, self, Coordinate, Speed, PickupZone )
|
||||
--- Pickup task function. Triggers Load event.
|
||||
-- @param Wrapper.Group#GROUP APC The cargo carrier group.
|
||||
-- @param #AI_CARGO_APC sel `AI_CARGO_APC` class.
|
||||
-- @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() } )
|
||||
|
||||
@@ -362,8 +404,12 @@ function AI_CARGO_APC._Pickup( APC, self, Coordinate, Speed, PickupZone )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function AI_CARGO_APC._Deploy( APC, self, Coordinate, DeployZone )
|
||||
--- Deploy task function. Triggers Unload event.
|
||||
-- @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 } )
|
||||
|
||||
@@ -392,12 +438,20 @@ function AI_CARGO_APC:onafterPickup( APC, From, Event, To, Coordinate, Speed, He
|
||||
self.RoutePickup = true
|
||||
|
||||
local _speed=Speed or APC:GetSpeedMax()*0.5
|
||||
|
||||
-- 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 Waypoints = APC:TaskGroundOnRoad( Coordinate, _speed, "Line abreast", true )
|
||||
|
||||
local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Pickup", self, Coordinate, Speed, PickupZone )
|
||||
|
||||
self:F({Waypoints = Waypoints})
|
||||
|
||||
local Waypoint = Waypoints[#Waypoints]
|
||||
APC:SetTaskWaypoint( Waypoint, TaskFunction ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone.
|
||||
|
||||
@@ -428,18 +482,34 @@ function AI_CARGO_APC:onafterDeploy( APC, From, Event, To, Coordinate, Speed, He
|
||||
|
||||
self.RouteDeploy = true
|
||||
|
||||
local _speed=Speed or APC:GetSpeedMax()*0.5
|
||||
|
||||
local Waypoints = APC:TaskGroundOnRoad( Coordinate, _speed, "Line abreast", true )
|
||||
-- Set speed in km/h.
|
||||
local speedmax=APC:GetSpeedMax()
|
||||
local _speed=Speed or speedmax*0.5
|
||||
_speed=math.min(_speed, speedmax)
|
||||
|
||||
-- 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 )
|
||||
|
||||
self:F({Waypoints = Waypoints})
|
||||
-- Last waypoint
|
||||
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.
|
||||
|
||||
-- Call parent function.
|
||||
self:GetParent( self, AI_CARGO_APC ).onafterDeploy( self, APC, From, Event, To, Coordinate, Speed, Height, DeployZone )
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
||||
|
||||
--- Route the airplane from one airport or it's current position to another airbase.
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @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.
|
||||
local AirbasePointVec2 = Airbase:GetPointVec2()
|
||||
local ToWaypoint = AirbasePointVec2:WaypointAir(
|
||||
POINT_VEC3.RoutePointAltType.BARO,
|
||||
"Land",
|
||||
"Landing",
|
||||
Speed or Airplane:GetSpeedMax()*0.8
|
||||
)
|
||||
ToWaypoint["airdromeId"] = Airbase:GetID()
|
||||
ToWaypoint["speed_locked"] = true
|
||||
local ToWaypoint = AirbasePointVec2:WaypointAir(POINT_VEC3.RoutePointAltType.BARO, "Land", "Landing", Speed or Airplane:GetSpeedMax()*0.8, true, Airbase)
|
||||
|
||||
--ToWaypoint["airdromeId"] = Airbase:GetID()
|
||||
--ToWaypoint["speed_locked"] = true
|
||||
|
||||
|
||||
-- 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:
|
||||
--
|
||||
@@ -1104,7 +1104,7 @@ function AI_CARGO_DISPATCHER:onafterMonitor()
|
||||
-- The Pickup sequence ...
|
||||
-- Check if this Carrier need to go and Pickup something...
|
||||
-- So, if the cargo bay is not full yet with cargo to be loaded ...
|
||||
self:I( { Carrier = CarrierGroupName, IsRelocating = AI_Cargo:IsRelocating(), IsTransporting = AI_Cargo:IsTransporting() } )
|
||||
self:T( { Carrier = CarrierGroupName, IsRelocating = AI_Cargo:IsRelocating(), IsTransporting = AI_Cargo:IsTransporting() } )
|
||||
if AI_Cargo:IsRelocating() == false and AI_Cargo:IsTransporting() == false then
|
||||
-- ok, so there is a free Carrier
|
||||
-- now find the first cargo that is Unloaded
|
||||
|
||||
@@ -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:
|
||||
--
|
||||
@@ -181,25 +181,36 @@ function AI_CARGO_DISPATCHER_APC:New( APCSet, CargoSet, PickupZoneSet, DeployZon
|
||||
return self
|
||||
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 )
|
||||
|
||||
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
|
||||
|
||||
--- 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.
|
||||
-- @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.
|
||||
-- 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!
|
||||
-- @return #AI_CARGO_DISPATCHER_APC
|
||||
-- @usage
|
||||
--
|
||||
-- -- 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.
|
||||
-- AICargoDispatcher:SetCombatRadius( false )
|
||||
-- AICargoDispatcher:SetCombatRadius( 0 )
|
||||
function AI_CARGO_DISPATCHER_APC:SetCombatRadius( CombatRadius )
|
||||
|
||||
self.CombatRadius = CombatRadius or 0
|
||||
@@ -207,3 +218,41 @@ function AI_CARGO_DISPATCHER_APC:SetCombatRadius( CombatRadius )
|
||||
return self
|
||||
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.
|
||||
--
|
||||
-- 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.
|
||||
-- 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.
|
||||
@@ -67,16 +67,11 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet )
|
||||
|
||||
self:AddTransition( "Unloaded", "Pickup", "*" )
|
||||
self:AddTransition( "Loaded", "Deploy", "*" )
|
||||
|
||||
self:AddTransition( { "Unloaded", "Loading" }, "Load", "Boarding" )
|
||||
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( "*", "Loaded", "Loaded" )
|
||||
self:AddTransition( "Unboarding", "Pickup", "Unloaded" )
|
||||
self:AddTransition( "Unloaded", "Unboard", "Unloaded" )
|
||||
self:AddTransition( "Unloaded", "Unloaded", "Unloaded" )
|
||||
self:AddTransition( "*", "PickedUp", "*" )
|
||||
self:AddTransition( "*", "Landed", "*" )
|
||||
self:AddTransition( "*", "Queue", "*" )
|
||||
self:AddTransition( "*", "Orbit" , "*" )
|
||||
@@ -100,13 +95,31 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet )
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @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
|
||||
-- @function [parent=#AI_CARGO_HELICOPTER] Pickup
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @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
|
||||
-- @function [parent=#AI_CARGO_HELICOPTER] __Pickup
|
||||
@@ -122,7 +135,7 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet )
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @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
|
||||
|
||||
--- Deploy Handler OnAfter for AI_CARGO_HELICOPTER
|
||||
@@ -132,21 +145,42 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet )
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @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
|
||||
-- @function [parent=#AI_CARGO_HELICOPTER] Deploy
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @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
|
||||
-- @function [parent=#AI_CARGO_HELICOPTER] __Deploy
|
||||
-- @param #number Delay Delay in seconds.
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @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.
|
||||
-- The helicopter reference is used in the semaphore AI_CARGO_QUEUE.
|
||||
@@ -228,7 +262,7 @@ end
|
||||
-- @param Event
|
||||
-- @param To
|
||||
function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To )
|
||||
|
||||
self:F({From, Event, To})
|
||||
Helicopter:F( { Name = Helicopter:GetName() } )
|
||||
|
||||
if Helicopter and Helicopter:IsAlive() then
|
||||
@@ -243,7 +277,7 @@ function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To )
|
||||
self:F( { Helicopter:GetName(), Height = Helicopter:GetHeight( true ), Velocity = Helicopter:GetVelocityKMH() } )
|
||||
|
||||
if self.RoutePickup == true then
|
||||
if Helicopter:GetHeight( true ) <= 5 and Helicopter:GetVelocityKMH() < 10 then
|
||||
if Helicopter:GetHeight( true ) <= 5.5 and Helicopter:GetVelocityKMH() < 15 then
|
||||
--self:Load( Helicopter:GetPointVec2() )
|
||||
self:Load( self.PickupZone )
|
||||
self.RoutePickup = false
|
||||
@@ -251,7 +285,7 @@ function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To )
|
||||
end
|
||||
|
||||
if self.RouteDeploy == true then
|
||||
if Helicopter:GetHeight( true ) <= 5 and Helicopter:GetVelocityKMH() < 10 then
|
||||
if Helicopter:GetHeight( true ) <= 5.5 and Helicopter:GetVelocityKMH() < 15 then
|
||||
self:Unload( self.DeployZone )
|
||||
self.RouteDeploy = false
|
||||
end
|
||||
@@ -269,7 +303,7 @@ end
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @param #number Speed
|
||||
function AI_CARGO_HELICOPTER:onafterQueue( Helicopter, From, Event, To, Coordinate, Speed, DeployZone )
|
||||
|
||||
self:F({From, Event, To, Coordinate, Speed, DeployZone})
|
||||
local HelicopterInZone = false
|
||||
|
||||
if Helicopter and Helicopter:IsAlive() == true then
|
||||
@@ -308,7 +342,11 @@ function AI_CARGO_HELICOPTER:onafterQueue( Helicopter, From, Event, To, Coordina
|
||||
-- true
|
||||
-- )
|
||||
-- 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(
|
||||
"RADIO",
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
@@ -348,28 +386,17 @@ end
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @param #number Speed
|
||||
function AI_CARGO_HELICOPTER:onafterOrbit( Helicopter, From, Event, To, Coordinate )
|
||||
|
||||
self:F({From, Event, To, Coordinate})
|
||||
|
||||
if Helicopter and Helicopter:IsAlive() then
|
||||
|
||||
local Route = {}
|
||||
|
||||
-- local CoordinateFrom = Helicopter:GetCoordinate()
|
||||
-- local WaypointFrom = CoordinateFrom:WaypointAir(
|
||||
-- "RADIO",
|
||||
-- POINT_VEC3.RoutePointType.TurningPoint,
|
||||
-- POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
-- Speed,
|
||||
-- true
|
||||
-- )
|
||||
-- Route[#Route+1] = WaypointFrom
|
||||
local CoordinateTo = Coordinate
|
||||
local WaypointTo = CoordinateTo:WaypointAir(
|
||||
"RADIO",
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
50,
|
||||
true
|
||||
)
|
||||
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("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, 50, true)
|
||||
Route[#Route+1] = WaypointTo
|
||||
|
||||
local Tasks = {}
|
||||
@@ -379,7 +406,7 @@ function AI_CARGO_HELICOPTER:onafterOrbit( Helicopter, From, Event, To, Coordina
|
||||
Route[#Route+1] = WaypointTo
|
||||
|
||||
-- Now route the helicopter
|
||||
Helicopter:Route( Route, 0 )
|
||||
Helicopter:Route(Route, 0)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -395,7 +422,7 @@ end
|
||||
-- @param #boolean Deployed Cargo is deployed.
|
||||
-- @return #boolean True if all cargo has been unloaded.
|
||||
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 )
|
||||
|
||||
@@ -408,7 +435,6 @@ function AI_CARGO_HELICOPTER:onafterDeployed( Helicopter, From, Event, To, Deplo
|
||||
|
||||
self:GetParent( self, AI_CARGO_HELICOPTER ).onafterDeployed( self, Helicopter, From, Event, To, DeployZone )
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- On after Pickup event.
|
||||
@@ -418,11 +444,12 @@ end
|
||||
-- @param Event
|
||||
-- @param To
|
||||
-- @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 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 )
|
||||
|
||||
self:F({Coordinate, Speed, Height, PickupZone })
|
||||
|
||||
if Helicopter and Helicopter:IsAlive() ~= nil then
|
||||
|
||||
Helicopter:Activate()
|
||||
@@ -436,25 +463,16 @@ function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordin
|
||||
|
||||
--- Calculate the target route point.
|
||||
local CoordinateFrom = Helicopter:GetCoordinate()
|
||||
local CoordinateTo = Coordinate
|
||||
|
||||
--- Create a route point of type air.
|
||||
local WaypointFrom = CoordinateFrom:WaypointAir(
|
||||
"RADIO",
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
_speed,
|
||||
true
|
||||
)
|
||||
local WaypointFrom = CoordinateFrom:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, _speed, true)
|
||||
|
||||
--- Create a route point of type air.
|
||||
local WaypointTo = CoordinateTo:WaypointAir(
|
||||
"RADIO",
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
_speed,
|
||||
true
|
||||
)
|
||||
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("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint,_speed, true)
|
||||
|
||||
Route[#Route+1] = WaypointFrom
|
||||
Route[#Route+1] = WaypointTo
|
||||
@@ -495,10 +513,10 @@ end
|
||||
-- @param Event
|
||||
-- @param To
|
||||
-- @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.
|
||||
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
|
||||
|
||||
self.RouteDeploy = true
|
||||
@@ -514,25 +532,17 @@ function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordin
|
||||
|
||||
--- Create a route point of type air.
|
||||
local CoordinateFrom = Helicopter:GetCoordinate()
|
||||
local WaypointFrom = CoordinateFrom:WaypointAir(
|
||||
"RADIO",
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
_speed,
|
||||
true
|
||||
)
|
||||
local WaypointFrom = CoordinateFrom:WaypointAir("RADIO", 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.
|
||||
local CoordinateTo = Coordinate
|
||||
local WaypointTo = CoordinateTo:WaypointAir(
|
||||
"RADIO",
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
_speed,
|
||||
true
|
||||
)
|
||||
|
||||
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("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, _speed, true)
|
||||
|
||||
Route[#Route+1] = WaypointTo
|
||||
Route[#Route+1] = WaypointTo
|
||||
@@ -542,7 +552,9 @@ function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordin
|
||||
|
||||
local Tasks = {}
|
||||
|
||||
-- The _Deploy function does not exist.
|
||||
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:TaskLandAtVec2( CoordinateTo:GetVec2() )
|
||||
@@ -566,11 +578,12 @@ end
|
||||
-- @param Event
|
||||
-- @param To
|
||||
-- @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 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 )
|
||||
|
||||
self:F({From, Event, To, Coordinate, Speed, Height})
|
||||
|
||||
if Helicopter and Helicopter:IsAlive() ~= nil then
|
||||
|
||||
self.RouteHome = true
|
||||
@@ -579,30 +592,23 @@ function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinat
|
||||
|
||||
--- Calculate the target route point.
|
||||
|
||||
Coordinate.y = Height
|
||||
--Coordinate.y = Height
|
||||
Height = Height or 50
|
||||
|
||||
Speed = Speed or Helicopter:GetSpeedMax()*0.5
|
||||
|
||||
--- Create a route point of type air.
|
||||
local CoordinateFrom = Helicopter:GetCoordinate()
|
||||
local WaypointFrom = CoordinateFrom:WaypointAir(
|
||||
"RADIO",
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
Speed ,
|
||||
true
|
||||
)
|
||||
|
||||
local WaypointFrom = CoordinateFrom:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, Speed, true)
|
||||
Route[#Route+1] = WaypointFrom
|
||||
|
||||
--- Create a route point of type air.
|
||||
local CoordinateTo = Coordinate
|
||||
local WaypointTo = CoordinateTo:WaypointAir(
|
||||
"RADIO",
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
Speed ,
|
||||
true
|
||||
)
|
||||
local CoordinateTo = Coordinate
|
||||
local landheight = CoordinateTo:GetLandHeight() -- get target height
|
||||
CoordinateTo.y = landheight + Height -- flight height should be 50m above ground
|
||||
|
||||
local WaypointTo = CoordinateTo:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, Speed, true)
|
||||
|
||||
Route[#Route+1] = WaypointTo
|
||||
|
||||
@@ -613,12 +619,11 @@ function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinat
|
||||
|
||||
Tasks[#Tasks+1] = Helicopter:TaskLandAtVec2( CoordinateTo:GetVec2() )
|
||||
Route[#Route].task = Helicopter:TaskCombo( Tasks )
|
||||
|
||||
|
||||
Route[#Route+1] = WaypointTo
|
||||
|
||||
-- Now route the helicopter
|
||||
Helicopter:Route( Route, 0 )
|
||||
|
||||
Helicopter:Route(Route, 0)
|
||||
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(
|
||||
function( FollowGroup )
|
||||
self:E("Following")
|
||||
--self:E("Following")
|
||||
FollowGroup:SetState( self, "Mode", self.__Enum.Mode.Formation )
|
||||
end
|
||||
)
|
||||
|
||||
self:SetFlightModeFormation()
|
||||
|
||||
self:SetFlightRandomization( 2 )
|
||||
|
||||
self:SetStartState( "None" )
|
||||
@@ -997,7 +999,7 @@ function AI_FORMATION:SetFlightModeMission( FollowGroup )
|
||||
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
|
||||
FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Mission )
|
||||
else
|
||||
self.EscortGroupSet:ForSomeGroupAlive(
|
||||
self.FollowGroupSet:ForSomeGroupAlive(
|
||||
--- @param Core.Group#GROUP EscortGroup
|
||||
function( FollowGroup )
|
||||
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, "Mode", self.__Enum.Mode.Attack )
|
||||
else
|
||||
self.EscortGroupSet:ForSomeGroupAlive(
|
||||
self.FollowGroupSet:ForSomeGroupAlive(
|
||||
--- @param Core.Group#GROUP EscortGroup
|
||||
function( FollowGroup )
|
||||
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, "Mode", self.__Enum.Mode.Formation )
|
||||
else
|
||||
self.EscortGroupSet:ForSomeGroupAlive(
|
||||
self.FollowGroupSet:ForSomeGroupAlive(
|
||||
--- @param Core.Group#GROUP EscortGroup
|
||||
function( FollowGroup )
|
||||
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
|
||||
|
||||
@@ -190,7 +190,7 @@ do -- ACT_ROUTE
|
||||
self:F( { ZoneName = ZoneName } )
|
||||
local Zone = Zone -- Core.Zone#ZONE
|
||||
local ZoneCoord = Zone:GetCoordinate()
|
||||
local ZoneDistance = ZoneCoord:Get2DDistance( self.Coordinate )
|
||||
local ZoneDistance = ZoneCoord:Get2DDistance( Coordinate )
|
||||
self:F( { ShortestDistance, ShortestReferenceName } )
|
||||
if ShortestDistance == 0 or ZoneDistance < ShortestDistance then
|
||||
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.
|
||||
-- @param #CARGO_REPRESENTABLE self
|
||||
-- @param #string Type
|
||||
-- @param #string Name
|
||||
-- @param #number LoadRadius (optional)
|
||||
-- @param #number NearRadius (optional)
|
||||
-- @param Wrapper.Positionable#POSITIONABLE CargoObject The cargo object.
|
||||
-- @param #string Type Type name
|
||||
-- @param #string Name Name.
|
||||
-- @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
|
||||
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
|
||||
self:F( { Type, Name, LoadRadius, NearRadius } )
|
||||
|
||||
local Desc = CargoObject:GetDesc()
|
||||
self:I( { Desc = Desc } )
|
||||
-- Descriptors.
|
||||
local Desc=CargoObject:GetDesc()
|
||||
self:T({Desc=Desc})
|
||||
|
||||
-- Weight.
|
||||
local Weight = math.random( 80, 120 )
|
||||
|
||||
-- Adjust weight..
|
||||
if Desc then
|
||||
if Desc.typeName == "2B11 mortar" then
|
||||
Weight = 210
|
||||
@@ -1086,13 +1094,8 @@ do -- CARGO_REPRESENTABLE
|
||||
end
|
||||
end
|
||||
|
||||
-- Set 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
|
||||
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.
|
||||
-- @return #CARGO_GROUP Cargo group object.
|
||||
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
|
||||
self:F( { Type, Name, LoadRadius } )
|
||||
|
||||
@@ -76,6 +78,9 @@ do -- CARGO_GROUP
|
||||
local GroupName = CargoGroup:GetName()
|
||||
self.CargoName = Name
|
||||
self.CargoTemplate = UTILS.DeepCopy( _DATABASE:GetGroupTemplate( GroupName ) )
|
||||
|
||||
-- Deactivate late activation.
|
||||
self.CargoTemplate.lateActivation=false
|
||||
|
||||
self.GroupTemplate = UTILS.DeepCopy( self.CargoTemplate )
|
||||
self.GroupTemplate.name = self.CargoName .. "#CARGO"
|
||||
@@ -481,7 +486,7 @@ do -- CARGO_GROUP
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Core.Point#POINT_VEC2
|
||||
-- @param Core.Point#POINT_VEC2 ToPointVec2
|
||||
function CARGO_GROUP:onafterUnLoad( From, Event, To, ToPointVec2, ... )
|
||||
--self:F( { From, Event, To, ToPointVec2 } )
|
||||
|
||||
@@ -491,7 +496,10 @@ do -- CARGO_GROUP
|
||||
self.CargoSet:ForEach(
|
||||
function( Cargo )
|
||||
--Cargo:UnLoad( ToPointVec2 )
|
||||
local RandomVec2=ToPointVec2:GetRandomPointVec2InRadius(20, 10)
|
||||
local RandomVec2=nil
|
||||
if ToPointVec2 then
|
||||
RandomVec2=ToPointVec2:GetRandomPointVec2InRadius(20, 10)
|
||||
end
|
||||
Cargo:UnBoard( RandomVec2 )
|
||||
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)
|
||||
-- @return #CARGO_UNIT
|
||||
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:T( self.ClassName )
|
||||
|
||||
-- Set event prio.
|
||||
self:SetEventPriority( 5 )
|
||||
|
||||
return self
|
||||
@@ -100,7 +103,12 @@ do -- CARGO_UNIT
|
||||
|
||||
-- Respawn the group...
|
||||
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.CargoCarrier = nil
|
||||
|
||||
|
||||
@@ -451,23 +451,23 @@ do -- Event Handling
|
||||
|
||||
--- Subscribe to a DCS Event.
|
||||
-- @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.
|
||||
-- @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
|
||||
end
|
||||
|
||||
--- UnSubscribe to a DCS event.
|
||||
-- @param #BASE self
|
||||
-- @param Core.Event#EVENTS Event
|
||||
-- @param Core.Event#EVENTS EventID Event ID.
|
||||
-- @return #BASE
|
||||
function BASE:UnHandleEvent( Event )
|
||||
function BASE:UnHandleEvent( EventID )
|
||||
|
||||
self:EventDispatcher():RemoveEvent( self, Event )
|
||||
self:EventDispatcher():RemoveEvent( self, EventID )
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -676,6 +676,44 @@ do -- Event Handling
|
||||
-- @param #BASE self
|
||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||
|
||||
--- 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
|
||||
|
||||
|
||||
@@ -765,31 +803,48 @@ function BASE:CreateEventTakeoff( EventTime, 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)
|
||||
end
|
||||
|
||||
-- TODO: Complete DCS#Event structure.
|
||||
--- The main event handling function... This function captures all events generated for the class.
|
||||
-- @param #BASE self
|
||||
-- @param DCS#Event event
|
||||
function BASE:onEvent(event)
|
||||
--self:F( { BaseEventCodes[event.id], event } )
|
||||
|
||||
if self then
|
||||
for EventID, EventObject in pairs( self.Events ) do
|
||||
|
||||
for EventID, EventObject in pairs(self.Events) do
|
||||
if EventObject.EventEnabled then
|
||||
--env.info( 'onEvent Table EventObject.Self = ' .. tostring(EventObject.Self) )
|
||||
--env.info( 'onEvent event.id = ' .. tostring(event.id) )
|
||||
--env.info( 'onEvent EventObject.Event = ' .. tostring(EventObject.Event) )
|
||||
|
||||
if event.id == EventObject.Event then
|
||||
|
||||
if self == EventObject.Self then
|
||||
|
||||
if event.initiator and event.initiator:isExist() then
|
||||
event.IniUnitName = event.initiator:getName()
|
||||
end
|
||||
|
||||
if event.target and event.target:isExist() then
|
||||
event.TgtUnitName = event.target:getName()
|
||||
end
|
||||
--self:T( { BaseEventCodes[event.id], event } )
|
||||
--EventObject.EventFunction( self, event )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions:
|
||||
-- ### Contributions: **funkyfranky**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -33,6 +33,9 @@
|
||||
|
||||
|
||||
--- @type DATABASE
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @field #table Templates Templates: Units, Groups, Statics, ClientsByName, ClientsByID.
|
||||
-- @field #table CLIENTS Clients.
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
--- Contains collections of wrapper objects defined within MOOSE that reflect objects within the simulator.
|
||||
@@ -126,8 +129,6 @@ function DATABASE:New()
|
||||
self:HandleEvent( EVENTS.DeleteCargo )
|
||||
self:HandleEvent( EVENTS.NewZone )
|
||||
self:HandleEvent( EVENTS.DeleteZone )
|
||||
|
||||
-- Follow alive players and clients
|
||||
--self:HandleEvent( EVENTS.PlayerEnterUnit, self._EventOnPlayerEnterUnit ) -- This is not working anymore!, handling this through the birth event.
|
||||
self:HandleEvent( EVENTS.PlayerLeaveUnit, self._EventOnPlayerLeaveUnit )
|
||||
|
||||
@@ -135,42 +136,11 @@ function DATABASE:New()
|
||||
self:_RegisterGroupsAndUnits()
|
||||
self:_RegisterClients()
|
||||
self:_RegisterStatics()
|
||||
--self:_RegisterPlayers()
|
||||
self:_RegisterAirbases()
|
||||
--self:_RegisterPlayers()
|
||||
|
||||
self.UNITS_Position = 0
|
||||
|
||||
--- @param #DATABASE self
|
||||
local function CheckPlayers( self )
|
||||
|
||||
local CoalitionsData = { AlivePlayersRed = coalition.getPlayers( coalition.side.RED ), AlivePlayersBlue = coalition.getPlayers( coalition.side.BLUE ), AlivePlayersNeutral = coalition.getPlayers( coalition.side.NEUTRAL )}
|
||||
for CoalitionId, CoalitionData in pairs( CoalitionsData ) do
|
||||
--self:E( { "CoalitionData:", CoalitionData } )
|
||||
for UnitId, UnitData in pairs( CoalitionData ) do
|
||||
if UnitData and UnitData:isExist() then
|
||||
|
||||
local UnitName = UnitData:getName()
|
||||
local PlayerName = UnitData:getPlayerName()
|
||||
local PlayerUnit = UNIT:Find( UnitData )
|
||||
--self:T( { "UnitData:", UnitData, UnitName, PlayerName, PlayerUnit } )
|
||||
|
||||
if PlayerName and PlayerName ~= "" then
|
||||
if self.PLAYERS[PlayerName] == nil or self.PLAYERS[PlayerName] ~= UnitName then
|
||||
--self:E( { "Add player for unit:", UnitName, PlayerName } )
|
||||
self:AddPlayer( UnitName, PlayerName )
|
||||
--_EVENTDISPATCHER:CreateEventPlayerEnterUnit( PlayerUnit )
|
||||
local Settings = SETTINGS:Set( PlayerName )
|
||||
Settings:SetPlayerMenu( PlayerUnit )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--self:E( "Scheduling" )
|
||||
--PlayerCheckSchedule = SCHEDULER:New( nil, CheckPlayers, { self }, 1, 1 )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -187,14 +157,22 @@ end
|
||||
|
||||
--- Adds a Unit based on the Unit Name in the DATABASE.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string DCSUnitName Unit name.
|
||||
-- @return Wrapper.Unit#UNIT The added unit.
|
||||
function DATABASE:AddUnit( DCSUnitName )
|
||||
|
||||
if not self.UNITS[DCSUnitName] then
|
||||
if not self.UNITS[DCSUnitName] then
|
||||
|
||||
-- Debug info.
|
||||
self:T( { "Add UNIT:", DCSUnitName } )
|
||||
local UnitRegister = UNIT:Register( DCSUnitName )
|
||||
self.UNITS[DCSUnitName] = UNIT:Register( DCSUnitName )
|
||||
|
||||
--local UnitRegister = UNIT:Register( DCSUnitName )
|
||||
|
||||
-- Register unit
|
||||
self.UNITS[DCSUnitName]=UNIT:Register(DCSUnitName)
|
||||
|
||||
table.insert( self.UNITS_Index, DCSUnitName )
|
||||
-- This is not used anywhere in MOOSE as far as I can see so I remove it until there comes an error somewhere.
|
||||
--table.insert(self.UNITS_Index, DCSUnitName )
|
||||
end
|
||||
|
||||
return self.UNITS[DCSUnitName]
|
||||
@@ -210,6 +188,8 @@ end
|
||||
|
||||
--- Adds a Static based on the Static Name in the DATABASE.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string DCSStaticName Name of the static.
|
||||
-- @return Wrapper.Static#STATIC The static object.
|
||||
function DATABASE:AddStatic( DCSStaticName )
|
||||
|
||||
if not self.STATICS[DCSStaticName] then
|
||||
@@ -224,8 +204,7 @@ end
|
||||
--- Deletes a Static from the DATABASE based on the Static Name.
|
||||
-- @param #DATABASE self
|
||||
function DATABASE:DeleteStatic( DCSStaticName )
|
||||
|
||||
--self.STATICS[DCSStaticName] = nil
|
||||
self.STATICS[DCSStaticName] = nil
|
||||
end
|
||||
|
||||
--- Finds a STATIC based on the StaticName.
|
||||
@@ -319,24 +298,78 @@ do -- Zones
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:_RegisterZones()
|
||||
|
||||
for ZoneID, ZoneData in pairs( env.mission.triggers.zones ) do
|
||||
for ZoneID, ZoneData in pairs(env.mission.triggers.zones) do
|
||||
local ZoneName = ZoneData.name
|
||||
|
||||
-- Color
|
||||
local color=ZoneData.color or {1, 0, 0, 0.15}
|
||||
|
||||
-- Create new Zone
|
||||
local Zone=nil --Core.Zone#ZONE_BASE
|
||||
|
||||
if ZoneData.type==0 then
|
||||
|
||||
---
|
||||
-- Circular zone
|
||||
---
|
||||
|
||||
self:I(string.format("Register ZONE: %s (Circular)", ZoneName))
|
||||
|
||||
Zone=ZONE:New(ZoneName)
|
||||
|
||||
else
|
||||
|
||||
self:I( { "Register ZONE:", Name = ZoneName } )
|
||||
local Zone = ZONE:New( ZoneName )
|
||||
self.ZONENAMES[ZoneName] = ZoneName
|
||||
self:AddZone( ZoneName, Zone )
|
||||
---
|
||||
-- Quad-point zone
|
||||
---
|
||||
|
||||
self:I(string.format("Register ZONE: %s (Polygon, Quad)", ZoneName))
|
||||
|
||||
Zone=ZONE_POLYGON_BASE:New(ZoneName, ZoneData.verticies)
|
||||
|
||||
--for i,vec2 in pairs(ZoneData.verticies) do
|
||||
-- local coord=COORDINATE:NewFromVec2(vec2)
|
||||
-- coord:MarkToAll(string.format("%s Point %d", ZoneName, i))
|
||||
--end
|
||||
|
||||
end
|
||||
|
||||
if Zone then
|
||||
|
||||
-- Store color of zone.
|
||||
Zone.Color=color
|
||||
|
||||
-- Store in DB.
|
||||
self.ZONENAMES[ZoneName] = ZoneName
|
||||
|
||||
-- Add zone.
|
||||
self:AddZone(ZoneName, Zone)
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- Polygon zones defined by late activated groups.
|
||||
for ZoneGroupName, ZoneGroup in pairs( self.GROUPS ) do
|
||||
if ZoneGroupName:match("#ZONE_POLYGON") then
|
||||
|
||||
local ZoneName1 = ZoneGroupName:match("(.*)#ZONE_POLYGON")
|
||||
local ZoneName2 = ZoneGroupName:match(".*#ZONE_POLYGON(.*)")
|
||||
local ZoneName = ZoneName1 .. ( ZoneName2 or "" )
|
||||
|
||||
self:I( { "Register ZONE_POLYGON:", Name = ZoneName } )
|
||||
-- Debug output
|
||||
self:I(string.format("Register ZONE: %s (Polygon)", ZoneName))
|
||||
|
||||
-- Create a new polygon zone.
|
||||
local Zone_Polygon = ZONE_POLYGON:New( ZoneName, ZoneGroup )
|
||||
|
||||
-- Set color.
|
||||
Zone_Polygon:SetColor({1, 0, 0}, 0.15)
|
||||
|
||||
-- Store name in DB.
|
||||
self.ZONENAMES[ZoneName] = ZoneName
|
||||
|
||||
-- Add zone to DB.
|
||||
self:AddZone( ZoneName, Zone_Polygon )
|
||||
end
|
||||
end
|
||||
@@ -432,7 +465,6 @@ do -- cargo
|
||||
local Groups = UTILS.DeepCopy( self.GROUPS ) -- This is a very important statement. CARGO_GROUP:New creates a new _DATABASE.GROUP entry, which will confuse the loop. I searched 4 hours on this to find the bug!
|
||||
|
||||
for CargoGroupName, CargoGroup in pairs( Groups ) do
|
||||
self:I( { Cargo = CargoGroupName } )
|
||||
if self:IsCargo( CargoGroupName ) then
|
||||
local CargoInfo = CargoGroupName:match("#CARGO(.*)")
|
||||
local CargoParam = CargoInfo and CargoInfo:match( "%((.*)%)")
|
||||
@@ -489,6 +521,8 @@ end
|
||||
|
||||
--- Adds a CLIENT based on the ClientName in the DATABASE.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string ClientName Name of the Client unit.
|
||||
-- @return Wrapper.Client#CLIENT The client object.
|
||||
function DATABASE:AddClient( ClientName )
|
||||
|
||||
if not self.CLIENTS[ClientName] then
|
||||
@@ -626,6 +660,9 @@ function DATABASE:Spawn( SpawnTemplate )
|
||||
end
|
||||
|
||||
--- Set a status to a Group within the Database, this to check crossing events for example.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string GroupName Group name.
|
||||
-- @param #string Status Status.
|
||||
function DATABASE:SetStatusGroup( GroupName, Status )
|
||||
self:F2( Status )
|
||||
|
||||
@@ -633,8 +670,11 @@ function DATABASE:SetStatusGroup( GroupName, Status )
|
||||
end
|
||||
|
||||
--- Get a status to a Group within the Database, this to check crossing events for example.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string GroupName Group name.
|
||||
-- @return #string Status or an empty string "".
|
||||
function DATABASE:GetStatusGroup( GroupName )
|
||||
self:F2( Status )
|
||||
self:F2( GroupName )
|
||||
|
||||
if self.Templates.Groups[GroupName] then
|
||||
return self.Templates.Groups[GroupName].Status
|
||||
@@ -648,7 +688,8 @@ end
|
||||
-- @param #table GroupTemplate
|
||||
-- @param DCS#coalition.side CoalitionSide The coalition.side of the object.
|
||||
-- @param DCS#Object.Category CategoryID The Object.category of the object.
|
||||
-- @param DCS#country.id CountryID the country.id of the object
|
||||
-- @param DCS#country.id CountryID the country ID of the object.
|
||||
-- @param #string GroupName (Optional) The name of the group. Default is `GroupTemplate.name`.
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, CategoryID, CountryID, GroupName )
|
||||
|
||||
@@ -704,6 +745,7 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, Category
|
||||
UnitNames[#UnitNames+1] = self.Templates.Units[UnitTemplate.name].UnitName
|
||||
end
|
||||
|
||||
-- Debug info.
|
||||
self:T( { Group = self.Templates.Groups[GroupTemplateName].GroupName,
|
||||
Coalition = self.Templates.Groups[GroupTemplateName].CoalitionID,
|
||||
Category = self.Templates.Groups[GroupTemplateName].CategoryID,
|
||||
@@ -713,6 +755,10 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, Category
|
||||
)
|
||||
end
|
||||
|
||||
--- Get group template.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string GroupName Group name.
|
||||
-- @return #table Group template table.
|
||||
function DATABASE:GetGroupTemplate( GroupName )
|
||||
local GroupTemplate = self.Templates.Groups[GroupName].Template
|
||||
GroupTemplate.SpawnCoalitionID = self.Templates.Groups[GroupName].CoalitionID
|
||||
@@ -723,7 +769,10 @@ end
|
||||
|
||||
--- Private method that registers new Static Templates within the DATABASE Object.
|
||||
-- @param #DATABASE self
|
||||
-- @param #table StaticTemplate
|
||||
-- @param #table StaticTemplate Template table.
|
||||
-- @param #number CoalitionID Coalition ID.
|
||||
-- @param #number CategoryID Category ID.
|
||||
-- @param #number CountryID Country ID.
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, CategoryID, CountryID )
|
||||
|
||||
@@ -744,7 +793,8 @@ function DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, Category
|
||||
self.Templates.Statics[StaticTemplateName].CoalitionID = CoalitionID
|
||||
self.Templates.Statics[StaticTemplateName].CountryID = CountryID
|
||||
|
||||
self:I( { Static = self.Templates.Statics[StaticTemplateName].StaticName,
|
||||
-- Debug info.
|
||||
self:T( { Static = self.Templates.Statics[StaticTemplateName].StaticName,
|
||||
Coalition = self.Templates.Statics[StaticTemplateName].CoalitionID,
|
||||
Category = self.Templates.Statics[StaticTemplateName].CategoryID,
|
||||
Country = self.Templates.Statics[StaticTemplateName].CountryID
|
||||
@@ -753,48 +803,101 @@ function DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, Category
|
||||
|
||||
self:AddStatic( StaticTemplateName )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- @param #DATABASE self
|
||||
--- Get static group template.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string StaticName Name of the static.
|
||||
-- @return #table Static template table.
|
||||
function DATABASE:GetStaticGroupTemplate( StaticName )
|
||||
local StaticTemplate = self.Templates.Statics[StaticName].GroupTemplate
|
||||
return StaticTemplate, self.Templates.Statics[StaticName].CoalitionID, self.Templates.Statics[StaticName].CategoryID, self.Templates.Statics[StaticName].CountryID
|
||||
if self.Templates.Statics[StaticName] then
|
||||
local StaticTemplate = self.Templates.Statics[StaticName].GroupTemplate
|
||||
return StaticTemplate, self.Templates.Statics[StaticName].CoalitionID, self.Templates.Statics[StaticName].CategoryID, self.Templates.Statics[StaticName].CountryID
|
||||
else
|
||||
self:E("ERROR: Static group template does NOT exist for static "..tostring(StaticName))
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
--- @param #DATABASE self
|
||||
--- Get static unit template.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string StaticName Name of the static.
|
||||
-- @return #table Static template table.
|
||||
function DATABASE:GetStaticUnitTemplate( StaticName )
|
||||
local UnitTemplate = self.Templates.Statics[StaticName].UnitTemplate
|
||||
return UnitTemplate, self.Templates.Statics[StaticName].CoalitionID, self.Templates.Statics[StaticName].CategoryID, self.Templates.Statics[StaticName].CountryID
|
||||
if self.Templates.Statics[StaticName] then
|
||||
local UnitTemplate = self.Templates.Statics[StaticName].UnitTemplate
|
||||
return UnitTemplate, self.Templates.Statics[StaticName].CoalitionID, self.Templates.Statics[StaticName].CategoryID, self.Templates.Statics[StaticName].CountryID
|
||||
else
|
||||
self:E("ERROR: Static unit template does NOT exist for static "..tostring(StaticName))
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Get group name from unit name.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string UnitName Name of the unit.
|
||||
-- @return #string Group name.
|
||||
function DATABASE:GetGroupNameFromUnitName( UnitName )
|
||||
return self.Templates.Units[UnitName].GroupName
|
||||
if self.Templates.Units[UnitName] then
|
||||
return self.Templates.Units[UnitName].GroupName
|
||||
else
|
||||
self:E("ERROR: Unit template does not exist for unit "..tostring(UnitName))
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
--- Get group template from unit name.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string UnitName Name of the unit.
|
||||
-- @return #table Group template.
|
||||
function DATABASE:GetGroupTemplateFromUnitName( UnitName )
|
||||
return self.Templates.Units[UnitName].GroupTemplate
|
||||
if self.Templates.Units[UnitName] then
|
||||
return self.Templates.Units[UnitName].GroupTemplate
|
||||
else
|
||||
self:E("ERROR: Unit template does not exist for unit "..tostring(UnitName))
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
--- Get coalition ID from client name.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string ClientName Name of the Client.
|
||||
-- @return #number Coalition ID.
|
||||
function DATABASE:GetCoalitionFromClientTemplate( ClientName )
|
||||
return self.Templates.ClientsByName[ClientName].CoalitionID
|
||||
end
|
||||
|
||||
--- Get category ID from client name.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string ClientName Name of the Client.
|
||||
-- @return #number Category ID.
|
||||
function DATABASE:GetCategoryFromClientTemplate( ClientName )
|
||||
return self.Templates.ClientsByName[ClientName].CategoryID
|
||||
end
|
||||
|
||||
--- Get country ID from client name.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string ClientName Name of the Client.
|
||||
-- @return #number Country ID.
|
||||
function DATABASE:GetCountryFromClientTemplate( ClientName )
|
||||
return self.Templates.ClientsByName[ClientName].CountryID
|
||||
end
|
||||
|
||||
--- Airbase
|
||||
|
||||
--- Get coalition ID from airbase name.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string AirbaseName Name of the airbase.
|
||||
-- @return #number Coalition ID.
|
||||
function DATABASE:GetCoalitionFromAirbase( AirbaseName )
|
||||
return self.AIRBASES[AirbaseName]:GetCoalition()
|
||||
end
|
||||
|
||||
--- Get category from airbase name.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string AirbaseName Name of the airbase.
|
||||
-- @return #number Category.
|
||||
function DATABASE:GetCategoryFromAirbase( AirbaseName )
|
||||
return self.AIRBASES[AirbaseName]:GetCategory()
|
||||
end
|
||||
@@ -831,33 +934,38 @@ end
|
||||
function DATABASE:_RegisterGroupsAndUnits()
|
||||
|
||||
local CoalitionsData = { GroupsRed = coalition.getGroups( coalition.side.RED ), GroupsBlue = coalition.getGroups( coalition.side.BLUE ), GroupsNeutral = coalition.getGroups( coalition.side.NEUTRAL ) }
|
||||
|
||||
for CoalitionId, CoalitionData in pairs( CoalitionsData ) do
|
||||
|
||||
for DCSGroupId, DCSGroup in pairs( CoalitionData ) do
|
||||
|
||||
if DCSGroup:isExist() then
|
||||
|
||||
-- Group name.
|
||||
local DCSGroupName = DCSGroup:getName()
|
||||
|
||||
self:I( { "Register Group:", DCSGroupName } )
|
||||
-- Add group.
|
||||
self:I(string.format("Register Group: %s", tostring(DCSGroupName)))
|
||||
self:AddGroup( DCSGroupName )
|
||||
|
||||
-- Loop over units in group.
|
||||
for DCSUnitId, DCSUnit in pairs( DCSGroup:getUnits() ) do
|
||||
|
||||
-- Get unit name.
|
||||
local DCSUnitName = DCSUnit:getName()
|
||||
self:I( { "Register Unit:", DCSUnitName } )
|
||||
|
||||
-- Add unit.
|
||||
self:I(string.format("Register Unit: %s", tostring(DCSUnitName)))
|
||||
self:AddUnit( DCSUnitName )
|
||||
|
||||
end
|
||||
else
|
||||
self:E( { "Group does not exist: ", DCSGroup } )
|
||||
self:E({"Group does not exist: ", DCSGroup})
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
self:T("Groups:")
|
||||
for GroupName, Group in pairs( self.GROUPS ) do
|
||||
self:T( { "Group:", GroupName } )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -867,7 +975,7 @@ end
|
||||
function DATABASE:_RegisterClients()
|
||||
|
||||
for ClientName, ClientTemplate in pairs( self.Templates.ClientsByName ) do
|
||||
self:T( { "Register Client:", ClientName } )
|
||||
self:I(string.format("Register Client: %s", tostring(ClientName)))
|
||||
self:AddClient( ClientName )
|
||||
end
|
||||
|
||||
@@ -877,15 +985,15 @@ end
|
||||
--- @param #DATABASE self
|
||||
function DATABASE:_RegisterStatics()
|
||||
|
||||
local CoalitionsData = { GroupsRed = coalition.getStaticObjects( coalition.side.RED ), GroupsBlue = coalition.getStaticObjects( coalition.side.BLUE ) }
|
||||
self:I( { Statics = CoalitionsData } )
|
||||
local CoalitionsData={GroupsRed=coalition.getStaticObjects(coalition.side.RED), GroupsBlue=coalition.getStaticObjects(coalition.side.BLUE), GroupsNeutral=coalition.getStaticObjects(coalition.side.NEUTRAL)}
|
||||
|
||||
for CoalitionId, CoalitionData in pairs( CoalitionsData ) do
|
||||
for DCSStaticId, DCSStatic in pairs( CoalitionData ) do
|
||||
|
||||
if DCSStatic:isExist() then
|
||||
local DCSStaticName = DCSStatic:getName()
|
||||
|
||||
self:T( { "Register Static:", DCSStaticName } )
|
||||
self:I(string.format("Register Static: %s", tostring(DCSStaticName)))
|
||||
self:AddStatic( DCSStaticName )
|
||||
else
|
||||
self:E( { "Static does not exist: ", DCSStatic } )
|
||||
@@ -911,9 +1019,24 @@ function DATABASE:_RegisterAirbases()
|
||||
|
||||
-- Add and register airbase.
|
||||
local airbase=self:AddAirbase( DCSAirbaseName )
|
||||
|
||||
-- Unique ID.
|
||||
local airbaseUID=airbase:GetID(true)
|
||||
|
||||
-- Debug output.
|
||||
self:I(string.format("Register Airbase: %s, getID=%d, GetID=%d (unique=%d)", DCSAirbaseName, DCSAirbase:getID(), airbase:GetID(), airbase:GetID(true)))
|
||||
local text=string.format("Register %s: %s (ID=%d UID=%d), parking=%d [", AIRBASE.CategoryName[airbase.category], tostring(DCSAirbaseName), airbaseID, airbaseUID, airbase.NparkingTotal)
|
||||
for _,terminalType in pairs(AIRBASE.TerminalType) do
|
||||
if airbase.NparkingTerminal and airbase.NparkingTerminal[terminalType] then
|
||||
text=text..string.format("%d=%d ", terminalType, airbase.NparkingTerminal[terminalType])
|
||||
end
|
||||
end
|
||||
text=text.."]"
|
||||
self:I(text)
|
||||
|
||||
-- Check for DCS bug IDs.
|
||||
if airbaseID~=airbase:GetID() then
|
||||
--self:E("WARNING: :getID does NOT match :GetID!")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -930,36 +1053,74 @@ function DATABASE:_EventOnBirth( Event )
|
||||
self:F( { Event } )
|
||||
|
||||
if Event.IniDCSUnit then
|
||||
|
||||
if Event.IniObjectCategory == 3 then
|
||||
|
||||
self:AddStatic( Event.IniDCSUnitName )
|
||||
|
||||
else
|
||||
|
||||
if Event.IniObjectCategory == 1 then
|
||||
|
||||
self:AddUnit( Event.IniDCSUnitName )
|
||||
self:AddGroup( Event.IniDCSGroupName )
|
||||
|
||||
-- Add airbase if it was spawned later in the mission.
|
||||
local DCSAirbase = Airbase.getByName(Event.IniDCSUnitName)
|
||||
if DCSAirbase then
|
||||
self:I(string.format("Adding airbase %s", tostring(Event.IniDCSUnitName)))
|
||||
self:AddAirbase(Event.IniDCSUnitName)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
if Event.IniObjectCategory == 1 then
|
||||
|
||||
Event.IniUnit = self:FindUnit( Event.IniDCSUnitName )
|
||||
Event.IniGroup = self:FindGroup( Event.IniDCSGroupName )
|
||||
|
||||
-- Client
|
||||
local client=self.CLIENTS[Event.IniDCSUnitName] --Wrapper.Client#CLIENT
|
||||
|
||||
if client then
|
||||
-- TODO: create event ClientAlive
|
||||
end
|
||||
|
||||
-- Get player name.
|
||||
local PlayerName = Event.IniUnit:GetPlayerName()
|
||||
|
||||
if PlayerName then
|
||||
self:I( { "Player Joined:", PlayerName } )
|
||||
self:AddClient( Event.IniDCSUnitName )
|
||||
|
||||
-- Debug info.
|
||||
self:I(string.format("Player '%s' joint unit '%s' of group '%s'", tostring(PlayerName), tostring(Event.IniDCSUnitName), tostring(Event.IniDCSGroupName)))
|
||||
|
||||
-- Add client in case it does not exist already.
|
||||
if not client then
|
||||
client=self:AddClient(Event.IniDCSUnitName)
|
||||
end
|
||||
|
||||
-- Add player.
|
||||
client:AddPlayer(PlayerName)
|
||||
|
||||
-- Add player.
|
||||
if not self.PLAYERS[PlayerName] then
|
||||
self:AddPlayer( Event.IniUnitName, PlayerName )
|
||||
end
|
||||
|
||||
-- Player settings.
|
||||
local Settings = SETTINGS:Set( PlayerName )
|
||||
Settings:SetPlayerMenu( Event.IniUnit )
|
||||
--MENU_INDEX:Refresh( Event.IniGroup )
|
||||
Settings:SetPlayerMenu(Event.IniUnit)
|
||||
|
||||
-- Create an event.
|
||||
self:CreateEventPlayerEnterAircraft(Event.IniUnit)
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -967,22 +1128,53 @@ end
|
||||
-- @param #DATABASE self
|
||||
-- @param Core.Event#EVENTDATA Event
|
||||
function DATABASE:_EventOnDeadOrCrash( Event )
|
||||
self:F2( { Event } )
|
||||
|
||||
if Event.IniDCSUnit then
|
||||
|
||||
local name=Event.IniDCSUnitName
|
||||
|
||||
if Event.IniObjectCategory == 3 then
|
||||
|
||||
---
|
||||
-- STATICS
|
||||
---
|
||||
|
||||
if self.STATICS[Event.IniDCSUnitName] then
|
||||
self:DeleteStatic( Event.IniDCSUnitName )
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
if Event.IniObjectCategory == 1 then
|
||||
|
||||
---
|
||||
-- UNITS
|
||||
---
|
||||
|
||||
-- Delete unit.
|
||||
if self.UNITS[Event.IniDCSUnitName] then
|
||||
self:DeleteUnit( Event.IniDCSUnitName )
|
||||
self:DeleteUnit(Event.IniDCSUnitName)
|
||||
end
|
||||
|
||||
-- Remove client players.
|
||||
local client=self.CLIENTS[name] --Wrapper.Client#CLIENT
|
||||
|
||||
if client then
|
||||
client:RemovePlayers()
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
-- Add airbase if it was spawned later in the mission.
|
||||
local airbase=self.AIRBASES[Event.IniDCSUnitName] --Wrapper.Airbase#AIRBASE
|
||||
if airbase and (airbase:IsHelipad() or airbase:IsShip()) then
|
||||
self:DeleteAirbase(Event.IniDCSUnitName)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- Account destroys.
|
||||
self:AccountDestroys( Event )
|
||||
end
|
||||
|
||||
@@ -995,15 +1187,31 @@ function DATABASE:_EventOnPlayerEnterUnit( Event )
|
||||
|
||||
if Event.IniDCSUnit then
|
||||
if Event.IniObjectCategory == 1 then
|
||||
|
||||
-- Add unit.
|
||||
self:AddUnit( Event.IniDCSUnitName )
|
||||
|
||||
-- Ini unit.
|
||||
Event.IniUnit = self:FindUnit( Event.IniDCSUnitName )
|
||||
|
||||
-- Add group.
|
||||
self:AddGroup( Event.IniDCSGroupName )
|
||||
|
||||
-- Get player unit.
|
||||
local PlayerName = Event.IniDCSUnit:getPlayerName()
|
||||
if not self.PLAYERS[PlayerName] then
|
||||
self:AddPlayer( Event.IniDCSUnitName, PlayerName )
|
||||
|
||||
if PlayerName then
|
||||
|
||||
if not self.PLAYERS[PlayerName] then
|
||||
self:AddPlayer( Event.IniDCSUnitName, PlayerName )
|
||||
end
|
||||
|
||||
local Settings = SETTINGS:Set( PlayerName )
|
||||
Settings:SetPlayerMenu( Event.IniUnit )
|
||||
|
||||
else
|
||||
self:E("ERROR: getPlayerName() returned nil for event PlayerEnterUnit")
|
||||
end
|
||||
local Settings = SETTINGS:Set( PlayerName )
|
||||
Settings:SetPlayerMenu( Event.IniUnit )
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1016,13 +1224,30 @@ function DATABASE:_EventOnPlayerLeaveUnit( Event )
|
||||
self:F2( { Event } )
|
||||
|
||||
if Event.IniUnit then
|
||||
|
||||
if Event.IniObjectCategory == 1 then
|
||||
|
||||
-- Try to get the player name. This can be buggy for multicrew aircraft!
|
||||
local PlayerName = Event.IniUnit:GetPlayerName()
|
||||
if PlayerName and self.PLAYERS[PlayerName] then
|
||||
self:I( { "Player Left:", PlayerName } )
|
||||
|
||||
if PlayerName then --and self.PLAYERS[PlayerName] then
|
||||
|
||||
-- Debug info.
|
||||
self:I(string.format("Player '%s' left unit %s", tostring(PlayerName), tostring(Event.IniUnitName)))
|
||||
|
||||
-- Remove player menu.
|
||||
local Settings = SETTINGS:Set( PlayerName )
|
||||
Settings:RemovePlayerMenu( Event.IniUnit )
|
||||
self:DeletePlayer( Event.IniUnit, PlayerName )
|
||||
Settings:RemovePlayerMenu(Event.IniUnit)
|
||||
|
||||
-- Delete player.
|
||||
self:DeletePlayer(Event.IniUnit, PlayerName)
|
||||
|
||||
-- Client stuff.
|
||||
local client=self.CLIENTS[Event.IniDCSUnitName] --Wrapper.Client#CLIENT
|
||||
if client then
|
||||
client:RemovePlayer(PlayerName)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1355,19 +1580,13 @@ function DATABASE:_RegisterTemplates()
|
||||
for group_num, Template in pairs(obj_type_data.group) do
|
||||
|
||||
if obj_type_name ~= "static" and Template and Template.units and type(Template.units) == 'table' then --making sure again- this is a valid group
|
||||
self:_RegisterGroupTemplate(
|
||||
Template,
|
||||
CoalitionSide,
|
||||
_DATABASECategory[string.lower(CategoryName)],
|
||||
CountryID
|
||||
)
|
||||
|
||||
self:_RegisterGroupTemplate(Template, CoalitionSide, _DATABASECategory[string.lower(CategoryName)], CountryID)
|
||||
|
||||
else
|
||||
self:_RegisterStaticTemplate(
|
||||
Template,
|
||||
CoalitionSide,
|
||||
_DATABASECategory[string.lower(CategoryName)],
|
||||
CountryID
|
||||
)
|
||||
|
||||
self:_RegisterStaticTemplate(Template, CoalitionSide, _DATABASECategory[string.lower(CategoryName)], CountryID)
|
||||
|
||||
end --if GroupTemplate and GroupTemplate.units then
|
||||
end --for group_num, GroupTemplate in pairs(obj_type_data.group) do
|
||||
end --if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -540,7 +540,7 @@ do -- FSM
|
||||
|
||||
--- Returns a table with the scores defined.
|
||||
-- @param #FSM self
|
||||
-- @param #table Scores.
|
||||
-- @return #table Scores.
|
||||
function FSM:GetScores()
|
||||
return self._Scores or {}
|
||||
end
|
||||
|
||||
@@ -164,6 +164,7 @@ do -- COORDINATE
|
||||
--
|
||||
-- * @{#COORDINATE.WaypointAir}(): Build an air route point.
|
||||
-- * @{#COORDINATE.WaypointGround}(): Build a ground route point.
|
||||
-- * @{#COORDINATE.WaypointNaval}(): Build a naval route point.
|
||||
--
|
||||
-- Route points can be used in the Route methods of the @{Wrapper.Group#GROUP} class.
|
||||
--
|
||||
@@ -183,10 +184,18 @@ do -- COORDINATE
|
||||
--
|
||||
-- ## 9) Coordinate text generation
|
||||
--
|
||||
--
|
||||
-- * @{#COORDINATE.ToStringBR}(): Generates a Bearing & Range text in the format of DDD for DI where DDD is degrees and DI is distance.
|
||||
-- * @{#COORDINATE.ToStringLL}(): Generates a Latutude & Longutude text.
|
||||
--
|
||||
-- ## 10) Drawings on F10 map
|
||||
--
|
||||
-- * @{#COORDINATE.CircleToAll}(): Draw a circle on the F10 map.
|
||||
-- * @{#COORDINATE.LineToAll}(): Draw a line on the F10 map.
|
||||
-- * @{#COORDINATE.RectToAll}(): Draw a rectangle on the F10 map.
|
||||
-- * @{#COORDINATE.QuadToAll}(): Draw a shape with four points on the F10 map.
|
||||
-- * @{#COORDINATE.TextToAll}(): Write some text on the F10 map.
|
||||
-- * @{#COORDINATE.ArrowToAll}(): Draw an arrow on the F10 map.
|
||||
--
|
||||
-- @field #COORDINATE
|
||||
COORDINATE = {
|
||||
ClassName = "COORDINATE",
|
||||
@@ -355,9 +364,9 @@ do -- COORDINATE
|
||||
|
||||
-- Adjust height
|
||||
if altitude==nil then
|
||||
_coord.y=altitude
|
||||
else
|
||||
_coord.y=self:GetLandHeight()
|
||||
else
|
||||
_coord.y=altitude
|
||||
end
|
||||
|
||||
return _coord
|
||||
@@ -625,9 +634,9 @@ do -- COORDINATE
|
||||
|
||||
--- Return a random Coordinate within an Outer Radius and optionally NOT within an Inner Radius of the COORDINATE.
|
||||
-- @param #COORDINATE self
|
||||
-- @param DCS#Distance OuterRadius
|
||||
-- @param DCS#Distance InnerRadius
|
||||
-- @return #COORDINATE
|
||||
-- @param DCS#Distance OuterRadius Outer radius in meters.
|
||||
-- @param DCS#Distance InnerRadius Inner radius in meters.
|
||||
-- @return #COORDINATE self
|
||||
function COORDINATE:GetRandomCoordinateInRadius( OuterRadius, InnerRadius )
|
||||
self:F2( { OuterRadius, InnerRadius } )
|
||||
|
||||
@@ -770,16 +779,14 @@ do -- COORDINATE
|
||||
-- @param #COORDINATE self
|
||||
-- @param #COORDINATE TargetCoordinate The target COORDINATE. Can also be a DCS#Vec3.
|
||||
-- @return DCS#Distance Distance The distance in meters.
|
||||
function COORDINATE:Get2DDistance( TargetCoordinate )
|
||||
function COORDINATE:Get2DDistance(TargetCoordinate)
|
||||
|
||||
local a={x=TargetCoordinate.x-self.x, y=0, z=TargetCoordinate.z-self.z}
|
||||
|
||||
return UTILS.VecNorm(a)
|
||||
|
||||
--local TargetVec3 = TargetCoordinate:GetVec3()
|
||||
--local SourceVec3 = self:GetVec3()
|
||||
|
||||
--return ( ( TargetVec3.x - SourceVec3.x ) ^ 2 + ( TargetVec3.z - SourceVec3.z ) ^ 2 ) ^ 0.5
|
||||
local d=UTILS.VecNorm(a)
|
||||
|
||||
return d
|
||||
|
||||
end
|
||||
|
||||
--- Returns the temperature in Degrees Celsius.
|
||||
@@ -1186,7 +1193,7 @@ do -- COORDINATE
|
||||
|
||||
-- ETA.
|
||||
RoutePoint.ETA=0
|
||||
RoutePoint.ETA_locked=true
|
||||
RoutePoint.ETA_locked=false
|
||||
|
||||
-- Waypoint description.
|
||||
RoutePoint.name=description
|
||||
@@ -1335,7 +1342,7 @@ do -- COORDINATE
|
||||
RoutePoint.formation_template=""
|
||||
|
||||
RoutePoint.ETA=0
|
||||
RoutePoint.ETA_locked=true
|
||||
RoutePoint.ETA_locked=false
|
||||
|
||||
RoutePoint.speed = ( Speed or 20 ) / 3.6
|
||||
RoutePoint.speed_locked = true
|
||||
@@ -1370,7 +1377,7 @@ do -- COORDINATE
|
||||
RoutePoint.formation_template = ""
|
||||
|
||||
RoutePoint.ETA=0
|
||||
RoutePoint.ETA_locked=true
|
||||
RoutePoint.ETA_locked=false
|
||||
|
||||
RoutePoint.speed = ( Speed or 20 ) / 3.6
|
||||
RoutePoint.speed_locked = true
|
||||
@@ -1980,9 +1987,243 @@ do -- COORDINATE
|
||||
function COORDINATE:RemoveMark( MarkID )
|
||||
trigger.action.removeMark( MarkID )
|
||||
end
|
||||
|
||||
--- Line to all.
|
||||
-- Creates a line on the F10 map from one point to another.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #COORDINATE Endpoint COORDIANTE to where the line is drawn.
|
||||
-- @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 (default).
|
||||
-- @param #number Alpha Transparency [0,1]. Default 1.
|
||||
-- @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.
|
||||
-- @param #string Text (Optional) Text displayed when mark is added. Default none.
|
||||
-- @return #number The resulting Mark ID, which is a number. Can be used to remove the object again.
|
||||
function COORDINATE:LineToAll(Endpoint, Coalition, Color, Alpha, LineType, ReadOnly, Text)
|
||||
local MarkID = UTILS.GetMarkID()
|
||||
if ReadOnly==nil then
|
||||
ReadOnly=false
|
||||
end
|
||||
local vec3=Endpoint:GetVec3()
|
||||
Coalition=Coalition or -1
|
||||
Color=Color or {1,0,0}
|
||||
Color[4]=Alpha or 1.0
|
||||
LineType=LineType or 1
|
||||
trigger.action.lineToAll(Coalition, MarkID, self:GetVec3(), vec3, Color, LineType, ReadOnly, Text or "")
|
||||
return MarkID
|
||||
end
|
||||
|
||||
--- Circle to all.
|
||||
-- Creates a circle on the map with a given radius, color, fill color, and outline.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #numberr Radius Radius in meters. Default 1000 m.
|
||||
-- @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 (default).
|
||||
-- @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.
|
||||
-- @param #string Text (Optional) Text displayed when mark is added. Default none.
|
||||
-- @return #number The resulting Mark ID, which is a number. Can be used to remove the object again.
|
||||
function COORDINATE:CircleToAll(Radius, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly, Text)
|
||||
local MarkID = UTILS.GetMarkID()
|
||||
if ReadOnly==nil then
|
||||
ReadOnly=false
|
||||
end
|
||||
local vec3=self:GetVec3()
|
||||
Radius=Radius or 1000
|
||||
Coalition=Coalition or -1
|
||||
Color=Color or {1,0,0}
|
||||
Color[4]=Alpha or 1.0
|
||||
LineType=LineType or 1
|
||||
FillColor=FillColor or Color
|
||||
FillColor[4]=FillAlpha or 0.15
|
||||
trigger.action.circleToAll(Coalition, MarkID, vec3, Radius, Color, FillColor, LineType, ReadOnly, Text or "")
|
||||
return MarkID
|
||||
end
|
||||
|
||||
end -- Markings
|
||||
|
||||
|
||||
--- Rectangle to all. Creates a rectangle on the map from the COORDINATE in one corner to the end COORDINATE in the opposite corner.
|
||||
-- Creates a line on the F10 map from one point to another.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #COORDINATE Endpoint COORDIANTE in the opposite corner.
|
||||
-- @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 (default).
|
||||
-- @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.
|
||||
-- @param #string Text (Optional) Text displayed when mark is added. Default none.
|
||||
-- @return #number The resulting Mark ID, which is a number. Can be used to remove the object again.
|
||||
function COORDINATE:RectToAll(Endpoint, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly, Text)
|
||||
local MarkID = UTILS.GetMarkID()
|
||||
if ReadOnly==nil then
|
||||
ReadOnly=false
|
||||
end
|
||||
local vec3=Endpoint:GetVec3()
|
||||
Coalition=Coalition or -1
|
||||
Color=Color or {1,0,0}
|
||||
Color[4]=Alpha or 1.0
|
||||
LineType=LineType or 1
|
||||
FillColor=FillColor or Color
|
||||
FillColor[4]=FillAlpha or 0.15
|
||||
trigger.action.rectToAll(Coalition, MarkID, self:GetVec3(), vec3, Color, FillColor, LineType, ReadOnly, Text or "")
|
||||
return MarkID
|
||||
end
|
||||
|
||||
--- Creates a shape defined by 4 points on the F10 map. The first point is the current COORDINATE. The remaining three points need to be specified.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #COORDINATE Coord2 Second COORDIANTE of the quad shape.
|
||||
-- @param #COORDINATE Coord3 Third COORDIANTE of the quad shape.
|
||||
-- @param #COORDINATE Coord4 Fourth COORDIANTE of the quad shape.
|
||||
-- @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 (default).
|
||||
-- @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.
|
||||
-- @param #string Text (Optional) Text displayed when mark is added. Default none.
|
||||
-- @return #number The resulting Mark ID, which is a number. Can be used to remove the object again.
|
||||
function COORDINATE:QuadToAll(Coord2, Coord3, Coord4, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly, Text)
|
||||
local MarkID = UTILS.GetMarkID()
|
||||
if ReadOnly==nil then
|
||||
ReadOnly=false
|
||||
end
|
||||
local point1=self:GetVec3()
|
||||
local point2=Coord2:GetVec3()
|
||||
local point3=Coord3:GetVec3()
|
||||
local point4=Coord4:GetVec3()
|
||||
Coalition=Coalition or -1
|
||||
Color=Color or {1,0,0}
|
||||
Color[4]=Alpha or 1.0
|
||||
LineType=LineType or 1
|
||||
FillColor=FillColor or Color
|
||||
FillColor[4]=FillAlpha or 0.15
|
||||
trigger.action.quadToAll(Coalition, MarkID, self:GetVec3(), point2, point3, point4, Color, FillColor, LineType, ReadOnly, Text or "")
|
||||
return MarkID
|
||||
end
|
||||
|
||||
--- Creates a free form shape on the F10 map. The first point is the current COORDINATE. The remaining points need to be specified.
|
||||
-- **NOTE**: A free form polygon must have **at least three points** in total and currently only **up to 10 points** in total are supported.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #table Coordinates Table of coordinates of the remaining points of the shape.
|
||||
-- @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 (default).
|
||||
-- @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.
|
||||
-- @param #string Text (Optional) Text displayed when mark is added. Default none.
|
||||
-- @return #number The resulting Mark ID, which is a number. Can be used to remove the object again.
|
||||
function COORDINATE:MarkupToAllFreeForm(Coordinates, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly, Text)
|
||||
|
||||
local MarkID = UTILS.GetMarkID()
|
||||
if ReadOnly==nil then
|
||||
ReadOnly=false
|
||||
end
|
||||
|
||||
Coalition=Coalition or -1
|
||||
|
||||
Color=Color or {1,0,0}
|
||||
Color[4]=Alpha or 1.0
|
||||
|
||||
LineType=LineType or 1
|
||||
|
||||
FillColor=FillColor or UTILS.DeepCopy(Color)
|
||||
FillColor[4]=FillAlpha or 0.15
|
||||
|
||||
local vecs={}
|
||||
vecs[1]=self:GetVec3()
|
||||
for i,coord in ipairs(Coordinates) do
|
||||
vecs[i+1]=coord:GetVec3()
|
||||
end
|
||||
|
||||
if #vecs<3 then
|
||||
self:E("ERROR: A free form polygon needs at least three points!")
|
||||
elseif #vecs==3 then
|
||||
trigger.action.markupToAll(7, Coalition, MarkID, vecs[1], vecs[2], vecs[3], Color, FillColor, LineType, ReadOnly, Text or "")
|
||||
elseif #vecs==4 then
|
||||
trigger.action.markupToAll(7, Coalition, MarkID, vecs[1], vecs[2], vecs[3], vecs[4], Color, FillColor, LineType, ReadOnly, Text or "")
|
||||
elseif #vecs==5 then
|
||||
trigger.action.markupToAll(7, Coalition, MarkID, vecs[1], vecs[2], vecs[3], vecs[4], vecs[5], Color, FillColor, LineType, ReadOnly, Text or "")
|
||||
elseif #vecs==6 then
|
||||
trigger.action.markupToAll(7, Coalition, MarkID, vecs[1], vecs[2], vecs[3], vecs[4], vecs[5], vecs[6], Color, FillColor, LineType, Text or "")
|
||||
elseif #vecs==7 then
|
||||
trigger.action.markupToAll(7, Coalition, MarkID, vecs[1], vecs[2], vecs[3], vecs[4], vecs[5], vecs[6], vecs[7], Color, FillColor, LineType, ReadOnly, Text or "")
|
||||
elseif #vecs==8 then
|
||||
trigger.action.markupToAll(7, Coalition, MarkID, vecs[1], vecs[2], vecs[3], vecs[4], vecs[5], vecs[6], vecs[7], vecs[8], Color, FillColor, LineType, ReadOnly, Text or "")
|
||||
elseif #vecs==9 then
|
||||
trigger.action.markupToAll(7, Coalition, MarkID, vecs[1], vecs[2], vecs[3], vecs[4], vecs[5], vecs[6], vecs[7], vecs[8], vecs[9], Color, FillColor, LineType, ReadOnly, Text or "")
|
||||
elseif #vecs==10 then
|
||||
trigger.action.markupToAll(7, Coalition, MarkID, vecs[1], vecs[2], vecs[3], vecs[4], vecs[5], vecs[6], vecs[7], vecs[8], vecs[9], vecs[10], Color, FillColor, LineType, ReadOnly, Text or "")
|
||||
else
|
||||
self:E("ERROR: Currently a free form polygon can only have 10 points in total!")
|
||||
-- Unfortunately, unpack(vecs) does not work! So no idea how to generalize this :(
|
||||
trigger.action.markupToAll(7, Coalition, MarkID, unpack(vecs), Color, FillColor, LineType, ReadOnly, Text or "")
|
||||
end
|
||||
|
||||
return MarkID
|
||||
end
|
||||
|
||||
--- Text to all. Creates a text imposed on the map at the COORDINATE. Text scales with the map.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #string Text Text displayed on the F10 map.
|
||||
-- @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 (default).
|
||||
-- @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.3.
|
||||
-- @param #number FontSize Font size. Default 14.
|
||||
-- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false.
|
||||
-- @return #number The resulting Mark ID, which is a number. Can be used to remove the object again.
|
||||
function COORDINATE:TextToAll(Text, Coalition, Color, Alpha, FillColor, FillAlpha, FontSize, ReadOnly)
|
||||
local MarkID = UTILS.GetMarkID()
|
||||
if ReadOnly==nil then
|
||||
ReadOnly=false
|
||||
end
|
||||
Coalition=Coalition or -1
|
||||
Color=Color or {1,0,0}
|
||||
Color[4]=Alpha or 1.0
|
||||
FillColor=FillColor or Color
|
||||
FillColor[4]=FillAlpha or 0.3
|
||||
FontSize=FontSize or 14
|
||||
trigger.action.textToAll(Coalition, MarkID, self:GetVec3(), Color, FillColor, FontSize, ReadOnly, Text or "Hello World")
|
||||
return MarkID
|
||||
end
|
||||
|
||||
--- Arrow to all. Creates an arrow from the COORDINATE to the endpoint COORDINATE on the F10 map. There is no control over other dimensions of the arrow.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #COORDINATE Endpoint COORDINATE where the tip of the arrow is pointing at.
|
||||
-- @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 (default).
|
||||
-- @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.
|
||||
-- @param #string Text (Optional) Text displayed when mark is added. Default none.
|
||||
-- @return #number The resulting Mark ID, which is a number. Can be used to remove the object again.
|
||||
function COORDINATE:ArrowToAll(Endpoint, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly, Text)
|
||||
local MarkID = UTILS.GetMarkID()
|
||||
if ReadOnly==nil then
|
||||
ReadOnly=false
|
||||
end
|
||||
local vec3=Endpoint:GetVec3()
|
||||
Coalition=Coalition or -1
|
||||
Color=Color or {1,0,0}
|
||||
Color[4]=Alpha or 1.0
|
||||
LineType=LineType or 1
|
||||
FillColor=FillColor or Color
|
||||
FillColor[4]=FillAlpha or 0.15
|
||||
--trigger.action.textToAll(Coalition, MarkID, self:GetVec3(), Color, FillColor, FontSize, ReadOnly, Text or "Hello World")
|
||||
trigger.action.arrowToAll(Coalition, MarkID, vec3, self:GetVec3(), Color, FillColor, LineType, ReadOnly, Text or "")
|
||||
return MarkID
|
||||
end
|
||||
|
||||
--- Returns if a Coordinate has Line of Sight (LOS) with the ToCoordinate.
|
||||
-- @param #COORDINATE self
|
||||
|
||||
@@ -525,7 +525,7 @@ end
|
||||
|
||||
--- Get unit from which we want to transmit a radio message. This has to be an aircraft for subtitles to work.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @return Wrapper.Unit#UNIT Sending aircraft unit or nil if was not setup, is not an aircraft or is not alive.
|
||||
-- @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()
|
||||
|
||||
-- Check if we have a sending aircraft.
|
||||
@@ -538,7 +538,7 @@ function RADIOQUEUE:_GetRadioSender()
|
||||
sender=UNIT:FindByName(self.sendername)
|
||||
|
||||
-- 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
|
||||
end
|
||||
|
||||
|
||||
@@ -880,8 +880,8 @@ do -- SET_GROUP
|
||||
--
|
||||
-- * @{#SET_GROUP.FilterCoalitions}: Builds the SET_GROUP with the groups belonging to the coalition(s).
|
||||
-- * @{#SET_GROUP.FilterCategories}: Builds the SET_GROUP with the groups belonging to the category(ies).
|
||||
-- * @{#SET_GROUP.FilterCountries}: Builds the SET_GROUP with the gruops belonging to the country(ies).
|
||||
-- * @{#SET_GROUP.FilterPrefixes}: Builds the SET_GROUP with the groups starting with the same prefix string(s).
|
||||
-- * @{#SET_GROUP.FilterCountries}: Builds the SET_GROUP with the groups belonging to the country(ies).
|
||||
-- * @{#SET_GROUP.FilterPrefixes}: Builds the SET_GROUP with the groups *containing* the given string in the group name. **Attention!** Bad naming convention, as this not really filtering *prefixes*.
|
||||
-- * @{#SET_GROUP.FilterActive}: Builds the SET_GROUP with the groups that are only active. Groups that are inactive (late activation) won't be included in the set!
|
||||
--
|
||||
-- For the Category Filter, extra methods have been added:
|
||||
@@ -1241,10 +1241,10 @@ do -- SET_GROUP
|
||||
end
|
||||
|
||||
|
||||
--- Builds a set of groups of defined GROUP prefixes.
|
||||
-- All the groups starting with the given prefixes will be included within the set.
|
||||
--- Builds a set of groups that contain the given string in their group name.
|
||||
-- **Attention!** Bad naming convention as this **does not** filter only **prefixes** but all groups that **contain** the string.
|
||||
-- @param #SET_GROUP self
|
||||
-- @param #string Prefixes The prefix of which the group name starts with.
|
||||
-- @param #string Prefixes The string pattern(s) that needs to be contained in the group name. Can also be passed as a `#table` of strings.
|
||||
-- @return #SET_GROUP self
|
||||
function SET_GROUP:FilterPrefixes( Prefixes )
|
||||
if not self.Filter.GroupPrefixes then
|
||||
@@ -1843,7 +1843,7 @@ do -- SET_UNIT
|
||||
-- * @{#SET_UNIT.FilterCategories}: Builds the SET_UNIT with the units belonging to the category(ies).
|
||||
-- * @{#SET_UNIT.FilterTypes}: Builds the SET_UNIT with the units belonging to the unit type(s).
|
||||
-- * @{#SET_UNIT.FilterCountries}: Builds the SET_UNIT with the units belonging to the country(ies).
|
||||
-- * @{#SET_UNIT.FilterPrefixes}: Builds the SET_UNIT with the units starting with the same prefix string(s).
|
||||
-- * @{#SET_UNIT.FilterPrefixes}: Builds the SET_UNIT with the units sharing the same string(s) in their name. **ATTENTION!** Bad naming convention as this *does not* only filter *prefixes*.
|
||||
-- * @{#SET_UNIT.FilterActive}: Builds the SET_UNIT with the units that are only active. Units that are inactive (late activation) won't be included in the set!
|
||||
--
|
||||
-- Once the filter criteria have been set for the SET_UNIT, you can start filtering using:
|
||||
@@ -2105,10 +2105,10 @@ do -- SET_UNIT
|
||||
end
|
||||
|
||||
|
||||
--- Builds a set of units of defined unit prefixes.
|
||||
-- All the units starting with the given prefixes will be included within the set.
|
||||
--- Builds a set of UNITs that contain a given string in their unit name.
|
||||
-- **Attention!** Bad naming convention as this **does not** filter only **prefixes** but all units that **contain** the string.
|
||||
-- @param #SET_UNIT self
|
||||
-- @param #string Prefixes The prefix of which the unit name starts with.
|
||||
-- @param #string Prefixes The string pattern(s) that needs to be contained in the unit name. Can also be passed as a `#table` of strings.
|
||||
-- @return #SET_UNIT self
|
||||
function SET_UNIT:FilterPrefixes( Prefixes )
|
||||
if not self.Filter.UnitPrefixes then
|
||||
@@ -2963,7 +2963,7 @@ do -- SET_STATIC
|
||||
-- * @{#SET_STATIC.FilterCategories}: Builds the SET_STATIC with the units belonging to the category(ies).
|
||||
-- * @{#SET_STATIC.FilterTypes}: Builds the SET_STATIC with the units belonging to the unit type(s).
|
||||
-- * @{#SET_STATIC.FilterCountries}: Builds the SET_STATIC with the units belonging to the country(ies).
|
||||
-- * @{#SET_STATIC.FilterPrefixes}: Builds the SET_STATIC with the units starting with the same prefix string(s).
|
||||
-- * @{#SET_STATIC.FilterPrefixes}: Builds the SET_STATIC with the units containing the same string(s) in their name. **ATTENTION** bad naming convention as this *does not** only filter *prefixes*.
|
||||
--
|
||||
-- Once the filter criteria have been set for the SET_STATIC, you can start filtering using:
|
||||
--
|
||||
@@ -3176,10 +3176,10 @@ do -- SET_STATIC
|
||||
end
|
||||
|
||||
|
||||
--- Builds a set of units of defined unit prefixes.
|
||||
-- All the units starting with the given prefixes will be included within the set.
|
||||
--- Builds a set of STATICs that contain the given string in their name.
|
||||
-- **Attention!** Bad naming convention as this **does not** filter only **prefixes** but all statics that **contain** the string.
|
||||
-- @param #SET_STATIC self
|
||||
-- @param #string Prefixes The prefix of which the unit name starts with.
|
||||
-- @param #string Prefixes The string pattern(s) that need to be contained in the static name. Can also be passed as a `#table` of strings.
|
||||
-- @return #SET_STATIC self
|
||||
function SET_STATIC:FilterPrefixes( Prefixes )
|
||||
if not self.Filter.StaticPrefixes then
|
||||
@@ -3267,7 +3267,7 @@ do -- SET_STATIC
|
||||
-- @param #SET_STATIC self
|
||||
-- @param Core.Zone#ZONE Zone The Zone to be tested for.
|
||||
-- @return #boolean
|
||||
function SET_STATIC:IsPatriallyInZone( Zone )
|
||||
function SET_STATIC:IsPartiallyInZone( Zone )
|
||||
|
||||
local IsPartiallyInZone = false
|
||||
|
||||
@@ -3675,7 +3675,7 @@ do -- SET_CLIENT
|
||||
-- * @{#SET_CLIENT.FilterCategories}: Builds the SET_CLIENT with the clients belonging to the category(ies).
|
||||
-- * @{#SET_CLIENT.FilterTypes}: Builds the SET_CLIENT with the clients belonging to the client type(s).
|
||||
-- * @{#SET_CLIENT.FilterCountries}: Builds the SET_CLIENT with the clients belonging to the country(ies).
|
||||
-- * @{#SET_CLIENT.FilterPrefixes}: Builds the SET_CLIENT with the clients starting with the same prefix string(s).
|
||||
-- * @{#SET_CLIENT.FilterPrefixes}: Builds the SET_CLIENT with the clients containing the same string(s) in their unit/pilot name. **ATTENTION!** Bad naming convention as this *does not* only filter *prefixes*.
|
||||
-- * @{#SET_CLIENT.FilterActive}: Builds the SET_CLIENT with the units that are only active. Units that are inactive (late activation) won't be included in the set!
|
||||
--
|
||||
-- Once the filter criteria have been set for the SET_CLIENT, you can start filtering using:
|
||||
@@ -3858,10 +3858,10 @@ do -- SET_CLIENT
|
||||
end
|
||||
|
||||
|
||||
--- Builds a set of clients of defined client prefixes.
|
||||
-- All the clients starting with the given prefixes will be included within the set.
|
||||
--- Builds a set of CLIENTs that contain the given string in their unit/pilot name.
|
||||
-- **Attention!** Bad naming convention as this **does not** filter only **prefixes** but all clients that **contain** the string.
|
||||
-- @param #SET_CLIENT self
|
||||
-- @param #string Prefixes The prefix of which the client name starts with.
|
||||
-- @param #string Prefixes The string pattern(s) that needs to be contained in the unit/pilot name. Can also be passed as a `#table` of strings.
|
||||
-- @return #SET_CLIENT self
|
||||
function SET_CLIENT:FilterPrefixes( Prefixes )
|
||||
if not self.Filter.ClientPrefixes then
|
||||
@@ -4114,7 +4114,7 @@ do -- SET_PLAYER
|
||||
-- * @{#SET_PLAYER.FilterCategories}: Builds the SET_PLAYER with the clients belonging to the category(ies).
|
||||
-- * @{#SET_PLAYER.FilterTypes}: Builds the SET_PLAYER with the clients belonging to the client type(s).
|
||||
-- * @{#SET_PLAYER.FilterCountries}: Builds the SET_PLAYER with the clients belonging to the country(ies).
|
||||
-- * @{#SET_PLAYER.FilterPrefixes}: Builds the SET_PLAYER with the clients starting with the same prefix string(s).
|
||||
-- * @{#SET_PLAYER.FilterPrefixes}: Builds the SET_PLAYER with the clients sharing the same string(s) in their unit/pilot name. **ATTENTION** Bad naming convention as this *does not* only filter prefixes.
|
||||
--
|
||||
-- Once the filter criteria have been set for the SET_PLAYER, you can start filtering using:
|
||||
--
|
||||
@@ -4293,10 +4293,10 @@ do -- SET_PLAYER
|
||||
end
|
||||
|
||||
|
||||
--- Builds a set of clients of defined client prefixes.
|
||||
-- All the clients starting with the given prefixes will be included within the set.
|
||||
--- Builds a set of PLAYERs that contain the given string in their unit/pilot name.
|
||||
-- **Attention!** Bad naming convention as this **does not** filter only **prefixes** but all player clients that **contain** the string.
|
||||
-- @param #SET_PLAYER self
|
||||
-- @param #string Prefixes The prefix of which the client name starts with.
|
||||
-- @param #string Prefixes The string pattern(s) that needs to be contained in the unit/pilot name. Can also be passed as a `#table` of strings.
|
||||
-- @return #SET_PLAYER self
|
||||
function SET_PLAYER:FilterPrefixes( Prefixes )
|
||||
if not self.Filter.ClientPrefixes then
|
||||
@@ -4705,7 +4705,8 @@ do -- SET_AIRBASE
|
||||
if _DATABASE then
|
||||
|
||||
-- We use the BaseCaptured event, which is generated by DCS when a base got captured.
|
||||
self:HandleEvent( EVENTS.BaseCaptured )
|
||||
self:HandleEvent(EVENTS.BaseCaptured)
|
||||
self:HandleEvent(EVENTS.Dead)
|
||||
|
||||
-- We initialize the first set.
|
||||
for ObjectName, Object in pairs( self.Database ) do
|
||||
@@ -4720,10 +4721,9 @@ do -- SET_AIRBASE
|
||||
return self
|
||||
end
|
||||
|
||||
--- Starts the filtering.
|
||||
--- Base capturing event.
|
||||
-- @param #SET_AIRBASE self
|
||||
-- @param Core.Event#EVENT EventData
|
||||
-- @return #SET_AIRBASE self
|
||||
function SET_AIRBASE:OnEventBaseCaptured(EventData)
|
||||
|
||||
-- When a base got captured, we reevaluate the set.
|
||||
@@ -4739,24 +4739,36 @@ do -- SET_AIRBASE
|
||||
|
||||
end
|
||||
|
||||
--- Dead event.
|
||||
-- @param #SET_AIRBASE self
|
||||
-- @param Core.Event#EVENT EventData
|
||||
function SET_AIRBASE:OnEventDead(EventData)
|
||||
|
||||
local airbaseName, airbase=self:FindInDatabase(EventData)
|
||||
|
||||
if airbase and airbase:IsShip() or airbase:IsHelipad() then
|
||||
self:RemoveAirbasesByName(airbaseName)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Handles the Database to check on an event (birth) that the Object was added in the Database.
|
||||
-- This is required, because sometimes the _DATABASE birth event gets called later than the SET_BASE birth event!
|
||||
-- @param #SET_AIRBASE self
|
||||
-- @param Core.Event#EVENTDATA Event
|
||||
-- @return #string The name of the AIRBASE
|
||||
-- @return #table The AIRBASE
|
||||
-- @param Core.Event#EVENTDATA Event Event data.
|
||||
-- @return #string The name of the AIRBASE.
|
||||
-- @return Wrapper.Airbase#AIRBASE The AIRBASE object.
|
||||
function SET_AIRBASE:AddInDatabase( Event )
|
||||
self:F3( { Event } )
|
||||
|
||||
return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName]
|
||||
end
|
||||
|
||||
--- Handles the Database to check on any event that Object exists in the Database.
|
||||
-- This is required, because sometimes the _DATABASE event gets called later than the SET_BASE event or vise versa!
|
||||
-- @param #SET_AIRBASE self
|
||||
-- @param Core.Event#EVENTDATA Event
|
||||
-- @return #string The name of the AIRBASE
|
||||
-- @return #table The AIRBASE
|
||||
-- @param Core.Event#EVENTDATA Event Event data.
|
||||
-- @return #string The name of the AIRBASE.
|
||||
-- @return Wrapper.Airbase#AIRBASE The AIRBASE object.
|
||||
function SET_AIRBASE:FindInDatabase( Event )
|
||||
self:F3( { Event } )
|
||||
|
||||
@@ -4862,7 +4874,7 @@ do -- SET_CARGO
|
||||
-- Filter criteria are defined by:
|
||||
--
|
||||
-- * @{#SET_CARGO.FilterCoalitions}: Builds the SET_CARGO with the cargos belonging to the coalition(s).
|
||||
-- * @{#SET_CARGO.FilterPrefixes}: Builds the SET_CARGO with the cargos containing the prefix string(s).
|
||||
-- * @{#SET_CARGO.FilterPrefixes}: Builds the SET_CARGO with the cargos containing the same string(s). **ATTENTION** Bad naming convention as this *does not* only filter *prefixes*.
|
||||
-- * @{#SET_CARGO.FilterTypes}: Builds the SET_CARGO with the cargos belonging to the cargo type(s).
|
||||
-- * @{#SET_CARGO.FilterCountries}: Builds the SET_CARGO with the cargos belonging to the country(ies).
|
||||
--
|
||||
@@ -4916,7 +4928,7 @@ do -- SET_CARGO
|
||||
--- (R2.1) Add CARGO to SET_CARGO.
|
||||
-- @param Core.Set#SET_CARGO self
|
||||
-- @param Cargo.Cargo#CARGO Cargo A single cargo.
|
||||
-- @return self
|
||||
-- @return Core.Set#SET_CARGO self
|
||||
function SET_CARGO:AddCargo( Cargo ) --R2.4
|
||||
|
||||
self:Add( Cargo:GetName(), Cargo )
|
||||
@@ -4928,7 +4940,7 @@ do -- SET_CARGO
|
||||
--- (R2.1) Add CARGOs to SET_CARGO.
|
||||
-- @param Core.Set#SET_CARGO self
|
||||
-- @param #string AddCargoNames A single name or an array of CARGO names.
|
||||
-- @return self
|
||||
-- @return Core.Set#SET_CARGO self
|
||||
function SET_CARGO:AddCargosByName( AddCargoNames ) --R2.1
|
||||
|
||||
local AddCargoNamesArray = ( type( AddCargoNames ) == "table" ) and AddCargoNames or { AddCargoNames }
|
||||
@@ -4943,7 +4955,7 @@ do -- SET_CARGO
|
||||
--- (R2.1) Remove CARGOs from SET_CARGO.
|
||||
-- @param Core.Set#SET_CARGO self
|
||||
-- @param Wrapper.Cargo#CARGO RemoveCargoNames A single name or an array of CARGO names.
|
||||
-- @return self
|
||||
-- @return Core.Set#SET_CARGO self
|
||||
function SET_CARGO:RemoveCargosByName( RemoveCargoNames ) --R2.1
|
||||
|
||||
local RemoveCargoNamesArray = ( type( RemoveCargoNames ) == "table" ) and RemoveCargoNames or { RemoveCargoNames }
|
||||
@@ -5024,10 +5036,10 @@ do -- SET_CARGO
|
||||
end
|
||||
|
||||
|
||||
--- (R2.1) Builds a set of cargos of defined cargo prefixes.
|
||||
-- All the cargos starting with the given prefixes will be included within the set.
|
||||
--- Builds a set of CARGOs that contain a given string in their name.
|
||||
-- **Attention!** Bad naming convention as this **does not** filter only **prefixes** but all cargos that **contain** the string.
|
||||
-- @param #SET_CARGO self
|
||||
-- @param #string Prefixes The prefix of which the cargo name starts with.
|
||||
-- @param #string Prefixes The string pattern(s) that need to be in the cargo name. Can also be passed as a `#table` of strings.
|
||||
-- @return #SET_CARGO self
|
||||
function SET_CARGO:FilterPrefixes( Prefixes ) --R2.1
|
||||
if not self.Filter.CargoPrefixes then
|
||||
@@ -5303,7 +5315,7 @@ do -- SET_ZONE
|
||||
-- You can set filter criteria to build the collection of zones in SET_ZONE.
|
||||
-- Filter criteria are defined by:
|
||||
--
|
||||
-- * @{#SET_ZONE.FilterPrefixes}: Builds the SET_ZONE with the zones having a certain text pattern of prefix.
|
||||
-- * @{#SET_ZONE.FilterPrefixes}: Builds the SET_ZONE with the zones having a certain text pattern in their name. **ATTENTION!** Bad naming convention as this *does not* only filter *prefixes*.
|
||||
--
|
||||
-- Once the filter criteria have been set for the SET_ZONE, you can start filtering using:
|
||||
--
|
||||
@@ -5399,10 +5411,12 @@ do -- SET_ZONE
|
||||
|
||||
--- Get a random zone from the set.
|
||||
-- @param #SET_ZONE self
|
||||
-- @param #number margin Number of tries to find a zone
|
||||
-- @return Core.Zone#ZONE_BASE The random Zone.
|
||||
-- @return #nil if no zone in the collection.
|
||||
function SET_ZONE:GetRandomZone()
|
||||
|
||||
function SET_ZONE:GetRandomZone(margin)
|
||||
|
||||
local margin = margin or 100
|
||||
if self:Count() ~= 0 then
|
||||
|
||||
local Index = self.Index
|
||||
@@ -5411,9 +5425,11 @@ do -- SET_ZONE
|
||||
-- Loop until a zone has been found.
|
||||
-- The :GetZoneMaybe() call will evaluate the probability for the zone to be selected.
|
||||
-- If the zone is not selected, then nil is returned by :GetZoneMaybe() and the loop continues!
|
||||
while not ZoneFound do
|
||||
local counter = 0
|
||||
while (not ZoneFound) or (counter < margin) do
|
||||
local ZoneRandom = math.random( 1, #Index )
|
||||
ZoneFound = self.Set[Index[ZoneRandom]]:GetZoneMaybe()
|
||||
counter = counter + 1
|
||||
end
|
||||
|
||||
return ZoneFound
|
||||
@@ -5434,10 +5450,10 @@ do -- SET_ZONE
|
||||
|
||||
|
||||
|
||||
--- Builds a set of zones of defined zone prefixes.
|
||||
-- All the zones starting with the given prefixes will be included within the set.
|
||||
--- Builds a set of ZONEs that contain the given string in their name.
|
||||
-- **ATTENTION!** Bad naming convention as this **does not** filter only **prefixes** but all zones that **contain** the string.
|
||||
-- @param #SET_ZONE self
|
||||
-- @param #string Prefixes The prefix of which the zone name starts with.
|
||||
-- @param #string Prefixes The string pattern(s) that need to be contained in the zone name. Can also be passed as a `#table` of strings.
|
||||
-- @return #SET_ZONE self
|
||||
function SET_ZONE:FilterPrefixes( Prefixes )
|
||||
if not self.Filter.Prefixes then
|
||||
@@ -5636,7 +5652,7 @@ do -- SET_ZONE_GOAL
|
||||
-- You can set filter criteria to build the collection of zones in SET_ZONE_GOAL.
|
||||
-- Filter criteria are defined by:
|
||||
--
|
||||
-- * @{#SET_ZONE_GOAL.FilterPrefixes}: Builds the SET_ZONE_GOAL with the zones having a certain text pattern of prefix.
|
||||
-- * @{#SET_ZONE_GOAL.FilterPrefixes}: Builds the SET_ZONE_GOAL with the zones having a certain text pattern in their name. **ATTENTION!** Bad naming convention as this *does not* only filter *prefixes*.
|
||||
--
|
||||
-- Once the filter criteria have been set for the SET_ZONE_GOAL, you can start filtering using:
|
||||
--
|
||||
@@ -5752,10 +5768,10 @@ do -- SET_ZONE_GOAL
|
||||
|
||||
|
||||
|
||||
--- Builds a set of zones of defined zone prefixes.
|
||||
-- All the zones starting with the given prefixes will be included within the set.
|
||||
--- Builds a set of ZONE_GOALs that contain the given string in their name.
|
||||
-- **ATTENTION!** Bad naming convention as this **does not** filter only **prefixes** but all zones that **contain** the string.
|
||||
-- @param #SET_ZONE_GOAL self
|
||||
-- @param #string Prefixes The prefix of which the zone name starts with.
|
||||
-- @param #string Prefixes The string pattern(s) that needs to be contained in the zone name. Can also be passed as a `#table` of strings.
|
||||
-- @return #SET_ZONE_GOAL self
|
||||
function SET_ZONE_GOAL:FilterPrefixes( Prefixes )
|
||||
if not self.Filter.Prefixes then
|
||||
|
||||
@@ -1718,27 +1718,29 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
||||
self:T(string.format("Group %s is spawned on farp/ship/runway %s.", self.SpawnTemplatePrefix, SpawnAirbase:GetName()))
|
||||
nfree=SpawnAirbase:GetFreeParkingSpotsNumber(termtype, true)
|
||||
spots=SpawnAirbase:GetFreeParkingSpotsTable(termtype, true)
|
||||
--[[
|
||||
elseif Parkingdata~=nil then
|
||||
-- Parking data explicitly set by user as input parameter.
|
||||
nfree=#Parkingdata
|
||||
spots=Parkingdata
|
||||
]]
|
||||
else
|
||||
if ishelo then
|
||||
if termtype==nil then
|
||||
-- Helo is spawned. Try exclusive helo spots first.
|
||||
self:T(string.format("Helo group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.HelicopterOnly))
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterOnly, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits)
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterOnly, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata)
|
||||
nfree=#spots
|
||||
if nfree<nunits then
|
||||
-- Not enough helo ports. Let's try also other terminal types.
|
||||
self:T(string.format("Helo group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.HelicopterUsable))
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterUsable, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits)
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterUsable, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata)
|
||||
nfree=#spots
|
||||
end
|
||||
else
|
||||
-- No terminal type specified. We try all spots except shelters.
|
||||
self:T(string.format("Helo group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), termtype))
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, termtype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits)
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, termtype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata)
|
||||
nfree=#spots
|
||||
end
|
||||
else
|
||||
@@ -1747,29 +1749,30 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
||||
if isbomber or istransport or istanker or isawacs then
|
||||
-- First we fill the potentially bigger spots.
|
||||
self:T(string.format("Transport/bomber group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.OpenBig))
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.OpenBig, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits)
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.OpenBig, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata)
|
||||
nfree=#spots
|
||||
if nfree<nunits then
|
||||
-- Now we try the smaller ones.
|
||||
self:T(string.format("Transport/bomber group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.OpenMedOrBig))
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.OpenMedOrBig, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits)
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.OpenMedOrBig, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata)
|
||||
nfree=#spots
|
||||
end
|
||||
else
|
||||
self:T(string.format("Fighter group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.FighterAircraft))
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.FighterAircraft, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits)
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.FighterAircraft, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata)
|
||||
nfree=#spots
|
||||
end
|
||||
else
|
||||
-- Terminal type explicitly given.
|
||||
self:T(string.format("Plane group %s is at %s using terminal type %s.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), tostring(termtype)))
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, termtype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits)
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, termtype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata)
|
||||
nfree=#spots
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Get parking data.
|
||||
-- Debug: Get parking data.
|
||||
--[[
|
||||
local parkingdata=SpawnAirbase:GetParkingSpotsTable(termtype)
|
||||
self:T(string.format("Parking at %s, terminal type %s:", SpawnAirbase:GetName(), tostring(termtype)))
|
||||
for _,_spot in pairs(parkingdata) do
|
||||
@@ -1777,6 +1780,7 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
||||
SpawnAirbase:GetName(), _spot.TerminalID, _spot.TerminalType,tostring(_spot.Free),tostring(_spot.TOAC),_spot.TerminalID0,_spot.DistToRwy))
|
||||
end
|
||||
self:T(string.format("%s at %s: free parking spots = %d - number of units = %d", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), nfree, nunits))
|
||||
]]
|
||||
|
||||
-- Set this to true if not enough spots are available for emergency air start.
|
||||
local _notenough=false
|
||||
@@ -2144,27 +2148,29 @@ function SPAWN:ParkAircraft( SpawnAirbase, TerminalType, Parkingdata, SpawnIndex
|
||||
self:T(string.format("Group %s is spawned on farp/ship/runway %s.", self.SpawnTemplatePrefix, SpawnAirbase:GetName()))
|
||||
nfree=SpawnAirbase:GetFreeParkingSpotsNumber(termtype, true)
|
||||
spots=SpawnAirbase:GetFreeParkingSpotsTable(termtype, true)
|
||||
--[[
|
||||
elseif Parkingdata~=nil then
|
||||
-- Parking data explicitly set by user as input parameter.
|
||||
nfree=#Parkingdata
|
||||
spots=Parkingdata
|
||||
]]
|
||||
else
|
||||
if ishelo then
|
||||
if termtype==nil then
|
||||
-- Helo is spawned. Try exclusive helo spots first.
|
||||
self:T(string.format("Helo group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.HelicopterOnly))
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterOnly, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits)
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterOnly, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata)
|
||||
nfree=#spots
|
||||
if nfree<nunits then
|
||||
-- Not enough helo ports. Let's try also other terminal types.
|
||||
self:T(string.format("Helo group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.HelicopterUsable))
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterUsable, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits)
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterUsable, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata)
|
||||
nfree=#spots
|
||||
end
|
||||
else
|
||||
-- No terminal type specified. We try all spots except shelters.
|
||||
self:T(string.format("Helo group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), termtype))
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, termtype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits)
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, termtype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata)
|
||||
nfree=#spots
|
||||
end
|
||||
else
|
||||
@@ -2176,29 +2182,30 @@ function SPAWN:ParkAircraft( SpawnAirbase, TerminalType, Parkingdata, SpawnIndex
|
||||
if isbomber or istransport then
|
||||
-- First we fill the potentially bigger spots.
|
||||
self:T(string.format("Transport/bomber group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.OpenBig))
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.OpenBig, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits)
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.OpenBig, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata)
|
||||
nfree=#spots
|
||||
if nfree<nunits then
|
||||
-- Now we try the smaller ones.
|
||||
self:T(string.format("Transport/bomber group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.OpenMedOrBig))
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.OpenMedOrBig, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits)
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.OpenMedOrBig, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata)
|
||||
nfree=#spots
|
||||
end
|
||||
else
|
||||
self:T(string.format("Fighter group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.FighterAircraft))
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.FighterAircraft, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits)
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.FighterAircraft, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata)
|
||||
nfree=#spots
|
||||
end
|
||||
else
|
||||
-- Terminal type explicitly given.
|
||||
self:T(string.format("Plane group %s is at %s using terminal type %s.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), tostring(termtype)))
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, termtype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits)
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, termtype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata)
|
||||
nfree=#spots
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Get parking data.
|
||||
-- Debug: Get parking data.
|
||||
--[[
|
||||
local parkingdata=SpawnAirbase:GetParkingSpotsTable(termtype)
|
||||
self:T2(string.format("Parking at %s, terminal type %s:", SpawnAirbase:GetName(), tostring(termtype)))
|
||||
for _,_spot in pairs(parkingdata) do
|
||||
@@ -2206,6 +2213,7 @@ function SPAWN:ParkAircraft( SpawnAirbase, TerminalType, Parkingdata, SpawnIndex
|
||||
SpawnAirbase:GetName(), _spot.TerminalID, _spot.TerminalType,tostring(_spot.Free),tostring(_spot.TOAC),_spot.TerminalID0,_spot.DistToRwy))
|
||||
end
|
||||
self:T(string.format("%s at %s: free parking spots = %d - number of units = %d", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), nfree, nunits))
|
||||
]]
|
||||
|
||||
-- Set this to true if not enough spots are available for emergency air start.
|
||||
local _notenough=false
|
||||
|
||||
@@ -241,6 +241,20 @@ function SPAWNSTATIC:InitShape(StaticShape)
|
||||
return self
|
||||
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.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param #number Mass Mass of the cargo in kg.
|
||||
@@ -420,22 +434,49 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
|
||||
Template.offsets.angle=self.InitOffsetAngle and math.rad(self.InitOffsetAngle) or 0
|
||||
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.
|
||||
self.SpawnIndex = self.SpawnIndex + 1
|
||||
|
||||
-- Name of the spawned static.
|
||||
Template.name = self.InitStaticName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex)
|
||||
|
||||
-- Register the new static.
|
||||
--_DATABASE:_RegisterStaticTemplate(Template, self.CoalitionID, self.CategoryID, CountryID)
|
||||
_DATABASE:AddStatic(Template.name)
|
||||
-- Add and register the new static.
|
||||
local mystatic=_DATABASE:AddStatic(Template.name)
|
||||
|
||||
-- Debug output.
|
||||
self:T(Template)
|
||||
|
||||
-- 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
|
||||
|
||||
@@ -341,7 +341,7 @@ do
|
||||
-- @return #SPOT
|
||||
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
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
--
|
||||
-- ### Author: **funkyfranky**
|
||||
-- @module Core.Timer
|
||||
-- @image CORE_Timer.png
|
||||
-- @image Core_Scheduler.JPG
|
||||
|
||||
|
||||
--- TIMER class.
|
||||
@@ -19,6 +19,7 @@
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @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 uid Unique ID of the timer.
|
||||
-- @field #function func Timer function.
|
||||
-- @field #table para Parameters passed to the timer function.
|
||||
-- @field #number Tstart Relative start time in seconds.
|
||||
@@ -26,9 +27,10 @@
|
||||
-- @field #number dT Time interval between function calls in seconds.
|
||||
-- @field #number ncalls Counter of function calls.
|
||||
-- @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
|
||||
|
||||
--- *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 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).
|
||||
--
|
||||
@@ -62,20 +64,20 @@
|
||||
--
|
||||
-- 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 *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,
|
||||
--
|
||||
-- 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(1.0, nil, 10) -- Does not make sense as the function is only called once anyway.
|
||||
--
|
||||
-- ## 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()
|
||||
--
|
||||
@@ -110,7 +112,7 @@ _TIMERID=0
|
||||
|
||||
--- TIMER class version.
|
||||
-- @field #string version
|
||||
TIMER.version="0.1.0"
|
||||
TIMER.version="0.1.1"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
@@ -142,6 +144,9 @@ function TIMER:New(Function, ...)
|
||||
-- Number of function calls.
|
||||
self.ncalls=0
|
||||
|
||||
-- Not running yet.
|
||||
self.isrunning=false
|
||||
|
||||
-- Increase counter
|
||||
_TIMERID=_TIMERID+1
|
||||
|
||||
@@ -185,6 +190,9 @@ function TIMER:Start(Tstart, dT, Duration)
|
||||
-- Set log id.
|
||||
self.lid=string.format("TIMER UID=%d/%d | ", self.uid, self.tid)
|
||||
|
||||
-- Is now running.
|
||||
self.isrunning=true
|
||||
|
||||
-- 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)))
|
||||
|
||||
@@ -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))
|
||||
timer.removeFunction(self.tid)
|
||||
|
||||
-- Not running any more.
|
||||
self.isrunning=false
|
||||
|
||||
-- Remove DB entry.
|
||||
--_TIMERDB[self.uid]=nil
|
||||
|
||||
@@ -228,6 +239,13 @@ function TIMER:SetMaxFunctionCalls(Nmax)
|
||||
return self
|
||||
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.
|
||||
-- @param #TIMER self
|
||||
-- @param #number time DCS model time in seconds.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -295,6 +295,17 @@ do -- country
|
||||
-- @field QATAR
|
||||
-- @field OMAN
|
||||
-- @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
|
||||
|
||||
@@ -736,7 +747,66 @@ do -- 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
|
||||
--- 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
|
||||
-- @return #Unit.Desc
|
||||
|
||||
--- GROUND - Switch on/off radar emissions
|
||||
-- @function [parent=#Unit] enableEmission
|
||||
-- @param #Unit self
|
||||
-- @param #boolean switch
|
||||
|
||||
Unit = {} --#Unit
|
||||
|
||||
@@ -1163,7 +1237,7 @@ do -- Group
|
||||
-- @param #Group self
|
||||
-- @return #number
|
||||
|
||||
--- Returns initial size of the group. If some of the units will be destroyed, initial size of the group will not be changed. Initial size limits the unitNumber parameter for Group.getUnit() function.
|
||||
--- Returns initial size of the group. If some of the units will be destroyed, initial size of the group will not be changed; Initial size limits the unitNumber parameter for Group.getUnit() function.
|
||||
-- @function [parent=#Group] getInitialSize
|
||||
-- @param #Group self
|
||||
-- @return #number
|
||||
@@ -1178,6 +1252,11 @@ do -- Group
|
||||
-- @param #Group self
|
||||
-- @return #Controller
|
||||
|
||||
--- GROUND - Switch on/off radar emissions
|
||||
-- @function [parent=#Group] enableEmission
|
||||
-- @param #Group self
|
||||
-- @param #boolean switch
|
||||
|
||||
Group = {} --#Group
|
||||
|
||||
end -- Group
|
||||
@@ -1393,4 +1472,4 @@ do -- AI
|
||||
|
||||
AI = {} --#AI
|
||||
|
||||
end -- AI
|
||||
end -- AI
|
||||
|
||||
@@ -693,7 +693,7 @@ ARTY.db={
|
||||
|
||||
--- Arty script 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.
|
||||
-- @return #ARTY ARTY object or nil if group does not exist or is not a ground or naval group.
|
||||
function ARTY:NewFromCargoGroup(cargogroup, alias)
|
||||
BASE:F2({cargogroup=cargogroup, alias=alias})
|
||||
|
||||
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
|
||||
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
|
||||
end
|
||||
|
||||
@@ -2766,38 +2765,32 @@ function ARTY:onafterStatus(Controllable, From, Event, To)
|
||||
self:_EventFromTo("onafterStatus", Event, From, To)
|
||||
|
||||
-- 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.
|
||||
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
|
||||
|
||||
-- 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.
|
||||
if self.Debug then
|
||||
self:_StatusReport()
|
||||
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.
|
||||
if self:is("Moving") then
|
||||
self:T2(self.lid..string.format("%s: Moving", Controllable:GetName()))
|
||||
@@ -2884,7 +2877,7 @@ function ARTY:onafterStatus(Controllable, From, Event, To)
|
||||
end
|
||||
|
||||
-- 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.
|
||||
local gotsome=false
|
||||
@@ -2914,9 +2907,23 @@ function ARTY:onafterStatus(Controllable, From, Event, To)
|
||||
-- Call status again in ~10 sec.
|
||||
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
|
||||
self:E(self.lid..string.format("Arty group %s is not alive!", self.groupname))
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -4814,7 +4821,8 @@ function ARTY:_CheckShootingStarted()
|
||||
end
|
||||
|
||||
-- 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.
|
||||
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 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 )
|
||||
|
||||
-- Build the Lasing menu.
|
||||
|
||||
969
Moose Development/Moose/Functional/Mantis.lua
Normal file
969
Moose Development/Moose/Functional/Mantis.lua
Normal file
@@ -0,0 +1,969 @@
|
||||
--- **Functional** -- Modular, Automatic and Network capable Targeting and Interception System for Air Defenses
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- **MANTIS** - Moose derived Modular, Automatic and Network capable Targeting and Interception System
|
||||
-- Controls a network of SAM sites. Use detection to switch on the AA site closest to the enemy
|
||||
-- Leverage evasiveness from SEAD
|
||||
-- Leverage attack range setup added by DCS in 11/20
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Missions:
|
||||
--
|
||||
-- ### [MANTIS - Modular, Automatic and Network capable Targeting and Interception System](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/MTS%20-%20Mantis/MTS-010%20-%20Basic%20Mantis%20Demo)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author : **applevangelist **
|
||||
--
|
||||
-- @module Functional.Mantis
|
||||
-- @image Functional.Mantis.jpg
|
||||
|
||||
-- Date: Apr 2021
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
--- **MANTIS** class, extends #Core.Base#BASE
|
||||
-- @type MANTIS
|
||||
-- @field #string Classname
|
||||
-- @field #string name Name of this Mantis
|
||||
-- @field #string SAM_Templates_Prefix Prefix to build the #SET_GROUP for SAM sites
|
||||
-- @field Core.Set#SET_GROUP SAM_Group The SAM #SET_GROUP
|
||||
-- @field #string EWR_Templates_Prefix Prefix to build the #SET_GROUP for EWR group
|
||||
-- @field Core.Set#SET_GROUP EWR_Group The EWR #SET_GROUP
|
||||
-- @field Core.Set#SET_GROUP Adv_EWR_Group The EWR #SET_GROUP used for advanced mode
|
||||
-- @field #string HQ_Template_CC The ME name of the HQ object
|
||||
-- @field Wrapper.Group#GROUP HQ_CC The #GROUP object of the HQ
|
||||
-- @field #table SAM_Table Table of SAM sites
|
||||
-- @field #string lid Prefix for logging
|
||||
-- @field Functional.Detection#DETECTION_AREAS Detection The #DETECTION_AREAS object for EWR
|
||||
-- @field Functional.Detection#DETECTION_AREAS AWACS_Detection The #DETECTION_AREAS object for AWACS
|
||||
-- @field #boolean debug Switch on extra messages
|
||||
-- @field #boolean verbose Switch on extra logging
|
||||
-- @field #number checkradius Radius of the SAM sites
|
||||
-- @field #number grouping Radius to group detected objects
|
||||
-- @field #number acceptrange Radius of the EWR detection
|
||||
-- @field #number detectinterval Interval in seconds for the target detection
|
||||
-- @field #number engagerange Firing engage range of the SAMs, see [https://wiki.hoggitworld.com/view/DCS_option_engagementRange]
|
||||
-- @field #boolean autorelocate Relocate HQ and EWR groups in random intervals. Note: You need to select units for this which are *actually mobile*
|
||||
-- @field #boolean advanced Use advanced mode, will decrease reactivity of MANTIS, if HQ and/or EWR network dies. Set SAMs to RED state if both are dead. Requires usage of an HQ object
|
||||
-- @field #number adv_ratio Percentage to use for advanced mode, defaults to 100%
|
||||
-- @field #number adv_state Advanced mode state tracker
|
||||
-- @field #boolean advAwacs Boolean switch to use Awacs as a separate detection stream
|
||||
-- @field #number awacsrange Detection range of an optional Awacs unit
|
||||
-- @field #boolean UseEmOnOff Decide if we are using Emissions on/off (true) or AlarmState red/green (default)
|
||||
-- @field Functional.Shorad#SHORAD Shorad SHORAD Object, if available
|
||||
-- @field #boolean ShoradLink If true, #MANTIS has #SHORAD enabled
|
||||
-- @field #number ShoradTime Timer in seconds, how long #SHORAD will be active after a detection inside of the defense range
|
||||
-- @field #number ShoradActDistance Distance of an attacker in meters from a Mantis SAM site, on which Shorad will be switched on. Useful to not give away Shorad sites too early. Default 15km. Should be smaller than checkradius.
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
|
||||
--- *The worst thing that can happen to a good cause is, not to be skillfully attacked, but to be ineptly defended.* - Frédéric Bastiat
|
||||
--
|
||||
-- Simple Class for a more intelligent Air Defense System
|
||||
--
|
||||
-- #MANTIS
|
||||
-- Moose derived Modular, Automatic and Network capable Targeting and Interception System.
|
||||
-- Controls a network of SAM sites. Use detection to switch on the AA site closest to the enemy.
|
||||
-- Leverage evasiveness from @{Functional.Sead#SEAD}.
|
||||
-- Leverage attack range setup added by DCS in 11/20.
|
||||
--
|
||||
-- Set up your SAM sites in the mission editor. Name the groups with common prefix like "Red SAM".
|
||||
-- Set up your EWR system in the mission editor. Name the groups with common prefix like "Red EWR". Can be e.g. AWACS or a combination of AWACS and Search Radars like e.g. EWR 1L13 etc.
|
||||
-- [optional] Set up your HQ. Can be any group, e.g. a command vehicle.
|
||||
--
|
||||
-- # 1. Basic tactical considerations when setting up your SAM sites
|
||||
--
|
||||
-- ## 1.1 Radar systems and AWACS
|
||||
--
|
||||
-- Typically, your setup should consist of EWR (early warning) radars to detect and track targets, accompanied by AWACS if your scenario forsees that. Ensure that your EWR radars have a good coverage of the area you want to track.
|
||||
-- **Location** is of highest importantance here. Whilst AWACS in DCS has almost the "all seeing eye", EWR don't have that. Choose your location wisely, against a mountain backdrop or inside a valley even the best EWR system
|
||||
-- doesn't work well. Prefer higher-up locations with a good view; use F7 in-game to check where you actually placed your EWR and have a look around. Apart from the obvious choice, do also consider other radar units
|
||||
-- for this role, most have "SR" (search radar) or "STR" (search and track radar) in their names, use the encyclopedia to see what they actually do.
|
||||
--
|
||||
-- ## 1.2 SAM sites
|
||||
--
|
||||
-- Typically your SAM should cover all attack ranges. The closer the enemy gets, the more systems you will need to deploy to defend your location. Use a combination of long-range systems like the SA-10/11, midrange like SA-6 and short-range like
|
||||
-- SA-2 for defense (Patriot, Hawk, Gepard, Blindfire for the blue side). For close-up defense and defense against HARMs or low-flying aircraft, helicopters it is also advisable to deploy SA-15 TOR systems, Shilka, Strela and Tunguska units, as well as manpads (Think Gepard, Avenger, Chaparral,
|
||||
-- Linebacker, Roland systems for the blue side). If possible, overlap ranges for mutual coverage.
|
||||
--
|
||||
-- ## 1.3 Typical problems
|
||||
--
|
||||
-- Often times, people complain because the detection cannot "see" oncoming targets and/or Mantis switches on too late. Three typial problems here are
|
||||
--
|
||||
-- * bad placement of radar units,
|
||||
-- * overestimation how far units can "see" and
|
||||
-- * not taking into account that a SAM site will take (e.g for a SA-6) 30-40 seconds between switching to RED, acquiring the target and firing.
|
||||
--
|
||||
-- An attacker doing 350knots will cover ca 180meters/second or thus more than 6km until the SA-6 fires. Use triggers zones and the ruler in the missione editor to understand distances and zones. Take into account that the ranges given by the circles
|
||||
-- in the mission editor are absolute maximum ranges; in-game this is rather 50-75% of that depending on the system. Fiddle with placement and options to see what works best for your scenario, and remember **everything in here is in meters**.
|
||||
--
|
||||
-- # 2. Start up your MANTIS with a basic setting
|
||||
--
|
||||
-- `myredmantis = MANTIS:New("myredmantis","Red SAM","Red EWR",nil,"red",false)`
|
||||
-- `myredmantis:Start()`
|
||||
--
|
||||
-- [optional] Use
|
||||
--
|
||||
-- * `MANTIS:SetEWRGrouping(radius)`
|
||||
-- * `MANTIS:SetEWRRange(radius)`
|
||||
-- * `MANTIS:SetSAMRadius(radius)`
|
||||
-- * `MANTIS:SetDetectInterval(interval)`
|
||||
-- * `MANTIS:SetAutoRelocate(hq, ewr)`
|
||||
--
|
||||
-- before starting #MANTIS to fine-tune your setup.
|
||||
--
|
||||
-- If you want to use a separate AWACS unit (default detection range: 250km) to support your EWR system, use e.g. the following setup:
|
||||
--
|
||||
-- `mybluemantis = MANTIS:New("bluemantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")`
|
||||
-- `mybluemantis:Start()`
|
||||
--
|
||||
-- # 3. Default settings
|
||||
--
|
||||
-- By default, the following settings are active:
|
||||
--
|
||||
-- * SAM_Templates_Prefix = "Red SAM" - SAM site group names in the mission editor begin with "Red SAM"
|
||||
-- * EWR_Templates_Prefix = "Red EWR" - EWR group names in the mission editor begin with "Red EWR" - can also be combined with an AWACS unit
|
||||
-- * checkradius = 25000 (meters) - SAMs will engage enemy flights, if they are within a 25km around each SAM site - `MANTIS:SetSAMRadius(radius)`
|
||||
-- * grouping = 5000 (meters) - Detection (EWR) will group enemy flights to areas of 5km for tracking - `MANTIS:SetEWRGrouping(radius)`
|
||||
-- * acceptrange = 80000 (meters) - Detection (EWR) will on consider flights inside a 80km radius - `MANTIS:SetEWRRange(radius)`
|
||||
-- * detectinterval = 30 (seconds) - MANTIS will decide every 30 seconds which SAM to activate - `MANTIS:SetDetectInterval(interval)`
|
||||
-- * engagerange = 85 (percent) - SAMs will only fire if flights are inside of a 85% radius of their max firerange - `MANTIS:SetSAMRange(range)`
|
||||
-- * dynamic = false - Group filtering is set to once, i.e. newly added groups will not be part of the setup by default - `MANTIS:New(name,samprefix,ewrprefix,hq,coaltion,dynamic)`
|
||||
-- * autorelocate = false - HQ and (mobile) EWR system will not relocate in random intervals between 30mins and 1 hour - `MANTIS:SetAutoRelocate(hq, ewr)`
|
||||
-- * debug = false - Debugging reports on screen are set to off - `MANTIS:Debug(onoff)`
|
||||
--
|
||||
-- # 4. Advanced Mode
|
||||
--
|
||||
-- Advanced mode will *decrease* reactivity of MANTIS, if HQ and/or EWR network dies. Awacs is counted as one EWR unit. It will set SAMs to RED state if both are dead. Requires usage of an **HQ** object and the **dynamic** option.
|
||||
--
|
||||
-- E.g. `mymantis:SetAdvancedMode( true, 90 )`
|
||||
--
|
||||
-- Use this option if you want to make use of or allow advanced SEAD tactics.
|
||||
--
|
||||
-- # 5. Integrate SHORAD
|
||||
--
|
||||
-- You can also choose to integrate Mantis with @{Functional.Shorad#SHORAD} for protection against HARMs and AGMs. When SHORAD detects a missile fired at one of MANTIS' SAM sites, it will activate SHORAD systems in
|
||||
-- the given defense checkradius around that SAM site. Create a SHORAD object first, then integrate with MANTIS like so:
|
||||
--
|
||||
-- `local SamSet = SET_GROUP:New():FilterPrefixes("Blue SAM"):FilterCoalitions("blue"):FilterStart()`
|
||||
-- `myshorad = SHORAD:New("BlueShorad", "Blue SHORAD", SamSet, 22000, 600, "blue")`
|
||||
-- `-- now set up MANTIS`
|
||||
-- `mymantis = MANTIS:New("BlueMantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")`
|
||||
-- `mymantis:AddShorad(myshorad,720)`
|
||||
-- `mymantis:Start()`
|
||||
--
|
||||
-- and (optionally) remove the link later on with
|
||||
--
|
||||
-- `mymantis:RemoveShorad()`
|
||||
--
|
||||
-- @field #MANTIS
|
||||
MANTIS = {
|
||||
ClassName = "MANTIS",
|
||||
name = "mymantis",
|
||||
SAM_Templates_Prefix = "",
|
||||
SAM_Group = nil,
|
||||
EWR_Templates_Prefix = "",
|
||||
EWR_Group = nil,
|
||||
Adv_EWR_Group = nil,
|
||||
HQ_Template_CC = "",
|
||||
HQ_CC = nil,
|
||||
SAM_Table = {},
|
||||
lid = "",
|
||||
Detection = nil,
|
||||
AWACS_Detection = nil,
|
||||
debug = false,
|
||||
checkradius = 25000,
|
||||
grouping = 5000,
|
||||
acceptrange = 80000,
|
||||
detectinterval = 30,
|
||||
engagerange = 75,
|
||||
autorelocate = false,
|
||||
advanced = false,
|
||||
adv_ratio = 100,
|
||||
adv_state = 0,
|
||||
AWACS_Prefix = "",
|
||||
advAwacs = false,
|
||||
verbose = false,
|
||||
awacsrange = 250000,
|
||||
Shorad = nil,
|
||||
ShoradLink = false,
|
||||
ShoradTime = 600,
|
||||
ShoradActDistance = 15000,
|
||||
UseEmOnOff = true,
|
||||
}
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
-- MANTIS System
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
do
|
||||
--- Function to instantiate a new object of class MANTIS
|
||||
--@param #MANTIS self
|
||||
--@param #string name Name of this MANTIS for reporting
|
||||
--@param #string samprefix Prefixes for the SAM groups from the ME, e.g. all groups starting with "Red Sam..."
|
||||
--@param #string ewrprefix Prefixes for the EWR groups from the ME, e.g. all groups starting with "Red EWR..."
|
||||
--@param #string hq Group name of your HQ (optional)
|
||||
--@param #string coaltion Coalition side of your setup, e.g. "blue", "red" or "neutral"
|
||||
--@param #boolean dynamic Use constant (true) filtering or just filter once (false, default) (optional)
|
||||
--@param #string awacs Group name of your Awacs (optional)
|
||||
--@param #boolean EmOnOff Make MANTIS switch Emissions on and off instead of changing the alarm state between RED and GREEN (optional, deault true)
|
||||
--@return #MANTIS self
|
||||
--@usage Start up your MANTIS with a basic setting
|
||||
--
|
||||
-- `myredmantis = MANTIS:New("myredmantis","Red SAM","Red EWR",nil,"red",false)`
|
||||
-- `myredmantis:Start()`
|
||||
--
|
||||
-- [optional] Use
|
||||
--
|
||||
-- * `MANTIS:SetEWRGrouping(radius)`
|
||||
-- * `MANTIS:SetEWRRange(radius)`
|
||||
-- * `MANTIS:SetSAMRadius(radius)`
|
||||
-- * `MANTIS:SetDetectInterval(interval)`
|
||||
-- * `MANTIS:SetAutoRelocate(hq, ewr)`
|
||||
--
|
||||
-- before starting #MANTIS to fine-tune your setup.
|
||||
--
|
||||
-- If you want to use a separate AWACS unit (default detection range: 250km) to support your EWR system, use e.g. the following setup:
|
||||
--
|
||||
-- `mybluemantis = MANTIS:New("bluemantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")`
|
||||
-- `mybluemantis:Start()`
|
||||
--
|
||||
function MANTIS:New(name,samprefix,ewrprefix,hq,coaltion,dynamic,awacs, EmOnOff)
|
||||
|
||||
-- DONE: Create some user functions for these
|
||||
-- DONE: Make HQ useful
|
||||
-- DONE: Set SAMs to auto if EWR dies
|
||||
-- DONE: Refresh SAM table in dynamic mode
|
||||
-- DONE: Treat Awacs separately, since they might be >80km off site
|
||||
|
||||
self.name = name or "mymantis"
|
||||
self.SAM_Templates_Prefix = samprefix or "Red SAM"
|
||||
self.EWR_Templates_Prefix = ewrprefix or "Red EWR"
|
||||
self.HQ_Template_CC = hq or nil
|
||||
self.Coalition = coaltion or "red"
|
||||
self.SAM_Table = {}
|
||||
self.dynamic = dynamic or false
|
||||
self.checkradius = 25000
|
||||
self.grouping = 5000
|
||||
self.acceptrange = 80000
|
||||
self.detectinterval = 30
|
||||
self.engagerange = 75
|
||||
self.autorelocate = false
|
||||
self.autorelocateunits = { HQ = false, EWR = false}
|
||||
self.advanced = false
|
||||
self.adv_ratio = 100
|
||||
self.adv_state = 0
|
||||
self.verbose = false
|
||||
self.Adv_EWR_Group = nil
|
||||
self.AWACS_Prefix = awacs or nil
|
||||
self.awacsrange = 250000 --DONE: 250km, User Function to change
|
||||
self.Shorad = nil
|
||||
self.ShoradLink = false
|
||||
self.ShoradTime = 600
|
||||
self.ShoradActDistance = 15000
|
||||
-- TODO: add emissions on/off when available .... in 2 weeks
|
||||
if EmOnOff then
|
||||
if EmOnOff == false then
|
||||
self.UseEmOnOff = false
|
||||
end
|
||||
end
|
||||
|
||||
if type(awacs) == "string" then
|
||||
self.advAwacs = true
|
||||
else
|
||||
self.advAwacs = false
|
||||
end
|
||||
|
||||
-- Inherit everything from BASE class.
|
||||
local self = BASE:Inherit(self, BASE:New()) -- #MANTIS
|
||||
|
||||
-- Set the string id for output to DCS.log file.
|
||||
self.lid=string.format("MANTIS %s | ", self.name)
|
||||
|
||||
-- Debug trace.
|
||||
if self.debug then
|
||||
BASE:TraceOnOff(true)
|
||||
BASE:TraceClass(self.ClassName)
|
||||
--BASE:TraceClass("SEAD")
|
||||
BASE:TraceLevel(1)
|
||||
end
|
||||
|
||||
if self.dynamic then
|
||||
-- Set SAM SET_GROUP
|
||||
self.SAM_Group = SET_GROUP:New():FilterPrefixes(self.SAM_Templates_Prefix):FilterCoalitions(self.Coalition):FilterStart()
|
||||
-- Set EWR SET_GROUP
|
||||
self.EWR_Group = SET_GROUP:New():FilterPrefixes({self.SAM_Templates_Prefix,self.EWR_Templates_Prefix}):FilterCoalitions(self.Coalition):FilterStart()
|
||||
else
|
||||
-- Set SAM SET_GROUP
|
||||
self.SAM_Group = SET_GROUP:New():FilterPrefixes(self.SAM_Templates_Prefix):FilterCoalitions(self.Coalition):FilterOnce()
|
||||
-- Set EWR SET_GROUP
|
||||
self.EWR_Group = SET_GROUP:New():FilterPrefixes({self.SAM_Templates_Prefix,self.EWR_Templates_Prefix}):FilterCoalitions(self.Coalition):FilterOnce()
|
||||
end
|
||||
|
||||
-- set up CC
|
||||
if self.HQ_Template_CC then
|
||||
self.HQ_CC = GROUP:FindByName(self.HQ_Template_CC)
|
||||
end
|
||||
|
||||
-- @field #string version
|
||||
self.version="0.4.1"
|
||||
self:I(string.format("***** Starting MANTIS Version %s *****", self.version))
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
-- MANTIS helper functions
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
--- [internal] Function to get the self.SAM_Table
|
||||
-- @param #MANTIS self
|
||||
-- @return #table table
|
||||
function MANTIS:_GetSAMTable()
|
||||
return self.SAM_Table
|
||||
end
|
||||
|
||||
--- [internal] Function to set the self.SAM_Table
|
||||
-- @param #MANTIS self
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:_SetSAMTable(table)
|
||||
self.SAM_Table = table
|
||||
return self
|
||||
end
|
||||
|
||||
--- Function to set the grouping radius of the detection in meters
|
||||
-- @param #MANTIS self
|
||||
-- @param #number radius Radius upon which detected objects will be grouped
|
||||
function MANTIS:SetEWRGrouping(radius)
|
||||
local radius = radius or 5000
|
||||
self.grouping = radius
|
||||
end
|
||||
|
||||
--- Function to set the detection radius of the EWR in meters
|
||||
-- @param #MANTIS self
|
||||
-- @param #number radius Radius of the EWR detection zone
|
||||
function MANTIS:SetEWRRange(radius)
|
||||
local radius = radius or 80000
|
||||
self.acceptrange = radius
|
||||
end
|
||||
|
||||
--- Function to set switch-on/off zone for the SAM sites in meters
|
||||
-- @param #MANTIS self
|
||||
-- @param #number radius Radius of the firing zone
|
||||
function MANTIS:SetSAMRadius(radius)
|
||||
local radius = radius or 25000
|
||||
self.checkradius = radius
|
||||
end
|
||||
|
||||
--- Function to set SAM firing engage range, 0-100 percent, e.g. 75
|
||||
-- @param #MANTIS self
|
||||
-- @param #number range Percent of the max fire range
|
||||
function MANTIS:SetSAMRange(range)
|
||||
local range = range or 75
|
||||
if range < 0 or range > 100 then
|
||||
range = 75
|
||||
end
|
||||
self.engagerange = range
|
||||
end
|
||||
|
||||
--- Function to set a new SAM firing engage range, use this method to adjust range while running MANTIS, e.g. for different setups day and night
|
||||
-- @param #MANTIS self
|
||||
-- @param #number range Percent of the max fire range
|
||||
function MANTIS:SetNewSAMRangeWhileRunning(range)
|
||||
local range = range or 75
|
||||
if range < 0 or range > 100 then
|
||||
range = 75
|
||||
end
|
||||
self.engagerange = range
|
||||
self:_RefreshSAMTable()
|
||||
self.mysead.EngagementRange = range
|
||||
end
|
||||
|
||||
--- Function to set switch-on/off the debug state
|
||||
-- @param #MANTIS self
|
||||
-- @param #boolean onoff Set true to switch on
|
||||
function MANTIS:Debug(onoff)
|
||||
local onoff = onoff or false
|
||||
self.debug = onoff
|
||||
end
|
||||
|
||||
--- Function to get the HQ object for further use
|
||||
-- @param #MANTIS self
|
||||
-- @return Wrapper.GROUP#GROUP The HQ #GROUP object or *nil* if it doesn't exist
|
||||
function MANTIS:GetCommandCenter()
|
||||
if self.HQ_CC then
|
||||
return self.HQ_CC
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
--- Function to set separate AWACS detection instance
|
||||
-- @param #MANTIS self
|
||||
-- @param #string prefix Name of the AWACS group in the mission editor
|
||||
function MANTIS:SetAwacs(prefix)
|
||||
if prefix ~= nil then
|
||||
if type(prefix) == "string" then
|
||||
self.AWACS_Prefix = prefix
|
||||
self.advAwacs = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Function to set AWACS detection range. Defaults to 250.000m (250km) - use **before** starting your Mantis!
|
||||
-- @param #MANTIS self
|
||||
-- @param #number range Detection range of the AWACS group
|
||||
function MANTIS:SetAwacsRange(range)
|
||||
local range = range or 250000
|
||||
self.awacsrange = range
|
||||
end
|
||||
|
||||
--- Function to set the HQ object for further use
|
||||
-- @param #MANTIS self
|
||||
-- @param Wrapper.GROUP#GROUP group The #GROUP object to be set as HQ
|
||||
function MANTIS:SetCommandCenter(group)
|
||||
local group = group or nil
|
||||
if group ~= nil then
|
||||
if type(group) == "string" then
|
||||
self.HQ_CC = GROUP:FindByName(group)
|
||||
self.HQ_Template_CC = group
|
||||
else
|
||||
self.HQ_CC = group
|
||||
self.HQ_Template_CC = group:GetName()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Function to set the detection interval
|
||||
-- @param #MANTIS self
|
||||
-- @param #number interval The interval in seconds
|
||||
function MANTIS:SetDetectInterval(interval)
|
||||
local interval = interval or 30
|
||||
self.detectinterval = interval
|
||||
end
|
||||
|
||||
--- Function to set Advanded Mode
|
||||
-- @param #MANTIS self
|
||||
-- @param #boolean onoff If true, will activate Advanced Mode
|
||||
-- @param #number ratio [optional] Percentage to use for advanced mode, defaults to 100%
|
||||
-- @usage Advanced mode will *decrease* reactivity of MANTIS, if HQ and/or EWR network dies. Set SAMs to RED state if both are dead. Requires usage of an **HQ** object and the **dynamic** option.
|
||||
-- E.g. `mymantis:SetAdvancedMode(true, 90)`
|
||||
function MANTIS:SetAdvancedMode(onoff, ratio)
|
||||
self:F({onoff, ratio})
|
||||
local onoff = onoff or false
|
||||
local ratio = ratio or 100
|
||||
if (type(self.HQ_Template_CC) == "string") and onoff and self.dynamic then
|
||||
self.adv_ratio = ratio
|
||||
self.advanced = true
|
||||
self.adv_state = 0
|
||||
self.Adv_EWR_Group = SET_GROUP:New():FilterPrefixes(self.EWR_Templates_Prefix):FilterCoalitions(self.Coalition):FilterStart()
|
||||
env.info(string.format("***** Starting Advanced Mode MANTIS Version %s *****", self.version))
|
||||
else
|
||||
local text = self.lid.." Advanced Mode requires a HQ and dynamic to be set. Revisit your MANTIS:New() statement to add both."
|
||||
local m= MESSAGE:New(text,10,"MANTIS",true):ToAll()
|
||||
BASE:E(text)
|
||||
end
|
||||
end
|
||||
|
||||
--- Set using Emissions on/off instead of changing alarm state
|
||||
-- @param #MANTIS self
|
||||
-- @param #boolean switch Decide if we are changing alarm state or Emission state
|
||||
function MANTIS:SetUsingEmOnOff(switch)
|
||||
self.UseEmOnOff = switch or false
|
||||
end
|
||||
|
||||
--- [Internal] Function to check if HQ is alive
|
||||
-- @param #MANTIS self
|
||||
-- @return #boolean True if HQ is alive, else false
|
||||
function MANTIS:_CheckHQState()
|
||||
local text = self.lid.." Checking HQ State"
|
||||
self:T(text)
|
||||
local m= MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
if self.verbose then env.info(text) end
|
||||
-- start check
|
||||
if self.advanced then
|
||||
local hq = self.HQ_Template_CC
|
||||
local hqgrp = GROUP:FindByName(hq)
|
||||
if hqgrp then
|
||||
if hqgrp:IsAlive() then -- ok we're on, hq exists and as alive
|
||||
env.info(self.lid.." HQ is alive!")
|
||||
return true
|
||||
else
|
||||
env.info(self.lid.." HQ is dead!")
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- [Internal] Function to check if EWR is (at least partially) alive
|
||||
-- @param #MANTIS self
|
||||
-- @return #boolean True if EWR is alive, else false
|
||||
function MANTIS:_CheckEWRState()
|
||||
local text = self.lid.." Checking EWR State"
|
||||
self:F(text)
|
||||
local m= MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
if self.verbose then env.info(text) end
|
||||
-- start check
|
||||
if self.advanced then
|
||||
local EWR_Group = self.Adv_EWR_Group
|
||||
--local EWR_Set = EWR_Group.Set
|
||||
local nalive = EWR_Group:CountAlive()
|
||||
if self.advAwacs then
|
||||
local awacs = GROUP:FindByName(self.AWACS_Prefix)
|
||||
if awacs ~= nil then
|
||||
if awacs:IsAlive() then
|
||||
nalive = nalive+1
|
||||
end
|
||||
end
|
||||
end
|
||||
env.info(self.lid..string.format(" No of EWR alive is %d", nalive))
|
||||
if nalive > 0 then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- [Internal] Function to determine state of the advanced mode
|
||||
-- @param #MANTIS self
|
||||
-- @return #number Newly calculated interval
|
||||
-- @return #number Previous state for tracking 0, 1, or 2
|
||||
function MANTIS:_CheckAdvState()
|
||||
local text = self.lid.." Checking Advanced State"
|
||||
self:F(text)
|
||||
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
if self.verbose then env.info(text) end
|
||||
-- start check
|
||||
local currstate = self.adv_state -- save curr state for comparison later
|
||||
local EWR_State = self:_CheckEWRState()
|
||||
local HQ_State = self:_CheckHQState()
|
||||
-- set state
|
||||
if EWR_State and HQ_State then -- both alive
|
||||
self.adv_state = 0 --everything is fine
|
||||
elseif EWR_State or HQ_State then -- one alive
|
||||
self.adv_state = 1 --slow down level 1
|
||||
else -- none alive
|
||||
self.adv_state = 2 --slow down level 2
|
||||
end
|
||||
-- calculate new detectioninterval
|
||||
local interval = self.detectinterval -- e.g. 30
|
||||
local ratio = self.adv_ratio / 100 -- e.g. 80/100 = 0.8
|
||||
ratio = ratio * self.adv_state -- e.g 0.8*2 = 1.6
|
||||
local newinterval = interval + (interval * ratio) -- e.g. 30+(30*1.6) = 78
|
||||
local text = self.lid..string.format(" Calculated OldState/NewState/Interval: %d / %d / %d", currstate, self.adv_state, newinterval)
|
||||
self:F(text)
|
||||
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
if self.verbose then env.info(text) end
|
||||
return newinterval, currstate
|
||||
end
|
||||
|
||||
--- Function to set autorelocation for HQ and EWR objects. Note: Units must be actually mobile in DCS!
|
||||
-- @param #MANTIS self
|
||||
-- @param #boolean hq If true, will relocate HQ object
|
||||
-- @param #boolean ewr If true, will relocate EWR objects
|
||||
function MANTIS:SetAutoRelocate(hq, ewr)
|
||||
self:F({hq, ewr})
|
||||
local hqrel = hq or false
|
||||
local ewrel = ewr or false
|
||||
if hqrel or ewrel then
|
||||
self.autorelocate = true
|
||||
self.autorelocateunits = { HQ = hqrel, EWR = ewrel }
|
||||
self:T({self.autorelocate, self.autorelocateunits})
|
||||
end
|
||||
end
|
||||
|
||||
--- [Internal] Function to execute the relocation
|
||||
-- @param #MANTIS self
|
||||
function MANTIS:_RelocateGroups()
|
||||
self:T(self.lid.." Relocating Groups")
|
||||
local text = self.lid.." Relocating Groups"
|
||||
local m= MESSAGE:New(text,10,"MANTIS",true):ToAllIf(self.debug)
|
||||
if self.verbose then env.info(text) end
|
||||
if self.autorelocate then
|
||||
-- relocate HQ
|
||||
if self.autorelocateunits.HQ and self.HQ_CC then --only relocate if HQ exists
|
||||
local _hqgrp = self.HQ_CC
|
||||
self:T(self.lid.." Relocating HQ")
|
||||
local text = self.lid.." Relocating HQ"
|
||||
local m= MESSAGE:New(text,10,"MANTIS"):ToAll()
|
||||
_hqgrp:RelocateGroundRandomInRadius(20,500,true,true)
|
||||
end
|
||||
--relocate EWR
|
||||
-- TODO: maybe dependent on AlarmState? Observed: SA11 SR only relocates if no objects in reach
|
||||
if self.autorelocateunits.EWR then
|
||||
-- get EWR Group
|
||||
local EWR_GRP = SET_GROUP:New():FilterPrefixes(self.EWR_Templates_Prefix):FilterCoalitions(self.Coalition):FilterOnce()
|
||||
local EWR_Grps = EWR_GRP.Set --table of objects in SET_GROUP
|
||||
for _,_grp in pairs (EWR_Grps) do
|
||||
if _grp:IsGround() then
|
||||
self:T(self.lid.." Relocating EWR ".._grp:GetName())
|
||||
local text = self.lid.." Relocating EWR ".._grp:GetName()
|
||||
local m= MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
if self.verbose then env.info(text) end
|
||||
_grp:RelocateGroundRandomInRadius(20,500,true,true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- (Internal) Function to check if any object is in the given SAM zone
|
||||
-- @param #MANTIS self
|
||||
-- @param #table dectset Table of coordinates of detected items
|
||||
-- @param samcoordinate Core.Point#COORDINATE Coordinate object.
|
||||
-- @return #boolean True if in any zone, else false
|
||||
-- @return #number Distance Target distance in meters or zero when no object is in zone
|
||||
function MANTIS:CheckObjectInZone(dectset, samcoordinate)
|
||||
self:F(self.lid.."CheckObjectInZone Called")
|
||||
-- check if non of the coordinate is in the given defense zone
|
||||
local radius = self.checkradius
|
||||
local set = dectset
|
||||
for _,_coord in pairs (set) do
|
||||
local coord = _coord -- get current coord to check
|
||||
-- output for cross-check
|
||||
local dectstring = coord:ToStringLLDMS()
|
||||
local samstring = samcoordinate:ToStringLLDMS()
|
||||
local targetdistance = samcoordinate:DistanceFromPointVec2(coord)
|
||||
local text = string.format("Checking SAM at % s - Distance %d m - Target %s", samstring, targetdistance, dectstring)
|
||||
local m = MESSAGE:New(text,10,"Check"):ToAllIf(self.debug)
|
||||
if self.verbose then env.info(self.lid..text) end
|
||||
-- end output to cross-check
|
||||
if targetdistance <= radius then
|
||||
return true, targetdistance
|
||||
end
|
||||
end
|
||||
return false, 0
|
||||
end
|
||||
|
||||
--- (Internal) Function to start the detection via EWR groups
|
||||
-- @param #MANTIS self
|
||||
-- @return Functional.Detection #DETECTION_AREAS The running detection set
|
||||
function MANTIS:StartDetection()
|
||||
self:F(self.lid.."Starting Detection")
|
||||
|
||||
-- start detection
|
||||
local groupset = self.EWR_Group
|
||||
local grouping = self.grouping or 5000
|
||||
local acceptrange = self.acceptrange or 80000
|
||||
local interval = self.detectinterval or 60
|
||||
|
||||
--@param Functional.Detection #DETECTION_AREAS _MANTISdetection [internal] The MANTIS detection object
|
||||
_MANTISdetection = DETECTION_AREAS:New( groupset, grouping ) --[internal] Grouping detected objects to 5000m zones
|
||||
_MANTISdetection:FilterCategories({ Unit.Category.AIRPLANE, Unit.Category.HELICOPTER })
|
||||
_MANTISdetection:SetAcceptRange(acceptrange)
|
||||
_MANTISdetection:SetRefreshTimeInterval(interval)
|
||||
_MANTISdetection:Start()
|
||||
|
||||
function _MANTISdetection:OnAfterDetectedItem(From,Event,To,DetectedItem)
|
||||
--BASE:I( { From, Event, To, DetectedItem })
|
||||
local debug = false
|
||||
if DetectedItem.IsDetected and debug then
|
||||
local Coordinate = DetectedItem.Coordinate -- Core.Point#COORDINATE
|
||||
local text = "MANTIS: Detection at "..Coordinate:ToStringLLDMS()
|
||||
local m = MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
end
|
||||
end
|
||||
return _MANTISdetection
|
||||
end
|
||||
|
||||
--- (Internal) Function to start the detection via AWACS if defined as separate
|
||||
-- @param #MANTIS self
|
||||
-- @return Functional.Detection #DETECTION_AREAS The running detection set
|
||||
function MANTIS:StartAwacsDetection()
|
||||
self:F(self.lid.."Starting Awacs Detection")
|
||||
|
||||
-- start detection
|
||||
local group = self.AWACS_Prefix
|
||||
local groupset = SET_GROUP:New():FilterPrefixes(group):FilterCoalitions(self.Coalition):FilterStart()
|
||||
local grouping = self.grouping or 5000
|
||||
--local acceptrange = self.acceptrange or 80000
|
||||
local interval = self.detectinterval or 60
|
||||
|
||||
--@param Functional.Detection #DETECTION_AREAS _MANTISdetection [internal] The MANTIS detection object
|
||||
_MANTISAwacs = DETECTION_AREAS:New( groupset, grouping ) --[internal] Grouping detected objects to 5000m zones
|
||||
_MANTISAwacs:FilterCategories({ Unit.Category.AIRPLANE, Unit.Category.HELICOPTER })
|
||||
_MANTISAwacs:SetAcceptRange(self.awacsrange) --250km
|
||||
_MANTISAwacs:SetRefreshTimeInterval(interval)
|
||||
_MANTISAwacs:Start()
|
||||
|
||||
function _MANTISAwacs:OnAfterDetectedItem(From,Event,To,DetectedItem)
|
||||
--BASE:I( { From, Event, To, DetectedItem })
|
||||
local debug = false
|
||||
if DetectedItem.IsDetected and debug then
|
||||
local Coordinate = DetectedItem.Coordinate -- Core.Point#COORDINATE
|
||||
local text = "Awacs Detection at "..Coordinate:ToStringLLDMS()
|
||||
local m = MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
end
|
||||
end
|
||||
return _MANTISAwacs
|
||||
end
|
||||
|
||||
--- (Internal) Function to set the SAM start state
|
||||
-- @param #MANTIS self
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:SetSAMStartState()
|
||||
-- DONE: if using dynamic filtering, update SAM_Table and the (active) SEAD groups, pull req #1405/#1406
|
||||
self:F(self.lid.."Setting SAM Start States")
|
||||
-- get SAM Group
|
||||
local SAM_SET = self.SAM_Group
|
||||
local SAM_Grps = SAM_SET.Set --table of objects
|
||||
local SAM_Tbl = {} -- table of SAM defense zones
|
||||
local SEAD_Grps = {} -- table of SAM names to make evasive
|
||||
local engagerange = self.engagerange -- firing range in % of max
|
||||
--cycle through groups and set alarm state etc
|
||||
for _i,_group in pairs (SAM_Grps) do
|
||||
local group = _group
|
||||
-- TODO: add emissions on/off
|
||||
if self.UseEmOnOff then
|
||||
group:EnableEmission(false)
|
||||
--group:SetAIOff()
|
||||
else
|
||||
group:OptionAlarmStateGreen() -- AI off
|
||||
end
|
||||
group:SetOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION,engagerange) --default engagement will be 75% of firing range
|
||||
if group:IsGround() then
|
||||
local grpname = group:GetName()
|
||||
local grpcoord = group:GetCoordinate()
|
||||
table.insert( SAM_Tbl, {grpname, grpcoord})
|
||||
table.insert( SEAD_Grps, grpname )
|
||||
end
|
||||
end
|
||||
self.SAM_Table = SAM_Tbl
|
||||
-- make SAMs evasive
|
||||
local mysead = SEAD:New( SEAD_Grps )
|
||||
mysead:SetEngagementRange(engagerange)
|
||||
self.mysead = mysead
|
||||
return self
|
||||
end
|
||||
|
||||
--- (Internal) Function to update SAM table and SEAD state
|
||||
-- @param #MANTIS self
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:_RefreshSAMTable()
|
||||
self:F(self.lid.."Setting SAM Start States")
|
||||
-- Requires SEAD 0.2.2 or better
|
||||
-- get SAM Group
|
||||
local SAM_SET = self.SAM_Group
|
||||
local SAM_Grps = SAM_SET.Set --table of objects
|
||||
local SAM_Tbl = {} -- table of SAM defense zones
|
||||
local SEAD_Grps = {} -- table of SAM names to make evasive
|
||||
local engagerange = self.engagerange -- firing range in % of max
|
||||
--cycle through groups and set alarm state etc
|
||||
for _i,_group in pairs (SAM_Grps) do
|
||||
local group = _group
|
||||
group:SetOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION,engagerange) --engagement will be 75% of firing range
|
||||
if group:IsGround() then
|
||||
local grpname = group:GetName()
|
||||
local grpcoord = group:GetCoordinate()
|
||||
table.insert( SAM_Tbl, {grpname, grpcoord}) -- make the table lighter, as I don't really use the zone here
|
||||
table.insert( SEAD_Grps, grpname )
|
||||
end
|
||||
end
|
||||
self.SAM_Table = SAM_Tbl
|
||||
-- make SAMs evasive
|
||||
if self.mysead ~= nil then
|
||||
local mysead = self.mysead
|
||||
mysead:UpdateSet( SEAD_Grps )
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Function to link up #MANTIS with a #SHORAD installation
|
||||
-- @param #MANTIS self
|
||||
-- @param Functional.Shorad#SHORAD Shorad The #SHORAD object
|
||||
-- @param #number Shoradtime Number of seconds #SHORAD stays active post wake-up
|
||||
function MANTIS:AddShorad(Shorad,Shoradtime)
|
||||
local Shorad = Shorad or nil
|
||||
local ShoradTime = Shoradtime or 600
|
||||
local ShoradLink = true
|
||||
if Shorad:IsInstanceOf("SHORAD") then
|
||||
self.ShoradLink = ShoradLink
|
||||
self.Shorad = Shorad --#SHORAD
|
||||
self.ShoradTime = Shoradtime -- #number
|
||||
end
|
||||
end
|
||||
|
||||
--- Function to unlink #MANTIS from a #SHORAD installation
|
||||
-- @param #MANTIS self
|
||||
function MANTIS:RemoveShorad()
|
||||
self.ShoradLink = false
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
-- MANTIS main functions
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
--- Function to set the SAM start state
|
||||
-- @param #MANTIS self
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:Start()
|
||||
self:F(self.lid.."Starting MANTIS")
|
||||
self:SetSAMStartState()
|
||||
self.Detection = self:StartDetection()
|
||||
if self.advAwacs then
|
||||
self.AWACS_Detection = self:StartAwacsDetection()
|
||||
end
|
||||
-- detection function
|
||||
local function check(detection)
|
||||
--get detected set
|
||||
local detset = detection:GetDetectedItemCoordinates()
|
||||
self:F("Check:", {detset})
|
||||
-- randomly update SAM Table
|
||||
local rand = math.random(1,100)
|
||||
if rand > 65 then -- 1/3 of cases
|
||||
self:_RefreshSAMTable()
|
||||
end
|
||||
-- switch SAMs on/off if (n)one of the detected groups is inside their reach
|
||||
local samset = self:_GetSAMTable() -- table of i.1=names, i.2=coordinates
|
||||
for _,_data in pairs (samset) do
|
||||
local samcoordinate = _data[2]
|
||||
local name = _data[1]
|
||||
local samgroup = GROUP:FindByName(name)
|
||||
local IsInZone, Distance = self:CheckObjectInZone(detset, samcoordinate)
|
||||
if IsInZone then --check any target in zone
|
||||
if samgroup:IsAlive() then
|
||||
-- switch on SAM
|
||||
if self.UseEmOnOff then
|
||||
-- TODO: add emissions on/off
|
||||
--samgroup:SetAIOn()
|
||||
samgroup:EnableEmission(true)
|
||||
end
|
||||
samgroup:OptionAlarmStateRed()
|
||||
-- link in to SHORAD if available
|
||||
-- DONE: Test integration fully
|
||||
if self.ShoradLink and Distance < self.ShoradActDistance then -- don't give SHORAD position away too early
|
||||
local Shorad = self.Shorad
|
||||
local radius = self.checkradius
|
||||
local ontime = self.ShoradTime
|
||||
Shorad:WakeUpShorad(name, radius, ontime)
|
||||
end
|
||||
-- debug output
|
||||
local text = string.format("SAM %s switched to alarm state RED!", name)
|
||||
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
if self.verbose then env.info(self.lid..text) end
|
||||
end --end alive
|
||||
else
|
||||
if samgroup:IsAlive() then
|
||||
-- switch off SAM
|
||||
if self.UseEmOnOff then
|
||||
-- TODO: add emissions on/off
|
||||
samgroup:EnableEmission(false)
|
||||
--samgroup:SetAIOff()
|
||||
else
|
||||
samgroup:OptionAlarmStateGreen()
|
||||
end
|
||||
--samgroup:OptionROEWeaponFree()
|
||||
--samgroup:SetAIOn()
|
||||
local text = string.format("SAM %s switched to alarm state GREEN!", name)
|
||||
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
if self.verbose then env.info(self.lid..text) end
|
||||
end --end alive
|
||||
end --end check
|
||||
end --for for loop
|
||||
end --end function
|
||||
-- relocation relay function
|
||||
local function relocate()
|
||||
self:_RelocateGroups()
|
||||
end
|
||||
-- check advanced state
|
||||
local function checkadvstate()
|
||||
local interval, oldstate = self:_CheckAdvState()
|
||||
local newstate = self.adv_state
|
||||
if newstate ~= oldstate then
|
||||
-- deal with new state
|
||||
if newstate == 2 then
|
||||
-- switch alarm state RED
|
||||
if self.MantisTimer.isrunning then
|
||||
self.MantisTimer:Stop()
|
||||
self.MantisTimer.isrunning = false
|
||||
end -- stop Awacs timer
|
||||
if self.MantisATimer.isrunning then
|
||||
self.MantisATimer:Stop()
|
||||
self.MantisATimer.isrunning = false
|
||||
end -- stop timer
|
||||
local samset = self:_GetSAMTable() -- table of i.1=names, i.2=coordinates
|
||||
for _,_data in pairs (samset) do
|
||||
local name = _data[1]
|
||||
local samgroup = GROUP:FindByName(name)
|
||||
if samgroup:IsAlive() then
|
||||
if self.UseEmOnOff then
|
||||
-- TODO: add emissions on/off
|
||||
--samgroup:SetAIOn()
|
||||
samgroup:EnableEmission(true)
|
||||
end
|
||||
samgroup:OptionAlarmStateRed()
|
||||
end -- end alive
|
||||
end -- end for loop
|
||||
elseif newstate <= 1 then
|
||||
-- change MantisTimer to slow down or speed up
|
||||
if self.MantisTimer.isrunning then
|
||||
self.MantisTimer:Stop()
|
||||
self.MantisTimer.isrunning = false
|
||||
end
|
||||
if self.MantisATimer.isrunning then
|
||||
self.MantisATimer:Stop()
|
||||
self.MantisATimer.isrunning = false
|
||||
end
|
||||
self.MantisTimer = TIMER:New(check,self.Detection)
|
||||
self.MantisTimer:Start(5,interval,nil)
|
||||
self.MantisTimer.isrunning = true
|
||||
if self.advAwacs then
|
||||
self.MantisATimer = TIMER:New(check,self.AWACS_Detection)
|
||||
self.MantisATimer:Start(15,interval,nil)
|
||||
self.MantisATimer.isrunning = true
|
||||
end
|
||||
end
|
||||
end -- end newstate vs oldstate
|
||||
end
|
||||
-- timers to run the system
|
||||
local interval = self.detectinterval
|
||||
self.MantisTimer = TIMER:New(check,self.Detection)
|
||||
self.MantisTimer:Start(5,interval,nil)
|
||||
self.MantisTimer.isrunning = true
|
||||
-- Awacs timer
|
||||
if self.advAwacs then
|
||||
self.MantisATimer = TIMER:New(check,self.AWACS_Detection)
|
||||
self.MantisATimer:Start(15,interval,nil)
|
||||
self.MantisATimer.isrunning = true
|
||||
end
|
||||
-- timer to relocate HQ and EWR
|
||||
if self.autorelocate then
|
||||
local relointerval = math.random(1800,3600) -- random between 30 and 60 mins
|
||||
self.MantisReloTimer = TIMER:New(relocate)
|
||||
self.MantisReloTimer:Start(relointerval,relointerval,nil)
|
||||
end
|
||||
-- timer for advanced state check
|
||||
if self.advanced then
|
||||
self.MantisAdvTimer = TIMER:New(checkadvstate)
|
||||
self.MantisAdvTimer:Start(30,interval*5,nil)
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Function to stop MANTIS
|
||||
-- @param #MANTIS self
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:Stop()
|
||||
if self.MantisTimer.isrunning then
|
||||
self.MantisTimer:Stop()
|
||||
end
|
||||
if self.MantisATimer.isrunning then
|
||||
self.MantisATimer:Stop()
|
||||
end
|
||||
if self.autorelocate then
|
||||
self.MantisReloTimer:Stop()
|
||||
end
|
||||
if self.advanced then
|
||||
self.MantisAdvTimer:Stop()
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
end
|
||||
-----------------------------------------------------------------------
|
||||
-- MANTIS end
|
||||
-----------------------------------------------------------------------
|
||||
@@ -53,6 +53,7 @@
|
||||
-- @type RANGE
|
||||
-- @field #string ClassName Name of the Class.
|
||||
-- @field #boolean Debug If true, debug info is send as messages on the screen.
|
||||
-- @field #boolean verbose Verbosity level. Higher means more output to DCS log file.
|
||||
-- @field #string id String id of range for output in DCS log.
|
||||
-- @field #string rangename Name of the range.
|
||||
-- @field Core.Point#COORDINATE location Coordinate of the range location.
|
||||
@@ -289,6 +290,7 @@
|
||||
RANGE={
|
||||
ClassName = "RANGE",
|
||||
Debug = false,
|
||||
verbose = 0,
|
||||
id = nil,
|
||||
rangename = nil,
|
||||
location = nil,
|
||||
@@ -518,7 +520,7 @@ RANGE.MenuF10Root=nil
|
||||
|
||||
--- Range script version.
|
||||
-- @field #string version
|
||||
RANGE.version="2.2.3"
|
||||
RANGE.version="2.3.0"
|
||||
|
||||
--TODO list:
|
||||
--TODO: Verbosity level for messages.
|
||||
@@ -556,7 +558,6 @@ function RANGE:New(rangename)
|
||||
-- Debug info.
|
||||
local text=string.format("Script version %s - creating new RANGE object %s.", RANGE.version, self.rangename)
|
||||
self:I(self.id..text)
|
||||
MESSAGE:New(text, 10):ToAllIf(self.Debug)
|
||||
|
||||
-- Defaults
|
||||
self:SetDefaultPlayerSmokeBomb()
|
||||
@@ -723,7 +724,6 @@ function RANGE:onafterStart()
|
||||
-- Starting range.
|
||||
local text=string.format("Starting RANGE %s. Number of strafe targets = %d. Number of bomb targets = %d.", self.rangename, self.nstrafetargets, self.nbombtargets)
|
||||
self:I(self.id..text)
|
||||
MESSAGE:New(text,10):ToAllIf(self.Debug)
|
||||
|
||||
-- Event handling.
|
||||
if self.eventmoose then
|
||||
@@ -757,6 +757,7 @@ function RANGE:onafterStart()
|
||||
|
||||
-- Radio queue.
|
||||
self.rangecontrol=RADIOQUEUE:New(self.rangecontrolfreq, nil, self.rangename)
|
||||
self.rangecontrol.schedonce=true
|
||||
|
||||
-- Init numbers.
|
||||
self.rangecontrol:SetDigit(0, RANGE.Sound.RC0.filename, RANGE.Sound.RC0.duration, self.soundpath)
|
||||
@@ -781,7 +782,8 @@ function RANGE:onafterStart()
|
||||
if self.instructorfreq then
|
||||
|
||||
-- Radio queue.
|
||||
self.instructor=RADIOQUEUE:New(self.instructorfreq, nil, self.rangename)
|
||||
self.instructor=RADIOQUEUE:New(self.instructorfreq, nil, self.rangename)
|
||||
self.instructor.schedonce=true
|
||||
|
||||
-- Init numbers.
|
||||
self.instructor:SetDigit(0, RANGE.Sound.IR0.filename, RANGE.Sound.IR0.duration, self.soundpath)
|
||||
@@ -1148,7 +1150,6 @@ function RANGE:AddStrafePit(targetnames, boxlength, boxwidth, heading, inversehe
|
||||
-- Neither unit nor static object with this name could be found.
|
||||
local text=string.format("ERROR! Could not find ANY strafe target object with name %s.", _name)
|
||||
self:E(self.id..text)
|
||||
MESSAGE:New(text, 10):ToAllIf(self.Debug)
|
||||
|
||||
end
|
||||
|
||||
@@ -1168,7 +1169,6 @@ function RANGE:AddStrafePit(targetnames, boxlength, boxwidth, heading, inversehe
|
||||
if ntargets==0 then
|
||||
local text=string.format("ERROR! No strafe target could be found when calling RANGE:AddStrafePit() for range %s", self.rangename)
|
||||
self:E(self.id..text)
|
||||
MESSAGE:New(text, 10):ToAllIf(self.Debug)
|
||||
return
|
||||
end
|
||||
|
||||
@@ -1238,7 +1238,6 @@ function RANGE:AddStrafePit(targetnames, boxlength, boxwidth, heading, inversehe
|
||||
-- Debug info
|
||||
local text=string.format("Adding new strafe target %s with %d targets: heading = %03d, box_L = %.1f, box_W = %.1f, goodpass = %d, foul line = %.1f", _name, ntargets, heading, l, w, goodpass, foulline)
|
||||
self:T(self.id..text)
|
||||
MESSAGE:New(text, 5):ToAllIf(self.Debug)
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -1567,13 +1566,13 @@ function RANGE:OnEventBirth(EventData)
|
||||
-- Debug output.
|
||||
local text=string.format("Player %s, callsign %s entered unit %s (UID %d) of group %s (GID %d)", _playername, _callsign, _unitName, _uid, _group:GetName(), _gid)
|
||||
self:T(self.id..text)
|
||||
MESSAGE:New(text, 5):ToAllIf(self.Debug)
|
||||
|
||||
-- Reset current strafe status.
|
||||
self.strafeStatus[_uid] = nil
|
||||
|
||||
-- Add Menu commands after a delay of 0.1 seconds.
|
||||
SCHEDULER:New(nil, self._AddF10Commands, {self,_unitName}, 0.1)
|
||||
--SCHEDULER:New(nil, self._AddF10Commands, {self,_unitName}, 0.1)
|
||||
self:ScheduleOnce(0.1, self._AddF10Commands, self, _unitName)
|
||||
|
||||
-- By default, some bomb impact points and do not flare each hit on target.
|
||||
self.PlayerSettings[_playername]={} --#RANGE.PlayerData
|
||||
@@ -1591,7 +1590,8 @@ function RANGE:OnEventBirth(EventData)
|
||||
|
||||
-- Start check in zone timer.
|
||||
if self.planes[_uid] ~= true then
|
||||
SCHEDULER:New(nil, self._CheckInZone, {self, EventData.IniUnitName}, 1, 1)
|
||||
--SCHEDULER:New(nil, self._CheckInZone, {self, EventData.IniUnitName}, 1, 1)
|
||||
self.timerCheckZone=TIMER:New(self._CheckInZone, self, EventData.IniUnitName):Start(1, 1)
|
||||
self.planes[_uid] = true
|
||||
end
|
||||
|
||||
@@ -1674,15 +1674,12 @@ function RANGE:OnEventHit(EventData)
|
||||
|
||||
if _unit and _playername then
|
||||
|
||||
-- Position of target.
|
||||
local targetPos = _target:GetCoordinate()
|
||||
|
||||
-- Message to player.
|
||||
--local text=string.format("%s, direct hit on target %s.", self:_myname(_unitName), targetname)
|
||||
--self:DisplayMessageToGroup(_unit, text, 10, true)
|
||||
|
||||
-- Flare target.
|
||||
if self.PlayerSettings[_playername].flaredirecthits then
|
||||
|
||||
-- Position of target.
|
||||
local targetPos = _target:GetCoordinate()
|
||||
|
||||
targetPos:Flare(self.PlayerSettings[_playername].flarecolor)
|
||||
end
|
||||
|
||||
@@ -1724,9 +1721,6 @@ function RANGE:OnEventShot(EventData)
|
||||
self:T(self.id.."EVENT SHOT: Weapon name = ".._weaponName)
|
||||
self:T(self.id.."EVENT SHOT: Weapon cate = "..weaponcategory)
|
||||
|
||||
-- Special cases:
|
||||
--local _viggen=string.match(_weapon, "ROBOT") or string.match(_weapon, "RB75") or string.match(_weapon, "BK90") or string.match(_weapon, "RB15") or string.match(_weapon, "RB04")
|
||||
|
||||
-- Tracking conditions for bombs, rockets and missiles.
|
||||
local _bombs = weaponcategory==Weapon.Category.BOMB --string.match(_weapon, "weapons.bombs")
|
||||
local _rockets = weaponcategory==Weapon.Category.ROCKET --string.match(_weapon, "weapons.nurs")
|
||||
@@ -1760,7 +1754,7 @@ function RANGE:OnEventShot(EventData)
|
||||
self:T(self.id..string.format("RANGE %s: Tracking %s - %s.", self.rangename, _weapon, EventData.weapon:getName()))
|
||||
|
||||
-- Init bomb position.
|
||||
local _lastBombPos = {x=0,y=0,z=0}
|
||||
local _lastBombPos = {x=0,y=0,z=0} --DCS#Vec3
|
||||
|
||||
-- Function monitoring the position of a bomb until impact.
|
||||
local function trackBomb(_ordnance)
|
||||
@@ -1916,35 +1910,39 @@ end
|
||||
-- @param #string To To state.
|
||||
function RANGE:onafterStatus(From, Event, To)
|
||||
|
||||
local fsmstate=self:GetState()
|
||||
|
||||
local text=string.format("Range status: %s", fsmstate)
|
||||
|
||||
if self.instructor then
|
||||
local alive="N/A"
|
||||
if self.instructorrelayname then
|
||||
local relay=UNIT:FindByName(self.instructorrelayname)
|
||||
if relay then
|
||||
alive=tostring(relay:IsAlive())
|
||||
end
|
||||
end
|
||||
text=text..string.format(", Instructor %.3f MHz (Relay=%s alive=%s)", self.instructorfreq, tostring(self.instructorrelayname), alive)
|
||||
end
|
||||
|
||||
if self.rangecontrol then
|
||||
local alive="N/A"
|
||||
if self.rangecontrolrelayname then
|
||||
local relay=UNIT:FindByName(self.rangecontrolrelayname)
|
||||
if relay then
|
||||
alive=tostring(relay:IsAlive())
|
||||
end
|
||||
end
|
||||
text=text..string.format(", Control %.3f MHz (Relay=%s alive=%s)", self.rangecontrolfreq, tostring(self.rangecontrolrelayname), alive)
|
||||
end
|
||||
if self.verbose>0 then
|
||||
|
||||
|
||||
-- Check range status.
|
||||
self:I(self.id..text)
|
||||
local fsmstate=self:GetState()
|
||||
|
||||
local text=string.format("Range status: %s", fsmstate)
|
||||
|
||||
if self.instructor then
|
||||
local alive="N/A"
|
||||
if self.instructorrelayname then
|
||||
local relay=UNIT:FindByName(self.instructorrelayname)
|
||||
if relay then
|
||||
alive=tostring(relay:IsAlive())
|
||||
end
|
||||
end
|
||||
text=text..string.format(", Instructor %.3f MHz (Relay=%s alive=%s)", self.instructorfreq, tostring(self.instructorrelayname), alive)
|
||||
end
|
||||
|
||||
if self.rangecontrol then
|
||||
local alive="N/A"
|
||||
if self.rangecontrolrelayname then
|
||||
local relay=UNIT:FindByName(self.rangecontrolrelayname)
|
||||
if relay then
|
||||
alive=tostring(relay:IsAlive())
|
||||
end
|
||||
end
|
||||
text=text..string.format(", Control %.3f MHz (Relay=%s alive=%s)", self.rangecontrolfreq, tostring(self.rangecontrolrelayname), alive)
|
||||
end
|
||||
|
||||
|
||||
-- Check range status.
|
||||
self:I(self.id..text)
|
||||
|
||||
end
|
||||
|
||||
-- Check player status.
|
||||
self:_CheckPlayers()
|
||||
@@ -2736,6 +2734,32 @@ function RANGE:_CheckInZone(_unitName)
|
||||
|
||||
if _unit and _playername then
|
||||
|
||||
--- Function to check if unit is in zone and facing in the right direction and is below the max alt.
|
||||
local function checkme(targetheading, _zone)
|
||||
local zone=_zone --Core.Zone#ZONE
|
||||
|
||||
-- Heading check.
|
||||
local unitheading = _unit:GetHeading()
|
||||
local pitheading = targetheading-180
|
||||
local deltaheading = unitheading-pitheading
|
||||
local towardspit = math.abs(deltaheading)<=90 or math.abs(deltaheading-360)<=90
|
||||
|
||||
if towardspit then
|
||||
|
||||
local vec3=_unit:GetVec3()
|
||||
local vec2={x=vec3.x, y=vec3.z} --DCS#Vec2
|
||||
local landheight=land.getHeight(vec2)
|
||||
local unitalt=vec3.y-landheight
|
||||
|
||||
if unitalt<=self.strafemaxalt then
|
||||
local unitinzone=zone:IsVec2InZone(vec2)
|
||||
return unitinzone
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
-- Current position of player unit.
|
||||
local _unitID = _unit:GetID()
|
||||
|
||||
@@ -2747,18 +2771,8 @@ function RANGE:_CheckInZone(_unitName)
|
||||
-- Get the current approach zone and check if player is inside.
|
||||
local zone=_currentStrafeRun.zone.polygon --Core.Zone#ZONE_POLYGON_BASE
|
||||
|
||||
local unitheading = _unit:GetHeading()
|
||||
local pitheading = _currentStrafeRun.zone.heading - 180
|
||||
local deltaheading = unitheading-pitheading
|
||||
local towardspit = math.abs(deltaheading)<=90 or math.abs(deltaheading-360)<=90
|
||||
local unitalt=_unit:GetHeight()-_unit:GetCoordinate():GetLandHeight()
|
||||
|
||||
-- Check if unit is inside zone and below max height AGL.
|
||||
local unitinzone=_unit:IsInZone(zone) and unitalt <= self.strafemaxalt and towardspit
|
||||
|
||||
-- Debug output
|
||||
local text=string.format("Checking still in zone. Unit = %s, player = %s in zone = %s. alt = %d, delta heading = %d", _unitName, _playername, tostring(unitinzone), unitalt, deltaheading)
|
||||
self:T2(self.id..text)
|
||||
-- Check if unit in zone and facing the right direction.
|
||||
local unitinzone=checkme(_currentStrafeRun.zone.heading, zone)
|
||||
|
||||
-- Check if player is in strafe zone and below max alt.
|
||||
if unitinzone then
|
||||
@@ -2858,22 +2872,10 @@ function RANGE:_CheckInZone(_unitName)
|
||||
for _,_targetZone in pairs(self.strafeTargets) do
|
||||
|
||||
-- Get the current approach zone and check if player is inside.
|
||||
local zonenname=_targetZone.name
|
||||
local zone=_targetZone.polygon --Core.Zone#ZONE_POLYGON_BASE
|
||||
|
||||
-- Check if player is in zone and below max alt and flying towards the target.
|
||||
local unitheading = _unit:GetHeading()
|
||||
local pitheading = _targetZone.heading - 180
|
||||
local deltaheading = unitheading-pitheading
|
||||
local towardspit = math.abs(deltaheading)<=90 or math.abs(deltaheading-360)<=90
|
||||
local unitalt =_unit:GetHeight()-_unit:GetCoordinate():GetLandHeight()
|
||||
|
||||
-- Check if unit is inside zone and below max height AGL.
|
||||
local unitinzone=_unit:IsInZone(zone) and unitalt <= self.strafemaxalt and towardspit
|
||||
|
||||
-- Debug info.
|
||||
local text=string.format("Checking zone %s. Unit = %s, player = %s in zone = %s. alt = %d, delta heading = %d", _targetZone.name, _unitName, _playername, tostring(unitinzone), unitalt, deltaheading)
|
||||
self:T2(self.id..text)
|
||||
-- Check if unit in zone and facing the right direction.
|
||||
local unitinzone=checkme(_targetZone.heading, zone)
|
||||
|
||||
-- Player is inside zone.
|
||||
if unitinzone then
|
||||
@@ -2882,7 +2884,7 @@ function RANGE:_CheckInZone(_unitName)
|
||||
local _ammo=self:_GetAmmo(_unitName)
|
||||
|
||||
-- Init strafe status for this player.
|
||||
self.strafeStatus[_unitID] = {hits = 0, zone = _targetZone, time = 1, ammo=_ammo, pastfoulline=false }
|
||||
self.strafeStatus[_unitID] = {hits = 0, zone = _targetZone, time = 1, ammo=_ammo, pastfoulline=false}
|
||||
|
||||
-- Rolling in!
|
||||
local _msg=string.format("%s, rolling in on strafe pit %s.", self:_myname(_unitName), _targetZone.name)
|
||||
@@ -3089,11 +3091,9 @@ function RANGE:_GetAmmo(unitname)
|
||||
|
||||
local text=string.format("Player %s has %d rounds ammo of type %s", playername, Nammo, Tammo)
|
||||
self:T(self.id..text)
|
||||
MESSAGE:New(text, 10):ToAllIf(self.Debug)
|
||||
else
|
||||
local text=string.format("Player %s has %d ammo of type %s", playername, Nammo, Tammo)
|
||||
self:T(self.id..text)
|
||||
MESSAGE:New(text, 10):ToAllIf(self.Debug)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -15,8 +15,10 @@
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Authors: **FlightControl**
|
||||
-- ### Authors: **FlightControl**, **applevangelist**
|
||||
--
|
||||
-- Last Update: April 2021
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module Functional.Sead
|
||||
@@ -39,18 +41,55 @@
|
||||
SEAD = {
|
||||
ClassName = "SEAD",
|
||||
TargetSkill = {
|
||||
Average = { Evade = 50, DelayOff = { 10, 25 }, DelayOn = { 10, 30 } } ,
|
||||
Good = { Evade = 30, DelayOff = { 8, 20 }, DelayOn = { 20, 40 } } ,
|
||||
High = { Evade = 15, DelayOff = { 5, 17 }, DelayOn = { 30, 50 } } ,
|
||||
Excellent = { Evade = 10, DelayOff = { 3, 10 }, DelayOn = { 30, 60 } }
|
||||
Average = { Evade = 30, DelayOn = { 40, 60 } } ,
|
||||
Good = { Evade = 20, DelayOn = { 30, 50 } } ,
|
||||
High = { Evade = 15, DelayOn = { 20, 40 } } ,
|
||||
Excellent = { Evade = 10, DelayOn = { 10, 30 } }
|
||||
},
|
||||
SEADGroupPrefixes = {}
|
||||
SEADGroupPrefixes = {},
|
||||
SuppressedGroups = {},
|
||||
EngagementRange = 75 -- default 75% engagement range Feature Request #1355
|
||||
}
|
||||
|
||||
-- TODO Complete list?
|
||||
--- Missile enumerators
|
||||
-- @field Harms
|
||||
SEAD.Harms = {
|
||||
--[[
|
||||
["X58"] = "weapons.missiles.X_58", --Kh-58X anti-radiation missiles fired
|
||||
["Kh25"] = "weapons.missiles.Kh25MP_PRGS1VP", --Kh-25MP anti-radiation missiles fired
|
||||
["X25"] = "weapons.missiles.X_25MP", --Kh-25MPU anti-radiation missiles fired
|
||||
["X28"] = "weapons.missiles.X_28", --Kh-28 anti-radiation missiles fired
|
||||
["X31"] = "weapons.missiles.X_31P", --Kh-31P anti-radiation missiles fired
|
||||
["AGM45A"] = "weapons.missiles.AGM_45A", --AGM-45A anti-radiation missiles fired
|
||||
["AGM45"] = "weapons.missiles.AGM_45", --AGM-45B anti-radiation missiles fired
|
||||
["AGM88"] = "weapons.missiles.AGM_88", --AGM-88C anti-radiation missiles fired
|
||||
["AGM122"] = "weapons.missiles.AGM_122", --AGM-122 Sidearm anti-radiation missiles fired
|
||||
["LD10"] = "weapons.missiles.LD-10", --LD-10 anti-radiation missiles fired
|
||||
["ALARM"] = "weapons.missiles.ALARM", --ALARM anti-radiation missiles fired
|
||||
["AGM84E"] = "weapons.missiles.AGM_84E", --AGM84 anti-radiation missiles fired
|
||||
["AGM84A"] = "weapons.missiles.AGM_84A", --AGM84 anti-radiation missiles fired
|
||||
["AGM84H"] = "weapons.missiles.AGM_84H", --AGM84 anti-radiation missiles fired
|
||||
--]]
|
||||
["AGM_88"] = "AGM_88",
|
||||
["AGM_45"] = "AGM_45",
|
||||
["AGM_122"] = "AGM_122",
|
||||
["AGM_84"] = "AGM_84",
|
||||
["AGM_45"] = "AGM_45",
|
||||
["ALARN"] = "ALARM",
|
||||
["LD-10"] = "LD-10",
|
||||
["X_58"] = "X_58",
|
||||
["X_28"] = "X_28",
|
||||
["X_25"] = "X_25",
|
||||
["X_31"] = "X_31",
|
||||
["Kh25"] = "Kh25",
|
||||
}
|
||||
|
||||
--- 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...
|
||||
-- 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
|
||||
-- @param table{string,...}|string SEADGroupPrefixes which is a table of Prefixes of the SA Groups in the DCS mission editor on which evasive actions need to be taken.
|
||||
-- @return SEAD
|
||||
-- @usage
|
||||
-- -- CCCP SEAD Defenses
|
||||
@@ -66,29 +105,77 @@ function SEAD:New( SEADGroupPrefixes )
|
||||
self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix
|
||||
end
|
||||
else
|
||||
self.SEADGroupNames[SEADGroupPrefixes] = SEADGroupPrefixes
|
||||
self.SEADGroupPrefixes[SEADGroupPrefixes] = SEADGroupPrefixes
|
||||
end
|
||||
|
||||
self:HandleEvent( EVENTS.Shot )
|
||||
|
||||
self:I("*** SEAD - Started Version 0.2.7")
|
||||
return self
|
||||
end
|
||||
|
||||
--- Detects if an SA site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME.
|
||||
--- Update the active SEAD Set
|
||||
-- @param #SEAD self
|
||||
-- @param #table SEADGroupPrefixes The prefixes to add, note: can also be a single #string
|
||||
-- @return #SEAD self
|
||||
function SEAD:UpdateSet( SEADGroupPrefixes )
|
||||
|
||||
self:F( 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. 50
|
||||
-- @return self
|
||||
function SEAD:SetEngagementRange(range)
|
||||
self:F( { 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
|
||||
|
||||
--- Check if a known HARM was fired
|
||||
-- @param #SEAD self
|
||||
-- @param #string WeaponName
|
||||
-- @return #boolean Returns true for a match
|
||||
function SEAD:_CheckHarms(WeaponName)
|
||||
self:F( { WeaponName } )
|
||||
local hit = false
|
||||
for _,_name in pairs (SEAD.Harms) do
|
||||
if string.find(WeaponName,_name,1) then hit = true end
|
||||
end
|
||||
return hit
|
||||
end
|
||||
|
||||
--- 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.
|
||||
-- @see SEAD
|
||||
-- @param #SEAD
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function SEAD:OnEventShot( EventData )
|
||||
self:F( { EventData } )
|
||||
self:T( { EventData } )
|
||||
|
||||
local SEADUnit = EventData.IniDCSUnit
|
||||
local SEADUnitName = EventData.IniDCSUnitName
|
||||
local SEADWeapon = EventData.Weapon -- Identify the weapon fired
|
||||
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 )
|
||||
|
||||
self:T( "*** SEAD - Missile Launched = " .. SEADWeaponName)
|
||||
self:T({ SEADWeapon })
|
||||
|
||||
--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
|
||||
--[[check for SEAD missiles
|
||||
if SEADWeaponName == "weapons.missiles.X_58" --Kh-58U anti-radiation missiles fired
|
||||
or
|
||||
SEADWeaponName == "weapons.missiles.Kh25MP_PRGS1VP" --Kh-25MP anti-radiation missiles fired
|
||||
@@ -107,27 +194,39 @@ function SEAD:OnEventShot( EventData )
|
||||
or
|
||||
SEADWeaponName == "weapons.missiles.AGM_122" --AGM-122 Sidearm anti-radiation missiles fired
|
||||
or
|
||||
SEADWeaponName == "weapons.missiles.LD-10" --LD-10 anti-radiation missiles fired
|
||||
or
|
||||
SEADWeaponName == "weapons.missiles.ALARM" --ALARM anti-radiation missiles fired
|
||||
then
|
||||
|
||||
or
|
||||
SEADWeaponName == "weapons.missiles.AGM_84E" --AGM84 anti-radiation missiles fired
|
||||
or
|
||||
SEADWeaponName == "weapons.missiles.AGM_84A" --AGM84 anti-radiation missiles fired
|
||||
or
|
||||
SEADWeaponName == "weapons.missiles.AGM_84H" --AGM84 anti-radiation missiles fired
|
||||
--]]
|
||||
if self:_CheckHarms(SEADWeaponName) then
|
||||
local _targetskill = "Random"
|
||||
local _targetMimgroupName = "none"
|
||||
local _evade = math.random (1,100) -- random number for chance of evading action
|
||||
local _targetMim = EventData.Weapon:getTarget() -- Identify target
|
||||
local _targetMimname = Unit.getName(_targetMim)
|
||||
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 _targetUnit = UNIT:Find(_targetMim) -- Unit name by DCS Object
|
||||
if _targetUnit and _targetUnit:IsAlive() then
|
||||
local _targetMimgroup = _targetUnit:GetGroup()
|
||||
local _targetMimgroupName = _targetMimgroup:GetName() -- group name
|
||||
--local _targetskill = _DATABASE.Templates.Units[_targetUnit].Template.skill
|
||||
self:T( self.SEADGroupPrefixes )
|
||||
self:T( _targetMimgroupName )
|
||||
end
|
||||
-- see if we are shot at
|
||||
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' )
|
||||
self:T( '*** SEAD - Group Found' )
|
||||
break
|
||||
end
|
||||
end
|
||||
if SEADGroupFound == true then
|
||||
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) ]
|
||||
@@ -136,63 +235,38 @@ function SEAD:OnEventShot( EventData )
|
||||
if self.TargetSkill[_targetskill] then
|
||||
if (_evade > self.TargetSkill[_targetskill].Evade) then
|
||||
|
||||
self:T( string.format("Evading, target skill " ..string.format(_targetskill)) )
|
||||
|
||||
local _targetMim = Weapon.getTarget(SEADWeapon)
|
||||
local _targetMimname = Unit.getName(_targetMim)
|
||||
self:T( string.format("*** SEAD - Evading, target skill " ..string.format(_targetskill)) )
|
||||
|
||||
local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon))
|
||||
local _targetMimcont= _targetMimgroup:getController()
|
||||
|
||||
routines.groupRandomDistSelf(_targetMimgroup,300,'Diamond',250,20) -- move randomly
|
||||
|
||||
local SuppressedGroups1 = {} -- unit suppressed radar off for a random time
|
||||
|
||||
local function SuppressionEnd1(id)
|
||||
id.ctrl:setOption(AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.GREEN)
|
||||
SuppressedGroups1[id.groupName] = nil
|
||||
end
|
||||
|
||||
local id = {
|
||||
--tracker ID table to switch groups off and on again
|
||||
local id = {
|
||||
groupName = _targetMimgroup,
|
||||
ctrl = _targetMimcont
|
||||
}
|
||||
|
||||
local delay1 = math.random(self.TargetSkill[_targetskill].DelayOff[1], self.TargetSkill[_targetskill].DelayOff[2])
|
||||
|
||||
if SuppressedGroups1[id.groupName] == nil then
|
||||
|
||||
SuppressedGroups1[id.groupName] = {
|
||||
SuppressionEndTime1 = timer.getTime() + delay1,
|
||||
SuppressionEndN1 = SuppressionEndCounter1 --Store instance of SuppressionEnd() scheduled function
|
||||
}
|
||||
|
||||
Controller.setOption(_targetMimcont, AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.GREEN)
|
||||
timer.scheduleFunction(SuppressionEnd1, id, SuppressedGroups1[id.groupName].SuppressionEndTime1) --Schedule the SuppressionEnd() function
|
||||
--trigger.action.outText( string.format("Radar Off " ..string.format(delay1)), 20)
|
||||
end
|
||||
|
||||
local SuppressedGroups = {}
|
||||
|
||||
local function SuppressionEnd(id)
|
||||
|
||||
local function SuppressionEnd(id) --switch group back on
|
||||
local range = self.EngagementRange -- Feature Request #1355
|
||||
self:T(string.format("*** SEAD - Engagement Range is %d", range))
|
||||
id.ctrl:setOption(AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.RED)
|
||||
SuppressedGroups[id.groupName] = nil
|
||||
--id.groupName:enableEmission(true)
|
||||
id.ctrl:setOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION,range) --Feature Request #1355
|
||||
self.SuppressedGroups[id.groupName] = nil --delete group id from table when done
|
||||
end
|
||||
|
||||
local id = {
|
||||
groupName = _targetMimgroup,
|
||||
ctrl = _targetMimcont
|
||||
}
|
||||
|
||||
-- randomize switch-on time
|
||||
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)
|
||||
local SuppressionEndTime = timer.getTime() + delay
|
||||
--create entry
|
||||
if self.SuppressedGroups[id.groupName] == nil then --no timer entry for this group yet
|
||||
self.SuppressedGroups[id.groupName] = {
|
||||
SuppressionEndTime = delay
|
||||
}
|
||||
Controller.setOption(_targetMimcont, AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.GREEN)
|
||||
--_targetMimgroup:enableEmission(false)
|
||||
timer.scheduleFunction(SuppressionEnd, id, SuppressionEndTime) --Schedule the SuppressionEnd() function
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
527
Moose Development/Moose/Functional/Shorad.lua
Normal file
527
Moose Development/Moose/Functional/Shorad.lua
Normal file
@@ -0,0 +1,527 @@
|
||||
--- **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: May 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 = {
|
||||
--[[
|
||||
["X58"] = "weapons.missiles.X_58", --Kh-58X anti-radiation missiles fired
|
||||
["Kh25"] = "weapons.missiles.Kh25MP_PRGS1VP", --Kh-25MP anti-radiation missiles fired
|
||||
["X25"] = "weapons.missiles.X_25MP", --Kh-25MPU anti-radiation missiles fired
|
||||
["X28"] = "weapons.missiles.X_28", --Kh-28 anti-radiation missiles fired
|
||||
["X31"] = "weapons.missiles.X_31P", --Kh-31P anti-radiation missiles fired
|
||||
["AGM45A"] = "weapons.missiles.AGM_45A", --AGM-45A anti-radiation missiles fired
|
||||
["AGM45"] = "weapons.missiles.AGM_45", --AGM-45B anti-radiation missiles fired
|
||||
["AGM88"] = "weapons.missiles.AGM_88", --AGM-88C anti-radiation missiles fired
|
||||
["AGM122"] = "weapons.missiles.AGM_122", --AGM-122 Sidearm anti-radiation missiles fired
|
||||
["LD10"] = "weapons.missiles.LD-10", --LD-10 anti-radiation missiles fired
|
||||
["ALARM"] = "weapons.missiles.ALARM", --ALARM anti-radiation missiles fired
|
||||
["AGM84E"] = "weapons.missiles.AGM_84E", --AGM84 anti-radiation missiles fired
|
||||
["AGM84A"] = "weapons.missiles.AGM_84A", --AGM84 anti-radiation missiles fired
|
||||
["AGM84H"] = "weapons.missiles.AGM_84H", --AGM84 anti-radiation missiles fired
|
||||
--]]
|
||||
["AGM_88"] = "AGM_88",
|
||||
["AGM_45"] = "AGM_45",
|
||||
["AGM_122"] = "AGM_122",
|
||||
["AGM_84"] = "AGM_84",
|
||||
["AGM_45"] = "AGM_45",
|
||||
["ALARN"] = "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"
|
||||
function SHORAD:New(Name, ShoradPrefix, Samset, Radius, ActiveTimer, Coalition)
|
||||
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:HandleEvent( EVENTS.Shot )
|
||||
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 = true -- Decide if we are using Emission on/off (default) or AlarmState red/green
|
||||
self:I("*** SHORAD - Started Version 0.2.5")
|
||||
-- Set the string id for output to DCS.log file.
|
||||
self.lid=string.format("SHORAD %s | ", self.name)
|
||||
self:_InitState()
|
||||
return self
|
||||
end
|
||||
|
||||
--- Initially set all groups to alarm state GREEN
|
||||
-- @param #SHORAD self
|
||||
function SHORAD:_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)
|
||||
else
|
||||
_group:OptionAlarmStateGreen() --Wrapper.Group#GROUP
|
||||
end
|
||||
end
|
||||
-- gather entropy
|
||||
for i=1,10 do
|
||||
math.random()
|
||||
end
|
||||
end
|
||||
|
||||
--- Switch debug state
|
||||
-- @param #SHORAD self
|
||||
-- @param #boolean debug Switch debug on (true) or off (false)
|
||||
function SHORAD:SwitchDebug(debug)
|
||||
self:T( { debug } )
|
||||
local onoff = debug or false
|
||||
if debug then
|
||||
self.debug = true
|
||||
--tracing
|
||||
BASE:TraceOn()
|
||||
BASE:TraceClass("SHORAD")
|
||||
else
|
||||
self.debug = false
|
||||
BASE:TraceOff()
|
||||
end
|
||||
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
|
||||
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
|
||||
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
|
||||
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)
|
||||
local timer = seconds or 600
|
||||
if timer < 0 then
|
||||
timer = 600
|
||||
end
|
||||
self.ActiveTimer = timer
|
||||
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)
|
||||
local radius = meters or 20000
|
||||
if radius < 0 then
|
||||
radius = 20000
|
||||
end
|
||||
self.Radius = radius
|
||||
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.UseEmOnOff = switch or false
|
||||
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( { 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( { 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)
|
||||
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)
|
||||
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)
|
||||
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()
|
||||
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({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
|
||||
end
|
||||
|
||||
--- Main function - work on the EventData
|
||||
-- @param #SHORAD self
|
||||
-- @param Core.Event#EVENTDATA EventData The event details table data set
|
||||
function SHORAD:OnEventShot( EventData )
|
||||
self:T( { EventData } )
|
||||
|
||||
--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)
|
||||
--
|
||||
-- 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
|
||||
-- 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 = {},
|
||||
defending = {},
|
||||
portzone = nil,
|
||||
harborzone = nil,
|
||||
shippinglanes = {},
|
||||
offroadpaths = {},
|
||||
autodefence = false,
|
||||
@@ -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 SELFPROPELLED Assets go to their destination by themselves. No transport carrier needed.
|
||||
WAREHOUSE.TransportType = {
|
||||
AIRPLANE = "Air_TransportPlane",
|
||||
HELICOPTER = "Air_TransportHelo",
|
||||
APC = "Ground_APC",
|
||||
TRAIN = "Ground_Train",
|
||||
SHIP = "Naval_UnarmedShip",
|
||||
SELFPROPELLED = "Selfpropelled",
|
||||
AIRPLANE = "Air_TransportPlane",
|
||||
HELICOPTER = "Air_TransportHelo",
|
||||
APC = "Ground_APC",
|
||||
TRAIN = "Ground_Train",
|
||||
SHIP = "Naval_UnarmedShip",
|
||||
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.
|
||||
@@ -1776,7 +1786,7 @@ WAREHOUSE.version="1.0.2"
|
||||
-- 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: 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: 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!
|
||||
@@ -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
|
||||
-- @return #WAREHOUSE self
|
||||
function WAREHOUSE:New(warehouse, alias)
|
||||
BASE:T({warehouse=warehouse})
|
||||
|
||||
-- Check if just a string was given and convert to static.
|
||||
if type(warehouse)=="string" then
|
||||
@@ -2602,8 +2611,10 @@ end
|
||||
--- Check parking ID.
|
||||
-- @param #WAREHOUSE self
|
||||
-- @param Wrapper.Airbase#AIRBASE.ParkingSpot spot Parking spot.
|
||||
-- @param Wrapper.Airbase#AIRBASE airbase The airbase.
|
||||
-- @return #boolean If true, parking is valid.
|
||||
function WAREHOUSE:_CheckParkingValid(spot)
|
||||
function WAREHOUSE:_CheckParkingValid(spot, airbase)
|
||||
|
||||
if self.parkingIDs==nil then
|
||||
return true
|
||||
end
|
||||
@@ -2709,6 +2720,18 @@ function WAREHOUSE:SetPortZone(zone)
|
||||
return self
|
||||
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.
|
||||
-- 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
|
||||
@@ -4340,10 +4363,10 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request)
|
||||
self:_ErrorMessage("ERROR: Cargo transport by train not supported yet!")
|
||||
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!")
|
||||
return
|
||||
-- Spawn Ship in port zone
|
||||
spawngroup=self:_SpawnAssetGroundNaval(_alias, _assetitem, Request, self.portzone)
|
||||
|
||||
elseif Request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then
|
||||
|
||||
@@ -4471,6 +4494,9 @@ function WAREHOUSE:onafterRequestSpawned(From, Event, To, Request, CargoGroupSet
|
||||
--_boardradius=nil
|
||||
elseif Request.transporttype==WAREHOUSE.TransportType.APC then
|
||||
--_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
|
||||
|
||||
-- Empty cargo group set.
|
||||
@@ -4481,7 +4507,6 @@ function WAREHOUSE:onafterRequestSpawned(From, Event, To, Request, CargoGroupSet
|
||||
|
||||
-- Find asset belonging to this group.
|
||||
local asset=self:FindAssetInDB(_group)
|
||||
|
||||
-- New cargo group object.
|
||||
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.
|
||||
CargoGroups:AddCargo(cargogroup)
|
||||
|
||||
end
|
||||
|
||||
------------------------
|
||||
@@ -4535,23 +4561,54 @@ function WAREHOUSE:onafterRequestSpawned(From, Event, To, Request, CargoGroupSet
|
||||
-- Set home zone.
|
||||
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
|
||||
self:E(self.lid.."ERROR: Unknown transporttype!")
|
||||
end
|
||||
|
||||
-- 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.
|
||||
local pickupouter=200
|
||||
local pickupinner=0
|
||||
if self.spawnzone.Radius~=nil then
|
||||
pickupouter=self.spawnzone.Radius
|
||||
local pickupouter = 200
|
||||
local pickupinner = 0
|
||||
local deployouter = 200
|
||||
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
|
||||
end
|
||||
local deployouter=200
|
||||
local deployinner=0
|
||||
if self.spawnzone.Radius~=nil then
|
||||
deployouter=Request.warehouse.spawnzone.Radius
|
||||
deployinner=20
|
||||
deployouter=1000
|
||||
deployinner=0
|
||||
else
|
||||
pickupouter=200
|
||||
pickupinner=0
|
||||
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
|
||||
CargoTransport:SetPickupRadius(pickupouter, pickupinner)
|
||||
CargoTransport:SetDeployRadius(deployouter, deployinner)
|
||||
@@ -4630,7 +4687,7 @@ function WAREHOUSE:onafterRequestSpawned(From, Event, To, Request, CargoGroupSet
|
||||
-- Get cargo group object.
|
||||
local group=Cargo:GetObject() --Wrapper.Group#GROUP
|
||||
|
||||
-- Get request.
|
||||
-- Get request.
|
||||
local request=warehouse:_GetRequestOfGroup(group, warehouse.pending)
|
||||
|
||||
-- Add cargo group to this carrier.
|
||||
@@ -6925,11 +6982,16 @@ function WAREHOUSE:_CheckRequestValid(request)
|
||||
valid=false
|
||||
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.
|
||||
self:E("ERROR: Incorrect request. Transport by SHIP not implemented yet!")
|
||||
valid=false
|
||||
local shippinglane=self:HasConnectionNaval(request.warehouse)
|
||||
|
||||
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
|
||||
|
||||
@@ -7025,7 +7087,7 @@ function WAREHOUSE:_CheckRequestNow(request)
|
||||
|
||||
-- If no transport is requested, assets need to be mobile unless it is a self request.
|
||||
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
|
||||
end
|
||||
|
||||
@@ -7607,7 +7669,7 @@ function WAREHOUSE:_FindParkingForAssets(airbase, assets)
|
||||
local parkingspot=_parkingspot --Wrapper.Airbase#AIRBASE.ParkingSpot
|
||||
|
||||
-- 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.
|
||||
local _spot=parkingspot.Coordinate -- Core.Point#COORDINATE
|
||||
@@ -8052,10 +8114,9 @@ function WAREHOUSE:_GetAttribute(group)
|
||||
-- Ships
|
||||
local aircraftcarrier=group:HasAttribute("Aircraft Carriers")
|
||||
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")
|
||||
|
||||
|
||||
-- Define attribute. Order is important.
|
||||
if transportplane then
|
||||
attribute=WAREHOUSE.Attribute.AIR_TRANSPORTPLANE
|
||||
|
||||
@@ -2,6 +2,7 @@ __Moose.Include( 'Scripts/Moose/Utilities/Enums.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Utilities/Routines.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Utilities/Utils.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Utilities/Profiler.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Utilities/Templates.lua' )
|
||||
|
||||
__Moose.Include( 'Scripts/Moose/Core/Base.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/UserFlag.lua' )
|
||||
@@ -67,6 +68,8 @@ __Moose.Include( 'Scripts/Moose/Functional/Suppression.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Functional/PseudoATC.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Functional/Warehouse.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Functional/Fox.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Functional/Mantis.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Functional/Shorad.lua' )
|
||||
|
||||
__Moose.Include( 'Scripts/Moose/Ops/Airboss.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Ops/RecoveryTanker.lua' )
|
||||
@@ -98,10 +101,12 @@ __Moose.Include( 'Scripts/Moose/AI/AI_Cargo.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_APC.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Helicopter.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Airplane.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Ship.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Dispatcher.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Dispatcher_APC.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Dispatcher_Ship.lua' )
|
||||
|
||||
__Moose.Include( 'Scripts/Moose/Actions/Act_Assign.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Actions/Act_Route.lua' )
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
-- * Wind direction and speed
|
||||
-- * Visibility
|
||||
-- * Cloud coverage, base and ceiling
|
||||
-- * Temprature
|
||||
-- * Temperature
|
||||
-- * Dew point (approximate as there is no relative humidity in DCS yet)
|
||||
-- * Pressure QNH/QFE
|
||||
-- * Weather phenomena: rain, thunderstorm, fog, dust
|
||||
@@ -564,7 +564,7 @@ _ATIS={}
|
||||
|
||||
--- ATIS class version.
|
||||
-- @field #string version
|
||||
ATIS.version="0.9.0"
|
||||
ATIS.version="0.9.1"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
@@ -580,6 +580,8 @@ ATIS.version="0.9.0"
|
||||
-- DONE: Metric units.
|
||||
-- DONE: Set UTC correction.
|
||||
-- DONE: Set magnetic variation.
|
||||
-- DONE: New DCS 2.7 weather presets.
|
||||
-- DONE: whatever
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Constructor
|
||||
@@ -1318,6 +1320,10 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
time=time-UTILS.GMTToLocalTimeDifference()*60*60
|
||||
end
|
||||
|
||||
if time < 0 then
|
||||
time = 24*60*60 + time --avoid negative time around midnight
|
||||
end
|
||||
|
||||
local clock=UTILS.SecondsToClock(time)
|
||||
local zulu=UTILS.Split(clock, ":")
|
||||
local ZULU=string.format("%s%s", zulu[1], zulu[2])
|
||||
@@ -1427,9 +1433,103 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
local cloudceil=clouds.base+clouds.thickness
|
||||
local clouddens=clouds.density
|
||||
|
||||
-- Cloud preset (DCS 2.7)
|
||||
local cloudspreset=clouds.preset or "Nothing"
|
||||
|
||||
-- Precepitation: 0=None, 1=Rain, 2=Thunderstorm, 3=Snow, 4=Snowstorm.
|
||||
local precepitation=tonumber(clouds.iprecptns)
|
||||
local precepitation=0
|
||||
|
||||
if cloudspreset:find("Preset10") then
|
||||
-- Scattered 5
|
||||
clouddens=4
|
||||
elseif cloudspreset:find("Preset11") then
|
||||
-- Scattered 6
|
||||
clouddens=4
|
||||
elseif cloudspreset:find("Preset12") then
|
||||
-- Scattered 7
|
||||
clouddens=4
|
||||
elseif cloudspreset:find("Preset13") then
|
||||
-- Broken 1
|
||||
clouddens=7
|
||||
elseif cloudspreset:find("Preset14") then
|
||||
-- Broken 2
|
||||
clouddens=7
|
||||
elseif cloudspreset:find("Preset15") then
|
||||
-- Broken 3
|
||||
clouddens=7
|
||||
elseif cloudspreset:find("Preset16") then
|
||||
-- Broken 4
|
||||
clouddens=7
|
||||
elseif cloudspreset:find("Preset17") then
|
||||
-- Broken 5
|
||||
clouddens=7
|
||||
elseif cloudspreset:find("Preset18") then
|
||||
-- Broken 6
|
||||
clouddens=7
|
||||
elseif cloudspreset:find("Preset19") then
|
||||
-- Broken 7
|
||||
clouddens=7
|
||||
elseif cloudspreset:find("Preset20") then
|
||||
-- Broken 8
|
||||
clouddens=7
|
||||
elseif cloudspreset:find("Preset21") then
|
||||
-- Overcast 1
|
||||
clouddens=9
|
||||
elseif cloudspreset:find("Preset22") then
|
||||
-- Overcast 2
|
||||
clouddens=9
|
||||
elseif cloudspreset:find("Preset23") then
|
||||
-- Overcast 3
|
||||
clouddens=9
|
||||
elseif cloudspreset:find("Preset24") then
|
||||
-- Overcast 4
|
||||
clouddens=9
|
||||
elseif cloudspreset:find("Preset25") then
|
||||
-- Overcast 5
|
||||
clouddens=9
|
||||
elseif cloudspreset:find("Preset26") then
|
||||
-- Overcast 6
|
||||
clouddens=9
|
||||
elseif cloudspreset:find("Preset27") then
|
||||
-- Overcast 7
|
||||
clouddens=9
|
||||
elseif cloudspreset:find("Preset1") then
|
||||
-- Light Scattered 1
|
||||
clouddens=1
|
||||
elseif cloudspreset:find("Preset2") then
|
||||
-- Light Scattered 2
|
||||
clouddens=1
|
||||
elseif cloudspreset:find("Preset3") then
|
||||
-- High Scattered 1
|
||||
clouddens=4
|
||||
elseif cloudspreset:find("Preset4") then
|
||||
-- High Scattered 2
|
||||
clouddens=4
|
||||
elseif cloudspreset:find("Preset5") then
|
||||
-- Scattered 1
|
||||
clouddens=4
|
||||
elseif cloudspreset:find("Preset6") then
|
||||
-- Scattered 2
|
||||
clouddens=4
|
||||
elseif cloudspreset:find("Preset7") then
|
||||
-- Scattered 3
|
||||
clouddens=4
|
||||
elseif cloudspreset:find("Preset8") then
|
||||
-- High Scattered 3
|
||||
clouddens=4
|
||||
elseif cloudspreset:find("Preset9") then
|
||||
-- Scattered 4
|
||||
clouddens=4
|
||||
elseif cloudspreset:find("RainyPreset") then
|
||||
-- Overcast + Rain
|
||||
clouddens=9
|
||||
if temperature>5 then
|
||||
precepitation=1 -- rain
|
||||
else
|
||||
precepitation=3 -- snow
|
||||
end
|
||||
end
|
||||
|
||||
local CLOUDBASE=string.format("%d", UTILS.MetersToFeet(cloudbase))
|
||||
local CLOUDCEIL=string.format("%d", UTILS.MetersToFeet(cloudceil))
|
||||
|
||||
|
||||
@@ -27,12 +27,16 @@
|
||||
-- **Supported Carriers:**
|
||||
--
|
||||
-- * [USS John C. Stennis](https://en.wikipedia.org/wiki/USS_John_C._Stennis) (CVN-74)
|
||||
-- * [USS Theodore Roosevelt](https://en.wikipedia.org/wiki/USS_Theodore_Roosevelt_(CVN-71)) (CVN-71) [Super Carrier Module]
|
||||
-- * [USS Abraham Lincoln](https://en.wikipedia.org/wiki/USS_Abraham_Lincoln_(CVN-72)) (CVN-72) [Super Carrier Module]
|
||||
-- * [USS George Washington](https://en.wikipedia.org/wiki/USS_George_Washington_(CVN-73)) (CVN-73) [Super Carrier Module]
|
||||
-- * [USS Harry S. Truman](https://en.wikipedia.org/wiki/USS_Harry_S._Truman) (CVN-75) [Super Carrier Module]
|
||||
-- * [USS Tarawa](https://en.wikipedia.org/wiki/USS_Tarawa_(LHA-1)) (LHA-1) [**WIP**]
|
||||
--
|
||||
-- **Supported Aircraft:**
|
||||
--
|
||||
-- * [F/A-18C Hornet Lot 20](https://forums.eagle.ru/forumdisplay.php?f=557) (Player & AI)
|
||||
-- * [F-14B Tomcat](https://forums.eagle.ru/forumdisplay.php?f=395) (Player & AI)
|
||||
-- * [F-14A/B Tomcat](https://forums.eagle.ru/forumdisplay.php?f=395) (Player & AI)
|
||||
-- * [A-4E Skyhawk Community Mod](https://forums.eagle.ru/showthread.php?t=224989) (Player & AI)
|
||||
-- * [AV-8B N/A Harrier](https://forums.eagle.ru/forumdisplay.php?f=555) (Player & AI) [**WIP**]
|
||||
-- * F/A-18C Hornet (AI)
|
||||
@@ -47,7 +51,9 @@
|
||||
-- the no other fixed wing aircraft (human or AI controlled) are supposed to land on the Tarawa. Currently only Case I is supported. Case II/III take slightly steps from the CVN carrier.
|
||||
-- However, the two Case II/III pattern are very similar so this is not a big drawback.
|
||||
--
|
||||
-- Heatblur's mighty F-14B Tomcat has been added (March 13th 2019) as well.
|
||||
-- Heatblur's mighty F-14B Tomcat has been added (March 13th 2019) as well. Same goes for the A version.
|
||||
--
|
||||
-- The [DCS Supercarriers](https://forums.eagle.ru/forum/151-dcs-supercarrier/) are also supported.
|
||||
--
|
||||
-- ## Discussion
|
||||
--
|
||||
@@ -1261,7 +1267,7 @@ AIRBOSS.AircraftCarrier={
|
||||
AV8B="AV8BNA",
|
||||
HORNET="FA-18C_hornet",
|
||||
A4EC="A-4E-C",
|
||||
F14A="F-14A_tomcat",
|
||||
F14A="F-14A-135-GR",
|
||||
F14B="F-14B",
|
||||
F14A_AI="F-14A",
|
||||
FA18C="F/A-18C",
|
||||
@@ -1273,17 +1279,19 @@ AIRBOSS.AircraftCarrier={
|
||||
|
||||
--- Carrier types.
|
||||
-- @type AIRBOSS.CarrierType
|
||||
-- @field #string ROOSEVELT USS Theodore Roosevelt (CVN-71)
|
||||
-- @field #string LINCOLN USS Abraham Lincoln (CVN-72)
|
||||
-- @field #string WASHINGTON USS George Washington (CVN-73)
|
||||
-- @field #string ROOSEVELT USS Theodore Roosevelt (CVN-71) [Super Carrier Module]
|
||||
-- @field #string LINCOLN USS Abraham Lincoln (CVN-72) [Super Carrier Module]
|
||||
-- @field #string WASHINGTON USS George Washington (CVN-73) [Super Carrier Module]
|
||||
-- @field #string STENNIS USS John C. Stennis (CVN-74)
|
||||
-- @field #string VINSON USS Carl Vinson (CVN-70)
|
||||
-- @field #string TRUMAN USS Harry S. Truman (CVN-75) [Super Carrier Module]
|
||||
-- @field #string VINSON USS Carl Vinson (CVN-70) [Obsolete]
|
||||
-- @field #string TARAWA USS Tarawa (LHA-1)
|
||||
-- @field #string KUZNETSOV Admiral Kuznetsov (CV 1143.5)
|
||||
AIRBOSS.CarrierType={
|
||||
ROOSEVELT="CVN_71",
|
||||
LINCOLN="CVN_72",
|
||||
WASHINGTON="CVN_73",
|
||||
TRUMAN="CVN_75",
|
||||
STENNIS="Stennis",
|
||||
VINSON="VINSON",
|
||||
TARAWA="LHA_Tarawa",
|
||||
@@ -1697,7 +1705,7 @@ AIRBOSS.MenuF10Root=nil
|
||||
|
||||
--- Airboss class version.
|
||||
-- @field #string version
|
||||
AIRBOSS.version="1.1.5"
|
||||
AIRBOSS.version="1.1.6"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
@@ -1939,8 +1947,10 @@ function AIRBOSS:New(carriername, alias)
|
||||
self:_InitNimitz()
|
||||
elseif self.carriertype==AIRBOSS.CarrierType.LINCOLN then
|
||||
self:_InitNimitz()
|
||||
elseif self.carriertype==AIRBOSS.CarrierType.WASHINGTON then
|
||||
elseif self.carriertype==AIRBOSS.CarrierType.WASHINGTON then
|
||||
self:_InitNimitz()
|
||||
elseif self.carriertype==AIRBOSS.CarrierType.TRUMAN then
|
||||
self:_InitNimitz()
|
||||
elseif self.carriertype==AIRBOSS.CarrierType.VINSON then
|
||||
-- TODO: Carl Vinson parameters.
|
||||
self:_InitStennis()
|
||||
@@ -2464,7 +2474,7 @@ function AIRBOSS:AddRecoveryWindow(starttime, stoptime, case, holdingoffset, tur
|
||||
local Tstart=UTILS.ClockToSeconds(starttime)
|
||||
|
||||
-- Set stop time.
|
||||
local Tstop=UTILS.ClockToSeconds(stoptime or Tstart+90*60)
|
||||
local Tstop=stoptime and UTILS.ClockToSeconds(stoptime) or Tstart+90*60
|
||||
|
||||
-- Consistancy check for timing.
|
||||
if Tstart>Tstop then
|
||||
@@ -2631,6 +2641,15 @@ function AIRBOSS:SetRecoveryTurnTime(interval)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set multiplayer environment wire correction.
|
||||
-- @param #AIRBOSS self
|
||||
-- @param #number Dcorr Correction distance in meters. Default 8.7 m.
|
||||
-- @return #AIRBOSS self
|
||||
function AIRBOSS:SetMPWireCorrection(Dcorr)
|
||||
self.mpWireCorrection=Dcorr or 8.7
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set time interval for updating queues and other stuff.
|
||||
-- @param #AIRBOSS self
|
||||
-- @param #number interval Time interval in seconds. Default 30 sec.
|
||||
@@ -4150,7 +4169,7 @@ function AIRBOSS:_InitStennis()
|
||||
|
||||
-- Carrier Parameters.
|
||||
self.carrierparam.sterndist =-153
|
||||
self.carrierparam.deckheight = 19
|
||||
self.carrierparam.deckheight = 19.06
|
||||
|
||||
-- Total size of the carrier (approx as rectangle).
|
||||
self.carrierparam.totlength=310 -- Wiki says 332.8 meters overall length.
|
||||
@@ -4158,7 +4177,7 @@ function AIRBOSS:_InitStennis()
|
||||
self.carrierparam.totwidthstarboard=30
|
||||
|
||||
-- Landing runway.
|
||||
self.carrierparam.rwyangle = -9
|
||||
self.carrierparam.rwyangle = -9.1359
|
||||
self.carrierparam.rwylength = 225
|
||||
self.carrierparam.rwywidth = 20
|
||||
|
||||
@@ -4301,7 +4320,7 @@ function AIRBOSS:_InitNimitz()
|
||||
|
||||
-- Carrier Parameters.
|
||||
self.carrierparam.sterndist =-164
|
||||
self.carrierparam.deckheight = 20
|
||||
self.carrierparam.deckheight = 20.1494 --DCS World OpenBeta\CoreMods\tech\USS_Nimitz\Database\USS_CVN_7X.lua
|
||||
|
||||
-- Total size of the carrier (approx as rectangle).
|
||||
self.carrierparam.totlength=332.8 -- Wiki says 332.8 meters overall length.
|
||||
@@ -4309,7 +4328,7 @@ function AIRBOSS:_InitNimitz()
|
||||
self.carrierparam.totwidthstarboard=35
|
||||
|
||||
-- Landing runway.
|
||||
self.carrierparam.rwyangle = -9
|
||||
self.carrierparam.rwyangle = -9.1359 --DCS World OpenBeta\CoreMods\tech\USS_Nimitz\scripts\USS_Nimitz_RunwaysAndRoutes.lua
|
||||
self.carrierparam.rwylength = 250
|
||||
self.carrierparam.rwywidth = 25
|
||||
|
||||
@@ -7895,7 +7914,7 @@ end
|
||||
function AIRBOSS:_RemoveUnitFromFlight(unit)
|
||||
|
||||
-- Check if unit exists.
|
||||
if unit then
|
||||
if unit and unit:IsInstanceOf("UNIT") then
|
||||
|
||||
-- Get group.
|
||||
local group=unit:GetGroup()
|
||||
@@ -10331,7 +10350,7 @@ function AIRBOSS:_GetSternCoord()
|
||||
stern=stern:Translate(self.carrierparam.sterndist, hdg):Translate(7, FB+90)
|
||||
else
|
||||
-- Nimitz SC: translate 8 meters starboard wrt Final bearing.
|
||||
stern=stern:Translate(self.carrierparam.sterndist, hdg):Translate(8.5, FB+90)
|
||||
stern=stern:Translate(self.carrierparam.sterndist, hdg):Translate(9.5, FB+90)
|
||||
end
|
||||
|
||||
-- Set altitude.
|
||||
@@ -10361,6 +10380,11 @@ function AIRBOSS:_GetWire(Lcoord, dc)
|
||||
|
||||
-- Corrected landing distance wrt to stern. Landing distance needs to be reduced due to delayed landing event for human players.
|
||||
local d=Ldist-dc
|
||||
|
||||
-- Multiplayer wire correction.
|
||||
if self.mpWireCorrection then
|
||||
d=d-self.mpWireCorrection
|
||||
end
|
||||
|
||||
-- Shift wires from stern to their correct position.
|
||||
local w1=self.carrierparam.wire1
|
||||
@@ -14048,10 +14072,10 @@ function AIRBOSS:_GetOnboardNumbers(group, playeronly)
|
||||
-- Onboard number and unit name.
|
||||
local n=tostring(unit.onboard_num)
|
||||
local name=unit.name
|
||||
local skill=unit.skill
|
||||
local skill=unit.skill or "Unknown"
|
||||
|
||||
-- Debug text.
|
||||
text=text..string.format("\n- unit %s: onboard #=%s skill=%s", name, n, skill)
|
||||
text=text..string.format("\n- unit %s: onboard #=%s skill=%s", name, n, tostring(skill))
|
||||
|
||||
if playeronly and skill=="Client" or skill=="Player" then
|
||||
-- There can be only one player in the group, so we skip everything else.
|
||||
@@ -17715,7 +17739,7 @@ function AIRBOSS:onbeforeSave(From, Event, To, path, filename)
|
||||
|
||||
-- Check default path.
|
||||
if path==nil and not lfs then
|
||||
self:E(self.lid.."WARNING: lfs not desanitized. Results will be saved in DCS installation root directory rather than your \"Saved Games\DCS\" folder.")
|
||||
self:E(self.lid.."WARNING: lfs not desanitized. Results will be saved in DCS installation root directory rather than your \"Saved Games\\DCS\" folder.")
|
||||
end
|
||||
|
||||
return true
|
||||
@@ -17822,7 +17846,7 @@ function AIRBOSS:onbeforeLoad(From, Event, To, path, filename)
|
||||
|
||||
-- Check default path.
|
||||
if path==nil and not lfs then
|
||||
self:E(self.lid.."WARNING: lfs not desanitized. Results will be saved in DCS installation root directory rather than your \"Saved Games\DCS\" folder.")
|
||||
self:E(self.lid.."WARNING: lfs not desanitized. Results will be saved in DCS installation root directory rather than your \"Saved Games\\DCS\" folder.")
|
||||
end
|
||||
|
||||
-- Set path or default.
|
||||
|
||||
@@ -881,6 +881,9 @@ do -- Group Assignment
|
||||
local Mission = self:GetMission()
|
||||
local CommandCenter = Mission:GetCommandCenter()
|
||||
CommandCenter:SetMenu()
|
||||
|
||||
self:MenuFlashTaskStatus( TaskGroup, false ) -- stop message flashing, if any #1383 & #1312
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1252,7 +1255,7 @@ function TASK:MenuFlashTaskStatus( TaskGroup, Flash )
|
||||
self.FlashTaskStatus = Flash
|
||||
|
||||
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
|
||||
if self.FlashTaskScheduler then
|
||||
self.FlashTaskScheduler:Stop( self.FlashTaskScheduleID )
|
||||
|
||||
@@ -344,7 +344,9 @@ function TASKINFO:Report( Report, Detail, ReportGroup, Task )
|
||||
Text = DataText
|
||||
else
|
||||
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
|
||||
|
||||
if Line < math.floor( Data.Order / 10 ) then
|
||||
|
||||
@@ -199,7 +199,7 @@ do -- TASK_A2A_DISPATCHER
|
||||
|
||||
self.Detection = Detection
|
||||
self.Mission = Mission
|
||||
|
||||
self.FlashNewTask = false
|
||||
|
||||
-- TODO: Check detection through radar.
|
||||
self.Detection:FilterCategories( Unit.Category.AIRPLANE, Unit.Category.HELICOPTER )
|
||||
@@ -253,7 +253,12 @@ do -- TASK_A2A_DISPATCHER
|
||||
return self
|
||||
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.
|
||||
-- @param #TASK_A2A_DISPATCHER self
|
||||
@@ -610,7 +615,7 @@ do -- TASK_A2A_DISPATCHER
|
||||
local TaskText = TaskReport:Text(", ")
|
||||
|
||||
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 )
|
||||
end
|
||||
end
|
||||
|
||||
@@ -451,6 +451,7 @@ do -- TASK_A2G_DISPATCHER
|
||||
|
||||
self.Detection = Detection
|
||||
self.Mission = Mission
|
||||
self.FlashNewTask = true --set to false to suppress flash messages
|
||||
|
||||
self.Detection:FilterCategories( { Unit.Category.GROUND_UNIT } )
|
||||
|
||||
@@ -471,6 +472,12 @@ do -- TASK_A2G_DISPATCHER
|
||||
return self
|
||||
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.
|
||||
-- @param #TASK_A2G_DISPATCHER self
|
||||
@@ -616,7 +623,9 @@ do -- TASK_A2G_DISPATCHER
|
||||
if not DetectedItem then
|
||||
local TaskText = Task:GetName()
|
||||
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
|
||||
Mission:GetCommandCenter():MessageToGroup( string.format( "Obsolete A2G task %s for %s removed.", TaskText, Mission:GetShortText() ), TaskGroup )
|
||||
if self.FlashNewTask then
|
||||
Mission:GetCommandCenter():MessageToGroup( string.format( "Obsolete A2G task %s for %s removed.", TaskText, Mission:GetShortText() ), TaskGroup )
|
||||
end
|
||||
end
|
||||
Task = self:RemoveTask( TaskIndex )
|
||||
end
|
||||
@@ -686,7 +695,7 @@ do -- TASK_A2G_DISPATCHER
|
||||
-- Now we send to each group the changes, if any.
|
||||
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
|
||||
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 )
|
||||
end
|
||||
end
|
||||
@@ -744,6 +753,7 @@ do -- TASK_A2G_DISPATCHER
|
||||
local TargetSetUnit = self:EvaluateSEAD( DetectedItem ) -- Returns a SetUnit if there are targets to be SEADed...
|
||||
if TargetSetUnit then
|
||||
Task = TASK_A2G_SEAD:New( Mission, self.SetGroup, string.format( "SEAD.%03d", DetectedItemID ), TargetSetUnit )
|
||||
DetectedItem.DesignateMenuName = string.format( "SEAD.%03d", DetectedItemID ) --inject a name for DESIGNATE, if using same DETECTION object
|
||||
Task:SetDetection( Detection, DetectedItem )
|
||||
end
|
||||
|
||||
@@ -752,6 +762,7 @@ do -- TASK_A2G_DISPATCHER
|
||||
local TargetSetUnit = self:EvaluateCAS( DetectedItem ) -- Returns a SetUnit if there are targets to be CASed...
|
||||
if TargetSetUnit then
|
||||
Task = TASK_A2G_CAS:New( Mission, self.SetGroup, string.format( "CAS.%03d", DetectedItemID ), TargetSetUnit )
|
||||
DetectedItem.DesignateMenuName = string.format( "CAS.%03d", DetectedItemID ) --inject a name for DESIGNATE, if using same DETECTION object
|
||||
Task:SetDetection( Detection, DetectedItem )
|
||||
end
|
||||
|
||||
@@ -760,6 +771,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...
|
||||
if TargetSetUnit then
|
||||
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 )
|
||||
end
|
||||
end
|
||||
@@ -805,7 +817,7 @@ do -- TASK_A2G_DISPATCHER
|
||||
|
||||
local TaskText = TaskReport:Text(", ")
|
||||
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 )
|
||||
end
|
||||
end
|
||||
@@ -815,4 +827,4 @@ do -- TASK_A2G_DISPATCHER
|
||||
return true
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -83,7 +83,7 @@ do -- TASK_CAPTURE_DISPATCHER
|
||||
-- ## 1.1. Create a command center.
|
||||
--
|
||||
-- First you need to create a command center using the @{Tasking.CommandCenter#COMMANDCENTER.New}() constructor.
|
||||
-- The command assumes that you´ve setup a group in the mission editor with the name HQ.
|
||||
-- The command assumes that you´ve setup a group in the mission editor with the name HQ.
|
||||
-- This group will act as the command center object.
|
||||
-- It is a good practice to mark this group as invisible and invulnerable.
|
||||
--
|
||||
@@ -184,6 +184,7 @@ do -- TASK_CAPTURE_DISPATCHER
|
||||
local self = BASE:Inherit( self, TASK_MANAGER:New( SetGroup ) ) -- #TASK_CAPTURE_DISPATCHER
|
||||
|
||||
self.Mission = Mission
|
||||
self.FlashNewTask = false
|
||||
|
||||
self:AddTransition( "Started", "Assign", "Started" )
|
||||
self:AddTransition( "Started", "ZoneCaptured", "Started" )
|
||||
@@ -383,7 +384,7 @@ do -- TASK_CAPTURE_DISPATCHER
|
||||
local TaskText = TaskReport:Text(", ")
|
||||
|
||||
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 )
|
||||
end
|
||||
end
|
||||
|
||||
@@ -310,4 +310,53 @@ ENUMS.Morse.N7="- - * * *"
|
||||
ENUMS.Morse.N8="- - - * *"
|
||||
ENUMS.Morse.N9="- - - - *"
|
||||
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',
|
||||
}
|
||||
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"]
|
||||
}
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -97,7 +97,7 @@ CALLSIGN={
|
||||
JTAC={
|
||||
Axeman=1,
|
||||
Darknight=2,
|
||||
Warrier=3,
|
||||
Warrior=3,
|
||||
Pointer=4,
|
||||
Eyeball=5,
|
||||
Moonbeam=6,
|
||||
@@ -115,6 +115,19 @@ CALLSIGN={
|
||||
Mantis=18,
|
||||
Badger=19,
|
||||
},
|
||||
-- FARP
|
||||
FARP={
|
||||
London=1,
|
||||
Dallas=2,
|
||||
Paris=3,
|
||||
Moscow=4,
|
||||
Berlin=5,
|
||||
Rome=6,
|
||||
Madrid=7,
|
||||
Warsaw=8,
|
||||
Dublin=9,
|
||||
Perth=10,
|
||||
},
|
||||
} --#CALLSIGN
|
||||
|
||||
--- Utilities static class.
|
||||
@@ -395,6 +408,14 @@ UTILS.hPa2inHg = function( hPa )
|
||||
return hPa * 0.0295299830714
|
||||
end
|
||||
|
||||
--- Convert knots to alitude corrected KIAS, e.g. for tankers.
|
||||
-- @param #number knots Speed in knots.
|
||||
-- @param #number altitude Altitude in feet
|
||||
-- @return #number Corrected KIAS
|
||||
UTILS.KnotsToAltKIAS = function( knots, altitude )
|
||||
return (knots * 0.018 * (altitude / 1000)) + knots
|
||||
end
|
||||
|
||||
--- Convert pressure from hecto Pascal (hPa) to millimeters of mercury (mmHg).
|
||||
-- @param #number hPa Pressure in hPa.
|
||||
-- @return #number Pressure in mmHg.
|
||||
@@ -631,6 +652,17 @@ function UTILS.GetMarkID()
|
||||
|
||||
end
|
||||
|
||||
--- Remove an object (marker, circle, arrow, text, quad, ...) on the F10 map.
|
||||
-- @param #number MarkID Unique ID of the object.
|
||||
-- @param #number Delay (Optional) Delay in seconds before the mark is removed.
|
||||
function UTILS.RemoveMark(MarkID, Delay)
|
||||
if Delay and Delay>0 then
|
||||
TIMER:New(UTILS.RemoveMark, MarkID):Start(Delay)
|
||||
else
|
||||
trigger.action.removeMark(MarkID)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Test if a Vec2 is in a radius of another Vec2
|
||||
function UTILS.IsInRadius( InVec2, Vec2, Radius )
|
||||
@@ -1444,4 +1476,4 @@ function UTILS.GetOSTime()
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
-- @field #table parking Parking spot data.
|
||||
-- @field #table parkingByID Parking spot data table with ID as key.
|
||||
-- @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
|
||||
|
||||
--- Wrapper class to handle the DCS Airbase objects:
|
||||
@@ -268,31 +270,31 @@ AIRBASE.Normandy = {
|
||||
-- * AIRBASE.PersianGulf.Tunb_Kochak
|
||||
-- @field PersianGulf
|
||||
AIRBASE.PersianGulf = {
|
||||
["Abu_Dhabi_International_Airport"] = "Abu Dhabi International Airport",
|
||||
["Abu_Musa_Island_Airport"] = "Abu Musa Island Airport",
|
||||
["Al_Ain_International_Airport"] = "Al Ain International Airport",
|
||||
["Al_Bateen_Airport"] = "Al-Bateen Airport",
|
||||
["Al_Dhafra_AB"] = "Al Dhafra AB",
|
||||
["Abu_Dhabi_International_Airport"] = "Abu Dhabi Intl",
|
||||
["Abu_Musa_Island_Airport"] = "Abu Musa Island",
|
||||
["Al_Ain_International_Airport"] = "Al Ain Intl",
|
||||
["Al_Bateen_Airport"] = "Al-Bateen",
|
||||
["Al_Dhafra_AB"] = "Al Dhafra AFB",
|
||||
["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_Lengeh"] = "Bandar Lengeh",
|
||||
["Bandar_e_Jask_airfield"] = "Bandar-e-Jask airfield",
|
||||
["Bandar_e_Jask_airfield"] = "Bandar-e-Jask",
|
||||
["Dubai_Intl"] = "Dubai Intl",
|
||||
["Fujairah_Intl"] = "Fujairah Intl",
|
||||
["Havadarya"] = "Havadarya",
|
||||
["Jiroft_Airport"] = "Jiroft Airport",
|
||||
["Kerman_Airport"] = "Kerman Airport",
|
||||
["Jiroft_Airport"] = "Jiroft",
|
||||
["Kerman_Airport"] = "Kerman",
|
||||
["Khasab"] = "Khasab",
|
||||
["Kish_International_Airport"] = "Kish International Airport",
|
||||
["Lar_Airbase"] = "Lar Airbase",
|
||||
["Lavan_Island_Airport"] = "Lavan Island Airport",
|
||||
["Liwa_Airbase"] = "Liwa Airbase",
|
||||
["Kish_International_Airport"] = "Kish Intl",
|
||||
["Lar_Airbase"] = "Lar",
|
||||
["Lavan_Island_Airport"] = "Lavan Island",
|
||||
["Liwa_Airbase"] = "Liwa AFB",
|
||||
["Qeshm_Island"] = "Qeshm Island",
|
||||
["Ras_Al_Khaimah"] = "Ras Al Khaimah",
|
||||
["Sas_Al_Nakheel_Airport"] = "Sas Al Nakheel Airport",
|
||||
["Ras_Al_Khaimah"] = "Ras Al Khaimah Intl",
|
||||
["Sas_Al_Nakheel_Airport"] = "Sas Al Nakheel",
|
||||
["Sharjah_Intl"] = "Sharjah Intl",
|
||||
["Shiraz_International_Airport"] = "Shiraz International Airport",
|
||||
["Shiraz_International_Airport"] = "Shiraz Intl",
|
||||
["Sir_Abu_Nuayr"] = "Sir Abu Nuayr",
|
||||
["Sirri_Island"] = "Sirri Island",
|
||||
["Tunb_Island_AFB"] = "Tunb Island AFB",
|
||||
@@ -359,6 +361,14 @@ AIRBASE.TheChannel = {
|
||||
-- * AIRBASE.Syria.Beirut_Rafic_Hariri
|
||||
-- * AIRBASE.Syria.An_Nasiriyah
|
||||
-- * AIRBASE.Syria.Abu_al_Duhur
|
||||
-- * AIRBASE.Syria.H4
|
||||
-- * AIRBASE.Syria.Gaziantep
|
||||
-- * AIRBASE.Syria.Rosh_Pina
|
||||
-- * AIRBASE.Syria.Sayqal
|
||||
-- * AIRBASE.Syria.Shayrat
|
||||
-- * AIRBASE.Syria.Tiyas
|
||||
-- * AIRBASE.Syria.Tha_lah
|
||||
-- * AIRBASE.Syria.Naqoura
|
||||
--
|
||||
-- @field Syria
|
||||
AIRBASE.Syria={
|
||||
@@ -395,6 +405,14 @@ AIRBASE.Syria={
|
||||
["Beirut_Rafic_Hariri"]="Beirut-Rafic Hariri",
|
||||
["An_Nasiriyah"]="An Nasiriyah",
|
||||
["Abu_al_Duhur"]="Abu al-Duhur",
|
||||
["H4"]="H4",
|
||||
["Gaziantep"]="Gaziantep",
|
||||
["Rosh_Pina"]="Rosh Pina",
|
||||
["Sayqal"]="Sayqal",
|
||||
["Shayrat"]="Shayrat",
|
||||
["Tiyas"]="Tiyas",
|
||||
["Tha_lah"]="Tha'lah",
|
||||
["Naqoura"]="Naqoura",
|
||||
}
|
||||
|
||||
|
||||
@@ -590,7 +608,7 @@ end
|
||||
|
||||
--- 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 #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.
|
||||
function AIRBASE.GetAllAirbaseNames(coalition, category)
|
||||
|
||||
@@ -645,6 +663,54 @@ function AIRBASE:GetID(unique)
|
||||
return nil
|
||||
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.
|
||||
-- @param #AIRBASE self
|
||||
@@ -829,25 +895,27 @@ function AIRBASE:_InitParkingSpots()
|
||||
-- Put coordinates of parking spots into table.
|
||||
for _,spot in pairs(parkingdata) do
|
||||
|
||||
-- New parking spot.
|
||||
local park={} --#AIRBASE.ParkingSpot
|
||||
park.Vec3=spot.vTerminalPos
|
||||
park.Coordinate=COORDINATE:NewFromVec3(spot.vTerminalPos)
|
||||
park.DistToRwy=spot.fDistToRW
|
||||
park.Free=nil
|
||||
park.TerminalID=spot.Term_Index
|
||||
park.TerminalID0=spot.Term_Index_0
|
||||
park.TerminalType=spot.Term_Type
|
||||
park.TOAC=spot.TO_AC
|
||||
|
||||
for _,terminalType in pairs(AIRBASE.TerminalType) do
|
||||
if self._CheckTerminalType(terminalType, park.TerminalType) then
|
||||
self.NparkingTerminal[terminalType]=self.NparkingTerminal[terminalType]+1
|
||||
end
|
||||
end
|
||||
|
||||
self.parkingByID[park.TerminalID]=park
|
||||
table.insert(self.parking, park)
|
||||
-- New parking spot.
|
||||
local park={} --#AIRBASE.ParkingSpot
|
||||
park.Vec3=spot.vTerminalPos
|
||||
park.Coordinate=COORDINATE:NewFromVec3(spot.vTerminalPos)
|
||||
park.DistToRwy=spot.fDistToRW
|
||||
park.Free=nil
|
||||
park.TerminalID=spot.Term_Index
|
||||
park.TerminalID0=spot.Term_Index_0
|
||||
park.TerminalType=spot.Term_Type
|
||||
park.TOAC=spot.TO_AC
|
||||
|
||||
self.NparkingTotal=self.NparkingTotal+1
|
||||
|
||||
for _,terminalType in pairs(AIRBASE.TerminalType) do
|
||||
if self._CheckTerminalType(terminalType, park.TerminalType) then
|
||||
self.NparkingTerminal[terminalType]=self.NparkingTerminal[terminalType]+1
|
||||
end
|
||||
end
|
||||
|
||||
self.parkingByID[park.TerminalID]=park
|
||||
table.insert(self.parking, park)
|
||||
end
|
||||
|
||||
return self
|
||||
@@ -1075,9 +1143,8 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius,
|
||||
local _spot=parkingspot.Coordinate -- Core.Point#COORDINATE
|
||||
local _termid=parkingspot.TerminalID
|
||||
|
||||
self:T2({_termid=_termid})
|
||||
|
||||
if AIRBASE._CheckTerminalType(parkingspot.TerminalType, terminaltype) then
|
||||
-- Check terminal type and black/white listed parking spots.
|
||||
if AIRBASE._CheckTerminalType(parkingspot.TerminalType, terminaltype) and self:_CheckParkingLists(_termid) 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.
|
||||
if verysafe and (parkingspot.Free==false or parkingspot.TOAC==true) then
|
||||
@@ -1179,6 +1246,39 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius,
|
||||
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
|
||||
|
||||
--- Helper function to check for the correct terminal type including "artificial" ones.
|
||||
-- @param #number Term_Type Termial type from getParking routine.
|
||||
-- @param #AIRBASE.TerminalType termtype Terminal type from AIRBASE.TerminalType enumerator.
|
||||
@@ -1272,8 +1372,7 @@ function AIRBASE:GetRunwayData(magvar, mark)
|
||||
name==AIRBASE.PersianGulf.Abu_Dhabi_International_Airport or
|
||||
name==AIRBASE.PersianGulf.Dubai_Intl or
|
||||
name==AIRBASE.PersianGulf.Shiraz_International_Airport or
|
||||
name==AIRBASE.PersianGulf.Kish_International_Airport
|
||||
then
|
||||
name==AIRBASE.PersianGulf.Kish_International_Airport then
|
||||
|
||||
-- 1-->4, 2-->3, 3-->2, 4-->1
|
||||
exception=1
|
||||
@@ -1290,13 +1389,14 @@ function AIRBASE:GetRunwayData(magvar, mark)
|
||||
|
||||
end
|
||||
|
||||
--- Function returning the index of the runway coordinate belonding to the given index i.
|
||||
local function f(i)
|
||||
|
||||
local j
|
||||
|
||||
if exception==1 then
|
||||
|
||||
j=N-(i+1) -- 1-->4, 2-->3
|
||||
j=N-(i-1) -- 1-->4, 2-->3
|
||||
|
||||
elseif exception==2 then
|
||||
|
||||
@@ -1357,6 +1457,9 @@ function AIRBASE:GetRunwayData(magvar, mark)
|
||||
|
||||
-- Get the other spawn point coordinate.
|
||||
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.
|
||||
local c1=runwaycoords[i] --Core.Point#COORDINATE
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ### Contributions:
|
||||
-- ### Contributions: **funkyfranky**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -52,13 +51,6 @@
|
||||
--
|
||||
-- @field #CLIENT
|
||||
CLIENT = {
|
||||
ONBOARDSIDE = {
|
||||
NONE = 0,
|
||||
LEFT = 1,
|
||||
RIGHT = 2,
|
||||
BACK = 3,
|
||||
FRONT = 4
|
||||
},
|
||||
ClassName = "CLIENT",
|
||||
ClientName = nil,
|
||||
ClientAlive = false,
|
||||
@@ -66,27 +58,20 @@ CLIENT = {
|
||||
ClientBriefingShown = false,
|
||||
_Menus = {},
|
||||
_Tasks = {},
|
||||
Messages = {
|
||||
}
|
||||
Messages = {},
|
||||
Players = {},
|
||||
}
|
||||
|
||||
|
||||
--- Finds a CLIENT from the _DATABASE using the relevant DCS Unit.
|
||||
-- @param #CLIENT self
|
||||
-- @param #string ClientName Name of the DCS **Unit** as defined within the Mission Editor.
|
||||
-- @param #string ClientBriefing Text that describes the briefing of the mission when a Player logs into the Client.
|
||||
-- @return #CLIENT
|
||||
-- @usage
|
||||
-- -- 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 )
|
||||
-- @param DCS#Unit DCSUnit The DCS unit of the client.
|
||||
-- @param #boolean Error Throw an error message.
|
||||
-- @return #CLIENT The CLIENT found in the _DATABASE.
|
||||
function CLIENT:Find(DCSUnit, Error)
|
||||
|
||||
local ClientName = DCSUnit:getName()
|
||||
|
||||
local ClientFound = _DATABASE:FindClient( ClientName )
|
||||
|
||||
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*RAMP-Deploy Troops 4' ):Transport() )
|
||||
function CLIENT:FindByName( ClientName, ClientBriefing, Error )
|
||||
|
||||
-- Client
|
||||
local ClientFound = _DATABASE:FindClient( ClientName )
|
||||
|
||||
if ClientFound then
|
||||
ClientFound:F( { ClientName, ClientBriefing } )
|
||||
ClientFound:AddBriefing( ClientBriefing )
|
||||
|
||||
ClientFound:AddBriefing(ClientBriefing)
|
||||
|
||||
ClientFound.MessageSwitch = true
|
||||
|
||||
return ClientFound
|
||||
@@ -132,51 +121,105 @@ function CLIENT:FindByName( ClientName, ClientBriefing, Error )
|
||||
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
|
||||
|
||||
self:F( ClientName )
|
||||
self.ClientName = ClientName
|
||||
self.MessageSwitch = true
|
||||
self.ClientAlive2 = false
|
||||
-- Inherit unit.
|
||||
local self = BASE:Inherit( self, UNIT:Register(ClientName )) -- #CLIENT
|
||||
|
||||
--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()
|
||||
-- Set client name.
|
||||
self.ClientName = ClientName
|
||||
|
||||
-- Message switch.
|
||||
self.MessageSwitch = true
|
||||
|
||||
-- Alive2.
|
||||
self.ClientAlive2 = false
|
||||
|
||||
self:F( self )
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Transport defines that the Client is a Transport. Transports show cargo.
|
||||
-- @param #CLIENT self
|
||||
-- @return #CLIENT
|
||||
-- @return #CLIENT self
|
||||
function CLIENT:Transport()
|
||||
self:F()
|
||||
|
||||
self.ClientTransport = true
|
||||
return self
|
||||
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 #string ClientBriefing is the text defining the Mission briefing.
|
||||
-- @return #CLIENT self
|
||||
function CLIENT:AddBriefing( ClientBriefing )
|
||||
self:F( ClientBriefing )
|
||||
|
||||
self.ClientBriefing = ClientBriefing
|
||||
self.ClientBriefingShown = false
|
||||
|
||||
return self
|
||||
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.
|
||||
-- @param #CLIENT self
|
||||
-- @return #CLIENT self
|
||||
function CLIENT:ShowBriefing()
|
||||
self:F( { self.ClientName, self.ClientBriefingShown } )
|
||||
|
||||
if not self.ClientBriefingShown then
|
||||
self.ClientBriefingShown = true
|
||||
@@ -204,8 +247,6 @@ function CLIENT:ShowMissionBriefing( MissionBriefing )
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Resets a CLIENT.
|
||||
-- @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.
|
||||
@@ -241,6 +282,7 @@ end
|
||||
--- Checks for a client alive event and calls a function on a continuous basis.
|
||||
-- @param #CLIENT self
|
||||
-- @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
|
||||
function CLIENT:Alive( CallBackFunction, ... )
|
||||
self:F()
|
||||
@@ -248,6 +290,9 @@ function CLIENT:Alive( CallBackFunction, ... )
|
||||
self.ClientCallBack = CallBackFunction
|
||||
self.ClientParameters = arg
|
||||
|
||||
self.AliveCheckScheduler = SCHEDULER:New( self, self._AliveCheckScheduler, { "Client Alive " .. self.ClientName }, 0.1, 5, 0.5 )
|
||||
self.AliveCheckScheduler:NoTrace()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -255,19 +300,29 @@ end
|
||||
function CLIENT:_AliveCheckScheduler( SchedulerName )
|
||||
self:F3( { SchedulerName, self.ClientName, self.ClientAlive2, self.ClientBriefingShown, self.ClientCallBack } )
|
||||
|
||||
if self:IsAlive() then
|
||||
if self:IsAlive() then
|
||||
|
||||
if self.ClientAlive2 == false then
|
||||
|
||||
-- Show briefing.
|
||||
self:ShowBriefing()
|
||||
|
||||
-- Callback function.
|
||||
if self.ClientCallBack then
|
||||
self:T("Calling Callback function")
|
||||
self.ClientCallBack( self, unpack( self.ClientParameters ) )
|
||||
end
|
||||
|
||||
-- Alive.
|
||||
self.ClientAlive2 = true
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
if self.ClientAlive2 == true then
|
||||
self.ClientAlive2 = false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return true
|
||||
@@ -291,6 +346,7 @@ function CLIENT:GetDCSGroup()
|
||||
local ClientUnit = Unit.getByName( self.ClientName )
|
||||
|
||||
local CoalitionsData = { AlivePlayersRed = coalition.getPlayers( coalition.side.RED ), AlivePlayersBlue = coalition.getPlayers( coalition.side.BLUE ) }
|
||||
|
||||
for CoalitionId, CoalitionData in pairs( CoalitionsData ) do
|
||||
self:T3( { "CoalitionData:", CoalitionData } )
|
||||
for UnitId, UnitData in pairs( CoalitionData ) do
|
||||
@@ -299,10 +355,14 @@ function CLIENT:GetDCSGroup()
|
||||
|
||||
--self:F(self.ClientName)
|
||||
if ClientUnit then
|
||||
|
||||
local ClientGroup = ClientUnit:getGroup()
|
||||
|
||||
if ClientGroup then
|
||||
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
|
||||
self:T3( "Normal logic" )
|
||||
self:T3( self.ClientName .. " : group found!" )
|
||||
@@ -310,15 +370,22 @@ function CLIENT:GetDCSGroup()
|
||||
self.ClientGroupName = ClientGroup:getName()
|
||||
return ClientGroup
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
-- 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)
|
||||
self:T3( "Bug 1.5 logic" )
|
||||
|
||||
local ClientGroupTemplate = _DATABASE.Templates.Units[self.ClientName].GroupTemplate
|
||||
|
||||
self.ClientGroupID = ClientGroupTemplate.groupId
|
||||
|
||||
self.ClientGroupName = _DATABASE.Templates.Units[self.ClientName].GroupName
|
||||
|
||||
self:T3( self.ClientName .. " : group found in bug 1.5 resolvement logic!" )
|
||||
return ClientGroup
|
||||
|
||||
end
|
||||
-- else
|
||||
-- error( "Client " .. self.ClientName .. " not found!" )
|
||||
@@ -343,22 +410,22 @@ function CLIENT:GetDCSGroup()
|
||||
end
|
||||
end
|
||||
|
||||
-- Nothing could be found :(
|
||||
self.ClientGroupID = nil
|
||||
self.ClientGroupUnit = nil
|
||||
self.ClientGroupName = nil
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
-- TODO: Check DCS#Group.ID
|
||||
--- Get the group ID of the client.
|
||||
-- @param #CLIENT self
|
||||
-- @return DCS#Group.ID
|
||||
-- @return #number DCS#Group ID.
|
||||
function CLIENT:GetClientGroupID()
|
||||
|
||||
local ClientGroup = self:GetDCSGroup()
|
||||
-- This updates the ID.
|
||||
self:GetDCSGroup()
|
||||
|
||||
--self:F( self.ClientGroupID ) -- Determined in GetDCSGroup()
|
||||
return self.ClientGroupID
|
||||
end
|
||||
|
||||
@@ -368,26 +435,28 @@ end
|
||||
-- @return #string
|
||||
function CLIENT:GetClientGroupName()
|
||||
|
||||
local ClientGroup = self:GetDCSGroup()
|
||||
|
||||
self:T( self.ClientGroupName ) -- Determined in GetDCSGroup()
|
||||
-- This updates the group name.
|
||||
self:GetDCSGroup()
|
||||
|
||||
return self.ClientGroupName
|
||||
end
|
||||
|
||||
--- Returns the UNIT of the CLIENT.
|
||||
-- @param #CLIENT self
|
||||
-- @return Wrapper.Unit#UNIT
|
||||
-- @return Wrapper.Unit#UNIT The client UNIT or `nil`.
|
||||
function CLIENT:GetClientGroupUnit()
|
||||
self:F2()
|
||||
|
||||
local ClientDCSUnit = Unit.getByName( self.ClientName )
|
||||
|
||||
self:T( self.ClientDCSUnit )
|
||||
|
||||
if ClientDCSUnit and ClientDCSUnit:isExist() then
|
||||
local ClientUnit = _DATABASE:FindUnit( self.ClientName )
|
||||
self:T2( ClientUnit )
|
||||
local ClientUnit=_DATABASE:FindUnit( self.ClientName )
|
||||
return ClientUnit
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns the DCSUnit of the CLIENT.
|
||||
|
||||
@@ -129,6 +129,7 @@
|
||||
-- * @{#CONTROLLABLE.Route}(): Make the Controllable to follow a given route.
|
||||
-- * @{#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.RelocateGroundRandomInRadius}(): Relocate the GROUND controllable to a random point in a given radius.
|
||||
--
|
||||
-- # 5) Option methods
|
||||
--
|
||||
@@ -173,6 +174,9 @@
|
||||
-- * @{#CONTROLLABLE.OptionAllowJettisonWeaponsOnThreat}
|
||||
-- * @{#CONTROLLABLE.OptionKeepWeaponsOnThreat}
|
||||
--
|
||||
-- ## 5.5) Air-2-Air missile attack range:
|
||||
-- * @{#CONTROLLABLE.OptionAAAttackRange}(): Defines the usage of A2A missiles against possible targets .
|
||||
--
|
||||
-- @field #CONTROLLABLE
|
||||
CONTROLLABLE = {
|
||||
ClassName = "CONTROLLABLE",
|
||||
@@ -500,7 +504,7 @@ function CONTROLLABLE:TaskCombo( DCSTasks )
|
||||
tasks = DCSTasks
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return DCSTaskCombo
|
||||
end
|
||||
|
||||
@@ -798,12 +802,12 @@ end
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:CommandSetFrequency(Frequency, Modulation, Delay)
|
||||
|
||||
local CommandSetFrequency = {
|
||||
id = 'SetFrequency',
|
||||
params = {
|
||||
frequency = Frequency*1000000,
|
||||
modulation = Modulation or radio.modulation.AM,
|
||||
}
|
||||
local CommandSetFrequency = {
|
||||
id = 'SetFrequency',
|
||||
params = {
|
||||
frequency = Frequency*1000000,
|
||||
modulation = Modulation or radio.modulation.AM,
|
||||
}
|
||||
}
|
||||
|
||||
if Delay and Delay>0 then
|
||||
@@ -877,7 +881,7 @@ function CONTROLLABLE:TaskAttackGroup( AttackGroup, WeaponType, WeaponExpend, At
|
||||
groupId = AttackGroup:GetID(),
|
||||
weaponType = WeaponType or 1073741822,
|
||||
expend = WeaponExpend or "Auto",
|
||||
attackQtyLimit = AttackQty and true or false,
|
||||
attackQtyLimit = AttackQty and true or false,
|
||||
attackQty = AttackQty or 1,
|
||||
directionEnabled = Direction and true or false,
|
||||
direction = Direction and math.rad(Direction) or 0,
|
||||
@@ -917,7 +921,7 @@ function CONTROLLABLE:TaskAttackUnit(AttackUnit, GroupAttack, WeaponExpend, Atta
|
||||
weaponType = WeaponType or 1073741822,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return DCSTask
|
||||
end
|
||||
|
||||
@@ -1081,7 +1085,7 @@ function CONTROLLABLE:TaskEmbarking(Coordinate, GroupSetForEmbarking, Duration,
|
||||
-- Distribution
|
||||
--local distribution={}
|
||||
--distribution[id]=gids
|
||||
|
||||
|
||||
local groupID=self and self:GetID()
|
||||
|
||||
local DCSTask = {
|
||||
@@ -1310,7 +1314,7 @@ function CONTROLLABLE:TaskLandAtVec2(Vec2, Duration)
|
||||
duration = Duration,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
return DCSTask
|
||||
end
|
||||
|
||||
@@ -1424,16 +1428,22 @@ end
|
||||
-- @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 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.
|
||||
function CONTROLLABLE:TaskFireAtPoint( Vec2, Radius, AmmoCount, WeaponType )
|
||||
function CONTROLLABLE:TaskFireAtPoint( Vec2, Radius, AmmoCount, WeaponType, Altitude, ASL )
|
||||
|
||||
local DCSTask = {
|
||||
id = 'FireAtPoint',
|
||||
params = {
|
||||
point = Vec2,
|
||||
x=Vec2.x,
|
||||
y=Vec2.y,
|
||||
zoneRadius = Radius,
|
||||
radius = Radius,
|
||||
expendQty = 100, -- dummy value
|
||||
expendQtyEnabled = false,
|
||||
alt_type = ASL and 0 or 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1442,10 +1452,16 @@ function CONTROLLABLE:TaskFireAtPoint( Vec2, Radius, AmmoCount, WeaponType )
|
||||
DCSTask.params.expendQtyEnabled = true
|
||||
end
|
||||
|
||||
if Altitude then
|
||||
DCSTask.params.altitude=Altitude
|
||||
end
|
||||
|
||||
if WeaponType then
|
||||
DCSTask.params.weaponType=WeaponType
|
||||
end
|
||||
|
||||
--self:I(DCSTask)
|
||||
|
||||
return DCSTask
|
||||
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.
|
||||
-- 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.
|
||||
-- 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 Wrapper.Group#GROUP AttackGroup Target GROUP object.
|
||||
-- @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 #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 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**.
|
||||
-- @return DCS#Task The DCS task structure.
|
||||
function CONTROLLABLE:TaskFAC_AttackGroup( AttackGroup, WeaponType, Designation, Datalink, Frequency, Modulation, CallsignName, CallsignNumber )
|
||||
@@ -1792,7 +1809,7 @@ function CONTROLLABLE:TaskFunction( FunctionString, ... )
|
||||
|
||||
-- DCS task.
|
||||
local DCSTask = self:TaskWrappedAction(self:CommandDoScript(table.concat( DCSScript )))
|
||||
|
||||
|
||||
return DCSTask
|
||||
end
|
||||
|
||||
@@ -1834,8 +1851,27 @@ do -- Patrol methods
|
||||
|
||||
-- Calculate the new Route.
|
||||
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 )
|
||||
|
||||
local TaskRoute = PatrolGroup:TaskFunction( "CONTROLLABLE.PatrolRoute" )
|
||||
@@ -1875,7 +1911,16 @@ do -- Patrol methods
|
||||
if ToWaypoint then
|
||||
FromWaypoint = ToWaypoint
|
||||
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.
|
||||
-- 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.
|
||||
@@ -1890,9 +1935,13 @@ do -- Patrol methods
|
||||
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
|
||||
local Route = {}
|
||||
Route[#Route+1] = FromCoord:WaypointGround( 0 )
|
||||
Route[#Route+1] = ToCoord:WaypointGround( Speed, Formation )
|
||||
|
||||
if IsSub then
|
||||
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 )
|
||||
|
||||
@@ -1933,9 +1982,20 @@ do -- Patrol methods
|
||||
self:F( { PatrolGroup = PatrolGroup:GetName() } )
|
||||
|
||||
if PatrolGroup:IsGround() or PatrolGroup:IsShip() then
|
||||
|
||||
|
||||
-- Calculate the new Route.
|
||||
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.
|
||||
local RandomZone = ZoneList[ math.random( 1, #ZoneList ) ] -- Core.Zone#ZONE
|
||||
@@ -1943,9 +2003,13 @@ do -- Patrol methods
|
||||
|
||||
-- Create a "ground route point", which is a "point" structure that can be given as a parameter to a Task
|
||||
local Route = {}
|
||||
Route[#Route+1] = FromCoord:WaypointGround( 20 )
|
||||
Route[#Route+1] = ToCoord:WaypointGround( Speed, Formation )
|
||||
|
||||
if IsSub then
|
||||
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 )
|
||||
|
||||
@@ -1969,7 +2033,7 @@ function CONTROLLABLE:TaskRoute( Points )
|
||||
id = 'Mission',
|
||||
params = {
|
||||
airborne = self:IsAir(),
|
||||
route = {points = Points},
|
||||
route = {points = Points},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -2828,7 +2892,7 @@ function CONTROLLABLE:GetDetectedUnitSet(DetectVisual, DetectOptical, DetectRada
|
||||
return unitset
|
||||
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.
|
||||
-- If no detection method is given, the detection will use all the available methods by default.
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE self
|
||||
@@ -2891,13 +2955,13 @@ end
|
||||
--- Set option for Rules of Engagement (ROE).
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE self
|
||||
-- @param #number ROEvalue ROE value. See ENUMS.ROE.
|
||||
-- @return Wrapper.Controllable#CONTROLLABLE self
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:OptionROE(ROEvalue)
|
||||
|
||||
local DCSControllable = self:GetDCSObject()
|
||||
|
||||
|
||||
if DCSControllable then
|
||||
|
||||
|
||||
local Controller = self:_GetController()
|
||||
|
||||
if self:IsAir() then
|
||||
@@ -2933,8 +2997,8 @@ function CONTROLLABLE:OptionROEHoldFirePossible()
|
||||
end
|
||||
|
||||
--- Weapons Hold: AI will hold fire under all circumstances.
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE self
|
||||
-- @return Wrapper.Controllable#CONTROLLABLE self
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:OptionROEHoldFire()
|
||||
self:F2( { self.ControllableName } )
|
||||
|
||||
@@ -3461,13 +3525,13 @@ end
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:OptionProhibitAfterburner(Prohibit)
|
||||
self:F2( { self.ControllableName } )
|
||||
|
||||
|
||||
if Prohibit==nil then
|
||||
Prohibit=true
|
||||
end
|
||||
|
||||
if self:IsAir() then
|
||||
self:SetOption(AI.Option.Air.id.PROHIBIT_AB, Prohibit)
|
||||
self:SetOption(AI.Option.Air.id.PROHIBIT_AB, Prohibit)
|
||||
end
|
||||
|
||||
return self
|
||||
@@ -3478,9 +3542,9 @@ end
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:OptionECM_Never()
|
||||
self:F2( { self.ControllableName } )
|
||||
|
||||
|
||||
if self:IsAir() then
|
||||
self:SetOption(AI.Option.Air.id.ECM_USING, 0)
|
||||
self:SetOption(AI.Option.Air.id.ECM_USING, 0)
|
||||
end
|
||||
|
||||
return self
|
||||
@@ -3491,9 +3555,9 @@ end
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:OptionECM_OnlyLockByRadar()
|
||||
self:F2( { self.ControllableName } )
|
||||
|
||||
|
||||
if self:IsAir() then
|
||||
self:SetOption(AI.Option.Air.id.ECM_USING, 1)
|
||||
self:SetOption(AI.Option.Air.id.ECM_USING, 1)
|
||||
end
|
||||
|
||||
return self
|
||||
@@ -3505,9 +3569,9 @@ end
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:OptionECM_DetectedLockByRadar()
|
||||
self:F2( { self.ControllableName } )
|
||||
|
||||
|
||||
if self:IsAir() then
|
||||
self:SetOption(AI.Option.Air.id.ECM_USING, 2)
|
||||
self:SetOption(AI.Option.Air.id.ECM_USING, 2)
|
||||
end
|
||||
|
||||
return self
|
||||
@@ -3518,9 +3582,9 @@ end
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:OptionECM_AlwaysOn()
|
||||
self:F2( { self.ControllableName } )
|
||||
|
||||
|
||||
if self:IsAir() then
|
||||
self:SetOption(AI.Option.Air.id.ECM_USING, 3)
|
||||
self:SetOption(AI.Option.Air.id.ECM_USING, 3)
|
||||
end
|
||||
|
||||
return self
|
||||
@@ -3532,7 +3596,7 @@ end
|
||||
-- Note that when WayPointInitialize is called, the Mission of the controllable is RESTARTED!
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @param #table WayPoints If WayPoints is given, then use the route.
|
||||
-- @return #CONTROLLABLE
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:WayPointInitialize( 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 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.
|
||||
-- @return #CONTROLLABLE
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:WayPointFunction( WayPoint, WayPointIndex, WayPointFunction, ... )
|
||||
self:F2( { WayPoint, WayPointIndex, WayPointFunction } )
|
||||
|
||||
@@ -3579,7 +3643,7 @@ end
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @param #number WayPoint The WayPoint from where to execute the mission.
|
||||
-- @param #number WaitTime The amount seconds to wait before initiating the mission.
|
||||
-- @return #CONTROLLABLE
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:WayPointExecute( WayPoint, WaitTime )
|
||||
self:F( { WayPoint, WaitTime } )
|
||||
|
||||
@@ -3659,3 +3723,124 @@ function CONTROLLABLE:OptionRestrictBurner(RestrictBurner)
|
||||
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
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:RelocateGroundRandomInRadius(speed, radius, onroad, shortcut)
|
||||
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
|
||||
|
||||
-- create a DCS Task an push it on the group
|
||||
-- TaskGroundOnRoad(ToCoordinate,Speed,OffRoadFormation,Shortcut,FromCoordinate,WaypointFunction,WaypointFunctionArguments)
|
||||
if onroad then
|
||||
_grptsk, _candoroad = self:TaskGroundOnRoad(_tocoord,_speed,"Off Road",_shortcut)
|
||||
self:Route(_grptsk,5)
|
||||
else
|
||||
self:TaskRouteToVec2(_tocoord:GetVec2(),_speed,"Off Road")
|
||||
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
|
||||
|
||||
@@ -2202,27 +2202,52 @@ function GROUP:IsAirborne(AllUnits)
|
||||
|
||||
if units then
|
||||
|
||||
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 AllUnits then
|
||||
|
||||
--- We want to know if ALL units are airborne.
|
||||
|
||||
for _,_unit in pairs(units) do
|
||||
local unit=_unit --Wrapper.Unit#UNIT
|
||||
|
||||
-- Unit is not in air and we wanted to know whether ALL units are ==> return false
|
||||
if inair==false and AllUnits==true then
|
||||
return false
|
||||
end
|
||||
if unit then
|
||||
|
||||
-- At least one unit is in are and we did not care which one.
|
||||
if inair==true and not AllUnits then
|
||||
return true
|
||||
-- Unit in air or not.
|
||||
local inair=unit:InAir()
|
||||
|
||||
-- At least one unit is not in air.
|
||||
if not inair then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
-- At least one unit is in the air.
|
||||
return true
|
||||
|
||||
-- All units are in air.
|
||||
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
|
||||
|
||||
@@ -2526,6 +2551,25 @@ do -- Players
|
||||
|
||||
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
|
||||
|
||||
--do -- Smoke
|
||||
--
|
||||
----- Signal a flare at the position of the GROUP.
|
||||
@@ -2616,4 +2660,4 @@ end
|
||||
--
|
||||
--
|
||||
--
|
||||
--end
|
||||
--end
|
||||
|
||||
@@ -590,26 +590,29 @@ end
|
||||
|
||||
--- Returns the POSITIONABLE heading in degrees.
|
||||
-- @param Wrapper.Positionable#POSITIONABLE self
|
||||
-- @return #number The POSITIONABLE heading
|
||||
-- @return #nil The POSITIONABLE is not existing or alive.
|
||||
-- @return #number The POSITIONABLE heading in degrees or `nil` if not existing or alive.
|
||||
function POSITIONABLE:GetHeading()
|
||||
|
||||
local DCSPositionable = self:GetDCSObject()
|
||||
|
||||
if DCSPositionable then
|
||||
|
||||
local PositionablePosition = DCSPositionable:getPosition()
|
||||
|
||||
if PositionablePosition then
|
||||
local PositionableHeading = math.atan2( PositionablePosition.x.z, PositionablePosition.x.x )
|
||||
|
||||
if PositionableHeading < 0 then
|
||||
PositionableHeading = PositionableHeading + 2 * math.pi
|
||||
end
|
||||
|
||||
PositionableHeading = PositionableHeading * 180 / math.pi
|
||||
self:T2( PositionableHeading )
|
||||
|
||||
return PositionableHeading
|
||||
end
|
||||
end
|
||||
|
||||
BASE:E( { "Cannot GetHeading", Positionable = self, Alive = self:IsAlive() } )
|
||||
self:E({"Cannot GetHeading", Positionable = self, Alive = self:IsAlive()})
|
||||
|
||||
return nil
|
||||
end
|
||||
@@ -1449,44 +1452,67 @@ do -- Cargo
|
||||
}
|
||||
|
||||
self.__.CargoBayWeightLimit = Weights[Desc.typeName] or ( Desc.massMax - ( Desc.massEmpty + Desc.fuelMassMax ) )
|
||||
elseif self:IsShip() then
|
||||
local Desc = self:GetDesc()
|
||||
self:F({Desc=Desc})
|
||||
|
||||
local Weights = {
|
||||
["Type_071"] = 245000,
|
||||
["LHA_Tarawa"] = 500000,
|
||||
["Ropucha-class"] = 150000,
|
||||
["Dry-cargo ship-1"] = 70000,
|
||||
["Dry-cargo ship-2"] = 70000,
|
||||
["Higgins_boat"] = 3700, -- Higgins Boat can load 3700 kg of general cargo or 36 men (source wikipedia).
|
||||
["USS_Samuel_Chase"] = 25000, -- Let's say 25 tons for now. Wiki says 33 Higgins boats, which would be 264 tons (can't be right!) and/or 578 troops.
|
||||
["LST_Mk2"] =2100000, -- Can carry 2100 tons according to wiki source!
|
||||
}
|
||||
self.__.CargoBayWeightLimit = ( Weights[Desc.typeName] or 50000 )
|
||||
|
||||
else
|
||||
local Desc = self:GetDesc()
|
||||
|
||||
local Weights = {
|
||||
["M1126 Stryker ICV"] = 9,
|
||||
["M-113"] = 9,
|
||||
["AAV7"] = 25,
|
||||
["M2A1_halftrack"] = 9,
|
||||
["BMD-1"] = 9,
|
||||
["Bedford_MWD"] = 8, -- new by kappa
|
||||
["Blitz_36-6700A"] = 10, -- new by kappa
|
||||
["BMD-1"] = 9, -- IRL should be 4 passengers
|
||||
["BMP-1"] = 8,
|
||||
["BMP-2"] = 7,
|
||||
["BMP-3"] = 8,
|
||||
["BMP-3"] = 8, -- IRL should be 7+2 passengers
|
||||
["Boman"] = 25,
|
||||
["BTR-80"] = 9,
|
||||
["BTR_D"] = 12,
|
||||
["BTR-80"] = 9, -- IRL should be 7 passengers
|
||||
["BTR-82A"] = 9, -- new by kappa -- IRL should be 7 passengers
|
||||
["BTR_D"] = 12, -- IRL should be 10 passengers
|
||||
["Cobra"] = 8,
|
||||
["Land_Rover_101_FC"] = 11, -- new by kappa
|
||||
["Land_Rover_109_S3"] = 7, -- new by kappa
|
||||
["LAV-25"] = 6,
|
||||
["M-2 Bradley"] = 6,
|
||||
["M1043 HMMWV Armament"] = 4,
|
||||
["M1045 HMMWV TOW"] = 4,
|
||||
["M1126 Stryker ICV"] = 9,
|
||||
["M1134 Stryker ATGM"] = 9,
|
||||
["M2A1_halftrack"] = 9,
|
||||
["M-113"] = 9, -- IRL should be 11 passengers
|
||||
["Marder"] = 6,
|
||||
["MCV-80"] = 9,
|
||||
["MCV-80"] = 9, -- IRL should be 7 passengers
|
||||
["MLRS FDDM"] = 4,
|
||||
["MTLB"] = 25,
|
||||
["TPZ"] = 10,
|
||||
["Ural-4320 APA-5D"] = 10,
|
||||
["MTLB"] = 25, -- IRL should be 11 passengers
|
||||
["GAZ-66"] = 8,
|
||||
["GAZ-3307"] = 12,
|
||||
["GAZ-3308"] = 14,
|
||||
["Tigr_233036"] = 6,
|
||||
["Grad_FDDM"] = 6, -- new by kappa
|
||||
["KAMAZ Truck"] = 12,
|
||||
["KrAZ6322"] = 12,
|
||||
["M 818"] = 12,
|
||||
["Tigr_233036"] = 6,
|
||||
["TPZ"] = 10,
|
||||
["UAZ-469"] = 4, -- new by kappa
|
||||
["Ural-375"] = 12,
|
||||
["Ural-4320-31"] = 14,
|
||||
["Ural-4320 APA-5D"] = 10,
|
||||
["Ural-4320T"] = 14,
|
||||
["ZBD04A"] = 7, -- new by kappa
|
||||
}
|
||||
|
||||
local CargoBayWeightLimit = ( Weights[Desc.typeName] or 0 ) * 95
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ### Contributions:
|
||||
-- ### Contributions: **funkyfranky**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -22,6 +22,8 @@
|
||||
|
||||
|
||||
--- @type UNIT
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @field #string UnitName Name of the unit.
|
||||
-- @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.
|
||||
@@ -61,7 +63,7 @@
|
||||
--
|
||||
-- The UNIT class provides methods to obtain the current point or position of the DCS Unit.
|
||||
-- The @{#UNIT.GetPointVec2}(), @{#UNIT.GetVec3}() will obtain the current **location** of the DCS Unit in a Vec2 (2D) or a **point** in a Vec3 (3D) vector respectively.
|
||||
-- If you want to obtain the complete **3D position** including ori<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
|
||||
--
|
||||
@@ -87,6 +89,7 @@
|
||||
-- @field #UNIT UNIT
|
||||
UNIT = {
|
||||
ClassName="UNIT",
|
||||
UnitName=nil,
|
||||
}
|
||||
|
||||
|
||||
@@ -103,12 +106,18 @@ UNIT = {
|
||||
--- Create a new UNIT from DCSUnit.
|
||||
-- @param #UNIT self
|
||||
-- @param #string UnitName The name of the DCS unit.
|
||||
-- @return #UNIT
|
||||
-- @return #UNIT self
|
||||
function UNIT:Register( UnitName )
|
||||
|
||||
-- Inherit CONTROLLABLE.
|
||||
local self = BASE:Inherit( self, CONTROLLABLE:New( UnitName ) )
|
||||
|
||||
-- Set unit name.
|
||||
self.UnitName = UnitName
|
||||
|
||||
-- Set event prio.
|
||||
self:SetEventPriority( 3 )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -145,8 +154,8 @@ function UNIT:Name()
|
||||
return self.UnitName
|
||||
end
|
||||
|
||||
|
||||
--- @param #UNIT self
|
||||
--- Get the DCS unit object.
|
||||
-- @param #UNIT self
|
||||
-- @return DCS#Unit
|
||||
function UNIT:GetDCSObject()
|
||||
|
||||
@@ -373,6 +382,32 @@ function UNIT:GetPlayerName()
|
||||
|
||||
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.
|
||||
-- The number is the same number the unit has in ME.
|
||||
-- It may not be changed during the mission.
|
||||
@@ -492,8 +527,7 @@ end
|
||||
|
||||
--- Returns the unit's group if it exist and nil otherwise.
|
||||
-- @param Wrapper.Unit#UNIT self
|
||||
-- @return Wrapper.Group#GROUP The Group of the Unit.
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
-- @return Wrapper.Group#GROUP The Group of the Unit or `nil` if the unit does not exist.
|
||||
function UNIT:GetGroup()
|
||||
self:F2( self.UnitName )
|
||||
|
||||
@@ -727,6 +761,7 @@ function UNIT:GetFuel()
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Returns a list of one @{Wrapper.Unit}.
|
||||
-- @param #UNIT self
|
||||
-- @return #list<Wrapper.Unit#UNIT> A list of one @{Wrapper.Unit}.
|
||||
@@ -1140,8 +1175,9 @@ end
|
||||
|
||||
--- Returns true if the UNIT is in the air.
|
||||
-- @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.
|
||||
function UNIT:InAir()
|
||||
function UNIT:InAir(NoHeloCheck)
|
||||
self:F2( self.UnitName )
|
||||
|
||||
-- Get DCS unit object.
|
||||
@@ -1151,14 +1187,14 @@ function UNIT:InAir()
|
||||
|
||||
-- Get DCS result of whether unit is in air or not.
|
||||
local UnitInAir = DCSUnit:inAir()
|
||||
|
||||
|
||||
-- Get unit category.
|
||||
local UnitCategory = DCSUnit:getDesc().category
|
||||
|
||||
-- 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.
|
||||
-- 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 Velocity = UTILS.VecNorm(VelocityVec3)
|
||||
local Coordinate = DCSUnit:getPoint()
|
||||
@@ -1180,23 +1216,26 @@ do -- Event Handling
|
||||
|
||||
--- Subscribe to a DCS Event.
|
||||
-- @param #UNIT self
|
||||
-- @param Core.Event#EVENTS Event
|
||||
-- @param #function EventFunction (optional) The function to be called when the event occurs for the unit.
|
||||
-- @return #UNIT
|
||||
function UNIT:HandleEvent( Event, EventFunction )
|
||||
-- @param Core.Event#EVENTS EventID Event ID.
|
||||
-- @param #function EventFunction (Optional) The function to be called when the event occurs for the unit.
|
||||
-- @return #UNIT self
|
||||
function UNIT:HandleEvent(EventID, EventFunction)
|
||||
|
||||
self:EventDispatcher():OnEventForUnit( self:GetName(), EventFunction, self, Event )
|
||||
self:EventDispatcher():OnEventForUnit(self:GetName(), EventFunction, self, EventID)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- UnSubscribe to a DCS event.
|
||||
-- @param #UNIT self
|
||||
-- @param Core.Event#EVENTS Event
|
||||
-- @return #UNIT
|
||||
function UNIT:UnHandleEvent( Event )
|
||||
-- @param Core.Event#EVENTS EventID Event ID.
|
||||
-- @return #UNIT self
|
||||
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
|
||||
end
|
||||
@@ -1355,3 +1394,23 @@ function UNIT:GetTemplateFuel()
|
||||
|
||||
return nil
|
||||
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
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
Utilities/Enums.lua
|
||||
Utilities/Routines.lua
|
||||
Utilities/Utils.lua
|
||||
Utilities/Enums.lua
|
||||
Utilities/Profiler.lua
|
||||
Utilities/Templates.lua
|
||||
|
||||
Core/Base.lua
|
||||
Core/UserFlag.lua
|
||||
@@ -13,6 +15,7 @@ Core/Event.lua
|
||||
Core/Settings.lua
|
||||
Core/Menu.lua
|
||||
Core/Zone.lua
|
||||
Core/Zone_Detection.lua
|
||||
Core/Database.lua
|
||||
Core/Set.lua
|
||||
Core/Point.lua
|
||||
@@ -20,6 +23,8 @@ Core/Velocity.lua
|
||||
Core/Message.lua
|
||||
Core/Fsm.lua
|
||||
Core/Radio.lua
|
||||
Core/RadioQueue.lua
|
||||
Core/RadioSpeech.lua
|
||||
Core/Spawn.lua
|
||||
Core/SpawnStatic.lua
|
||||
Core/Timer.lua
|
||||
@@ -52,6 +57,7 @@ Functional/Escort.lua
|
||||
Functional/MissileTrainer.lua
|
||||
Functional/ATC_Ground.lua
|
||||
Functional/Detection.lua
|
||||
Functional/DetectionZones.lua
|
||||
Functional/Designate.lua
|
||||
Functional/RAT.lua
|
||||
Functional/Range.lua
|
||||
@@ -63,6 +69,8 @@ Functional/Suppression.lua
|
||||
Functional/PseudoATC.lua
|
||||
Functional/Warehouse.lua
|
||||
Functional/Fox.lua
|
||||
Functional/Mantis.lua
|
||||
Functional/Shorad.lua
|
||||
|
||||
Ops/Airboss.lua
|
||||
Ops/RecoveryTanker.lua
|
||||
@@ -71,31 +79,35 @@ Ops/ATIS.lua
|
||||
|
||||
AI/AI_Balancer.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_Cap.lua
|
||||
AI/AI_A2A_Gci.lua
|
||||
AI/AI_A2A_Dispatcher.lua
|
||||
AI/AI_A2G.lua
|
||||
AI/AI_A2G_Engage.lua
|
||||
AI/AI_A2G_BAI.lua
|
||||
AI/AI_A2G_CAS.lua
|
||||
AI/AI_A2G_SEAD.lua
|
||||
AI/AI_A2G_Patrol.lua
|
||||
AI/AI_A2G_Dispatcher.lua
|
||||
AI/AI_Patrol.lua
|
||||
AI/AI_Cap.lua
|
||||
AI/AI_Cas.lua
|
||||
AI/AI_Bai.lua
|
||||
AI/AI_CAP.lua
|
||||
AI/AI_CAS.lua
|
||||
AI/AI_BAI.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_APC.lua
|
||||
AI/AI_Cargo_Helicopter.lua
|
||||
AI/AI_Cargo_Airplane.lua
|
||||
AI/AI_Cargo_Ship.lua
|
||||
AI/AI_Cargo_Dispatcher.lua
|
||||
AI/AI_Cargo_Dispatcher_APC.lua
|
||||
AI/AI_Cargo_Dispatcher_Helicopter.lua
|
||||
AI/AI_Cargo_Dispatcher_Airplane.lua
|
||||
AI/AI_Cargo_Dispatcher_Ship.lua
|
||||
|
||||
Actions/Act_Assign.lua
|
||||
Actions/Act_Route.lua
|
||||
@@ -112,10 +124,11 @@ Tasking/Task_A2G_Dispatcher.lua
|
||||
Tasking/Task_A2G.lua
|
||||
Tasking/Task_A2A_Dispatcher.lua
|
||||
Tasking/Task_A2A.lua
|
||||
Tasking/Task_Cargo.lua
|
||||
Tasking/Task_CARGO.lua
|
||||
Tasking/Task_Cargo_Transport.lua
|
||||
Tasking/Task_Cargo_CSAR.lua
|
||||
Tasking/Task_Cargo_Dispatcher.lua
|
||||
Tasking/TaskZoneCapture.lua
|
||||
Tasking/Task_Capture_Zone.lua
|
||||
Tasking/Task_Capture_Dispatcher.lua
|
||||
|
||||
Globals.lua
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
[](https://ci.appveyor.com/project/Applevangelist/MOOSE)
|
||||
|
||||
# MOOSE framework
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
@@ -50,9 +52,7 @@ 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.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [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.
|
||||
|
||||
Reference in New Issue
Block a user